MENU

問い合わせ


    【YOLOで作る画像認識ツール 第5回】ブラウザから写真をアップロードして、認識結果をその場で見る

    前回の第4回では、画像認識をFastAPIのAPIにしました。ただ、curl のようなコマンドは、現場の担当者には使えません。今回は、ブラウザで写真を選ぶだけで結果が見られる画面を作ります。

    画面は、HTMLファイル1枚だけで作ります。専用のフレームワークは使いません。「画像認識の機能」と「画面」を分けておくと、画面だけを後から作り替えやすくなります。

    目次

    この回で作るもの

    • 写真を選び、しきい値をスライダーで変えて「認識する」を押す画面
    • 認識結果の枠とラベルを、ブラウザ上で写真に重ねて表示する機能
    • 種類ごとの個数を表にして表示する機能
    • APIサーバーが、この画面を配る設定(/ にアクセスすると画面が開く)

    仕組み:画面は「APIを呼び出して、結果を描く」だけ

    [ブラウザ] 写真を選ぶ → /detect に送る
    [API]      認識して、JSON(種類・信頼度・位置)を返す
    [ブラウザ] 受け取ったJSONをもとに、写真の上に枠を描き、個数を表に出す

    第2回では、サーバー側(Python)で枠を描いた画像を作りました。今回は、ブラウザ側(JavaScript)で枠を描きます。サーバーは数字(JSON)を返すだけなので、画像を作り直す負荷がなく、写真の元データもそのまま残ります。

    実装

    APIサーバーから画面を配る

    # app/api.py(追加部分)
    STATIC_DIR = Path(__file__).parent / "static"
    ...
    @app.get("/", include_in_schema=False)
    def index() -> FileResponse:
        return FileResponse(STATIC_DIR / "index.html")

    / にアクセスすると app/static/index.html が返ります。画面とAPIが同じサーバーにあるため、通信先の設定(CORSなど)が不要です。

    画面 app/static/index.html

    写真を送って結果を受け取る部分です。

    // app/static/index.html(抜粋)
    $("run").addEventListener("click", async () => {
      const file = $("file").files[0];
      $("message").textContent = "";
      if (!file) { $("message").textContent = "先に写真を選んでください。"; return; }
      const form = new FormData();
      form.append("file", file);
      $("run").disabled = true;
      try {
        const res = await fetch(`/detect?conf=${$("conf").value}`, { method: "POST", body: form });
        const body = await res.json();
        if (!res.ok) { throw new Error(typeof body.detail === "string" ? body.detail : "入力を確認してください。"); }
        await draw(file, body.detections);
        summarize(body.detections);
      } catch (err) {
        $("message").textContent = `認識できませんでした: ${err.message}`;
      } finally {
        $("run").disabled = false;
      }
    });
    • 押している間はボタンを無効にする:連打して何度も送信されるのを防ぎます
    • エラーは画面に文章で出す:写真を選んでいないときや、APIがエラーを返したときに、次にすることが分かる文言を表示します

    写真の上に枠を描く部分です。

    // app/static/index.html(抜粋)
    async function draw(file, detections) {
      const bitmap = await createImageBitmap(file);
      const canvas = $("canvas");
      canvas.width = bitmap.width; canvas.height = bitmap.height;
      const ctx = canvas.getContext("2d");
      ctx.drawImage(bitmap, 0, 0);
      for (const d of detections) {
        const [l, t, r, b] = d.box;
        ctx.strokeStyle = ctx.fillStyle = colorFor(d.label);
        ctx.strokeRect(l, t, r - l, b - t);
        ...
      }
    }

    canvas(ブラウザ上のお絵かきの領域)に、選んだ写真をそのまま描き、その上に枠とラベルを重ねます。結果の表を作るときは、textContent で文字を入れています。innerHTML を使わないのは、APIの結果に予期しない文字が混ざっても、それがHTMLとして実行されないようにするためです。

    動かして確かめる

    uvicorn app.api:app --port 8000
    # ブラウザで http://127.0.0.1:8000/ を開き、samples/fruits.jpg を選んで「認識する」

    開発環境(Linux上のChromium)で操作した画面が次のとおりです。ブラウザやOSにより、見た目は異なります。

    写真を選んで認識した画面。写真の上に枠とラベルが表示され、下に種類ごとの個数の表が出ている
    • 写真の上に、第2回と同じ枠とラベルが表示されました
    • 下の表に「orange 4」と個数が出ています
    • 写真を選ばずに押すと「先に写真を選んでください。」と表示されました
    • 前回に続き、レモンやキウイを「orange」と答えている点は変わりません。画面にすると、この問題が現場の人にも一目で伝わります

    テスト

    pytest -q      # 21件成功
    ruff check .   # 問題なし

    画面が配られることは、自動テストで確認しています。ボタンの操作や描画は、この回では自動テストではなく、実際のブラウザ(Chromium)を操作して確認しました。

    つまずきやすい点

    • 大きな写真が横にはみ出す:canvas の最大幅を画面幅に合わせています(max-width: 100%)
    • スマホで撮った写真の向き:写真の向き情報(EXIF)の扱いで、横向き・縦向きが変わって見える場合があります。実際の端末で確認が必要です
    • 認証がない:前回と同じく、この画面には認証がありません。社内ネットワークの外に公開する前に、認証を付けます
    • 画面の作り込みは別:この画面は最小限です。ログイン、履歴、権限管理などは、要件が増えると費用が大きく変わります

    発注者向けメモ:画面の範囲を先に決める

    画像認識のシステムでは、AIの部分に注目が集まりがちですが、現場で使われるかどうかは画面の使いやすさで決まることが多くあります。一方で、画面の作り込みは費用が大きく変わる部分でもあります。

    発注者がやること チェックリスト

    • ☐ 誰が・どの端末(PC・スマホ・タブレット)で使うかを決めた
    • ☐ 写真をどう入れるか(ファイル選択・カメラ撮影・フォルダ監視)を決めた
    • ☐ 結果画面に必要な情報(枠・個数・履歴・修正機能)を、優先順位をつけて書き出した
    • ☐ ログインや権限(誰が何を見られるか)が必要かを決めた

    開発会社への質問例

    • 「画面は、どの端末・ブラウザまで動作確認の対象ですか」
    • 「最初のリリースに含める画面と、後から追加する画面は、どう分けますか」
    • 「ログインや、利用者ごとの権限は、見積もりに含まれていますか」
    • 「画面の変更(項目追加など)は、どの範囲まで保守に含まれますか」

    まとめと次回予告

    第5回では、次のことを行いました。

    • ブラウザで写真を選び、認識結果を写真の上に表示する画面を作った
    • 種類ごとの個数を、表で見られるようにした
    • APIサーバーが画面も配るようにした
    • 画面の操作を、実際のブラウザで確認した

    次回の第6回では、ここまで「見た目で」確認してきた精度を、数字で測る方法(正解ラベルと、適合率・再現率)に進みます。

    この連載の記事一覧

    この記事は連載「YOLOで作る画像認識ツール」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。

    システム制作・運用・保守のお問い合わせはこちら


      よかったらシェアしてね!
      • URLをコピーしました!
      • URLをコピーしました!

      この記事を書いた人

      株式会社THIRD HERO代表取締役 朝野貴朗
      Webシステム開発を中心に、toC向けサービスサイトの運営、ツール開発などを行ってまいりました。

      目次