MediaPipe を使用してカスタム オブジェクトを検出するウェブアプリを作成する

1. 始める前に

MediaPipe Solutions を使用すると、アプリに機械学習(ML)ソリューションを適用できます。このソリューションが提供するフレームワークでは、事前構築の処理パイプラインを構成して、ユーザーに有益で魅力のある出力を迅速に配信できます。これらのソリューションを Model Maker でカスタマイズし、デフォルトのモデルを更新できます。

MediaPipe Solutions はいくつかの ML 視覚タスクを提供していますが、その中の一つがオブジェクト検出です。MediaPipe Tasks は、Android、Python、ウェブで使用できます。

この Codelab では、ウェブアプリにオブジェクト検出機能を追加して、画像とライブ ウェブカメラの映像から犬を検出できるようにします。

学習内容

  • MediaPipe Tasks を使用してウェブアプリにオブジェクト検出タスクを組み込む方法。

作成するアプリの概要

  • 犬の存在を検出するウェブアプリ。MediaPipe Model Maker を使用して、他のオブジェクト クラスを検出できるようにモデルをカスタマイズすることもできます。

必要なもの

  • CodePen アカウント
  • ウェブブラウザのあるデバイス
  • JavaScript、CSS、HTML の基本的な知識

2. 設定する

この Codelab では CodePen でコードを実行します。CodePen は、ブラウザでコードを作成してビルド結果を確認できるソーシャル開発環境です。

設定手順は以下のとおりです。

  1. CodePen アカウントを使用して、この CodePen に移動します。このコードを出発点として、独自のオブジェクト検出機能を作成していきます。
  2. CodePen の下部にあるナビゲーション メニューで [Fork] をクリックして、スターター コードのコピーを作成します。

[Fork] ボタンのある CodePen のナビゲーション メニュー

  1. [JS] タブで、f079bd83ad4547c9.png 展開矢印をクリックして、[Maximize JavaScript editor] を選択します。この Codelab の編集作業で使用するのは [JS] タブだけです。[HTML] タブや [CSS] タブを表示する必要はありません。

スターター アプリを確認する

  1. プレビュー ペインを見てみましょう。犬の画像が 2 つあります。また、ウェブカメラを実行するオプションもあります。このチュートリアルで使用するモデルは、2 つの画像に表示されている 3 匹の犬でトレーニングされています。

スターター コードのウェブアプリのプレビュー。

  1. [JS] タブを見ると、コード全体でいくつかのコメントが追加されています。たとえば、15 行目に次のコメントがあります。
// Import the required package.

これらのコメントは、コード スニペットの挿入場所を示しています。

3. MediaPipe tasks-vision パッケージをインポートして必要な変数を追加する

  1. [JS] タブで、MediaPipe tasks-vision パッケージをインポートします。
// Import the required package.
​​import { ObjectDetector, FilesetResolver, Detection } from "https://cdn.skypack.dev/@mediapipe/tasks-vision@latest";

このコードは、パッケージのインポートにコンテンツ配信ネットワーク(CDN)を使用しています。CodePen で Skypack を使用する詳しい方法については、Skypack と CodePen をご覧ください。

プロジェクトでは、npm、任意のパッケージ マネージャーまたは CDN を介して Node.js を取得できます。インストールする必要があるパッケージの詳細については、JavaScript パッケージをご覧ください。

  1. オブジェクト検出と実行モードの変数を宣言します。
// Create required variables.
let objectDetector: ObjectDetector;
let runningMode = "IMAGE";

runningMode 変数は文字列で、画像内のオブジェクトを検出すると "IMAGE" 値に設定されます。また、動画内のオブジェクトを検出した場合は、"VIDEO" 値に設定されます。

4. オブジェクト検出を初期化する

  • オブジェクト検出を初期化するには、[JS] タブで該当するコメントの後ろに次のコードを追加します。
// Initialize the object detector.
async function initializeObjectDetector() {
  const visionFilesetResolver = await FilesetResolver.forVisionTasks(
    "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/wasm"
  );
  objectDetector = await ObjectDetector.createFromOptions(visionFilesetResolver, {
    baseOptions: {
      modelAssetPath: "https://storage.googleapis.com/mediapipe-assets/dogs.tflite"
    },
    scoreThreshold: 0.3,
    runningMode: runningMode
  });
}
initializeObjectDetector();

FilesetResolver.forVisionTasks() メソッドでは、タスクの WebAssembly(Wasm)バイナリの場所を指定します。

ObjectDetector.createFromOptions() メソッドがオブジェクト検出をインスタンス化します。検出に使用するモデルのパスを指定する必要があります。ここで使用する dog-detection モデルは Cloud Storage にホストされています。

scoreThreshold プロパティは 0.3 値に設定されています。これは、検出したオブジェクトの信頼度が 30% 以上の場合にモデルが結果を返すことを意味します。このしきい値は、アプリの要件に合わせて調整できます。

runningMode プロパティは、ObjectDetector オブジェクトの初期化時に設定されます。このオプションだけでなく、他のオプションも後で必要に応じて変更できます。

5. 画像で予測を実行する

  • 画像に対して予測を実行するには、handleClick() 関数に移動して、関数の本文に次のコードを追加します。
// Verify object detector is initialized and choose the correct running mode.
if (!objectDetector) {
    alert("Object Detector still loading. Please try again");
    return;
  }

  if (runningMode === "VIDEO") {
    runningMode = "IMAGE";
    await objectDetector.setOptions({ runningMode: runningMode });
  }

このコードは、オブジェクト検出が初期化されているかどうかを判断し、画像に実行モードを設定します。

オブジェクトを検出する

  • 画像内のオブジェクトを検出するには、handleClick() 関数の本文に次のコードを追加します。
// Run object detection.
  const detections = objectDetector.detect(event.target);

次のコード スニペットには、このタスクからの出力データのサンプルが含まれています。

ObjectDetectionResult:
 Detection #0:
  Box: (x: 355, y: 133, w: 190, h: 206)
  Categories:
   index       : 17
   score       : 0.73828
   class name  : aci
 Detection #1:
  Box: (x: 103, y: 15, w: 138, h: 369)
  Categories:
   index       : 17
   score       : 0.73047
   class name  : tikka

予測を処理して表示する

  1. handleClick() 関数の本文の最後で、displayImageDetections() 関数を呼び出します。
// Call the displayImageDetections() function.
displayImageDetections(detections, event.target);
  1. displayImageDetections() 関数の本文で、次のコードを追加して、オブジェクト検出の結果を表示します。
// Display object detection results.

  const ratio = resultElement.height / resultElement.naturalHeight;

  for (const detection of detections) {
    // Description text
    const p = document.createElement("p");
    p.setAttribute("class", "info");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    // Positioned at the top-left of the bounding box.
    // Height is that of the text.
    // Width subtracts text padding in CSS so that it fits perfectly.
    p.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px; " +
      "width: " +
      (detection.boundingBox.width * ratio - 10) +
      "px;";
    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      detection.boundingBox.originX * ratio +
      "px;" +
      "top: " +
      detection.boundingBox.originY * ratio +
      "px;" +
      "width: " +
      detection.boundingBox.width * ratio +
      "px;" +
      "height: " +
      detection.boundingBox.height * ratio +
      "px;";

    resultElement.parentNode.appendChild(highlighter);
    resultElement.parentNode.appendChild(p);
  }

この関数は、画像内で検出されたオブジェクトの上に境界ボックスを配置します。前のハイライト部分を削除します。<p> タグを作成して表示し、検出されたオブジェクトをハイライト表示します。

アプリをテストする

CodePen でコードを変更して保存すると、プレビュー ペインが自動的に更新されます。自動保存を有効にすると、アプリはすぐに更新されますが、再度更新してみても構いません。

アプリをテストするには、次の操作を行います。

  1. プレビュー ペインで各画像を確認し、予測を表示します。境界ボックスに犬の名前と、モデルの信頼度が表示されます。
  2. 境界ボックスが表示されない場合は、Chrome DevTools を開いて [Console] パネルでエラーを確認します。あるいは、前のステップを見直して不足しているものがないか確認します。

ウェブアプリのプレビュー。画像から検出した犬の上に境界ボックスが表示されています。

6. ライブ ウェブカメラの動画で予測を実行する

オブジェクトを検出する

  • ライブ ウェブカメラの映像からオブジェクトを検出するには、predictWebcam() 関数に移動して、関数の本文に次のコードを追加します。
// Run video object detection.
  // If image mode is initialized, create a classifier with video runningMode.
  if (runningMode === "IMAGE") {
    runningMode = "VIDEO";
    await objectDetector.setOptions({ runningMode: runningMode });
  }
  let startTimeMs = performance.now();

  // Detect objects with the detectForVideo() method.
  const detections = await objectDetector.detectForVideo(video, startTimeMs);

  displayVideoDetections(detections);

動画のオブジェクト検出では、推論の対象がストリーミング データか完全な動画かかにかかわらず、同じメソッドを使用します。この detectForVideo() メソッドは写真に使用する detect() メソッドに似ていますが、現在のフレームに関連付けられたタイムスタンプのパラメータが追加されています。この関数は検出をライブで実行するため、タイムスタンプとして現在の時刻が渡されます。

予測を処理して表示する

  • 検出結果を処理して表示するには、displayVideoDetections() 関数に移動し、関数の本文に次のコードを追加します。
//  Display video object detection results.
  for (let child of children) {
    liveView.removeChild(child);
  }
  children.splice(0);

  // Iterate through predictions and draw them to the live view.
  for (const detection of result.detections) {
    const p = document.createElement("p");
    p.innerText =
      detection.categories[0].categoryName +
      " - with " +
      Math.round(parseFloat(detection.categories[0].score) * 100) +
      "% confidence.";
    p.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px; " +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;";

    const highlighter = document.createElement("div");
    highlighter.setAttribute("class", "highlighter");
    highlighter.style =
      "left: " +
      (video.offsetWidth -
        detection.boundingBox.width -
        detection.boundingBox.originX) +
      "px;" +
      "top: " +
      detection.boundingBox.originY +
      "px;" +
      "width: " +
      (detection.boundingBox.width - 10) +
      "px;" +
      "height: " +
      detection.boundingBox.height +
      "px;";

    liveView.appendChild(highlighter);
    liveView.appendChild(p);

    // Store drawn objects in memory so that they're queued to delete at next call.
    children.push(highlighter);
    children.push(p);
  }

このコードは、前の例のハイライト部分をすべて削除します。<p> タグを作成して表示し、検出されたオブジェクトをハイライト表示します。

アプリをテストする

オブジェクトのライブ検出をテストする場合、モデルのトレーニングに使用した犬の画像があると便利です。

アプリをテストするには、次の操作を行います。

  1. 犬の写真の 1 つをスマートフォンにダウンロードします。
  2. プレビュー ペインで [Enable webcam] をクリックします。
  3. ブラウザでウェブカメラへのアクセス権が確認されたら、権限を付与します。
  4. スマートフォンで犬の写真を表示して、ウェブカメラに向けて置きます。境界ボックスに犬の名前とモデルの信頼度が表示されます。
  5. 境界ボックスが表示されない場合は、Chrome DevTools を開いて [Console] パネルでエラーを確認します。あるいは、前のステップを見直して不足しているものがないか確認します。

ライブ ウェブカメラに向けて置いた犬の写真。犬の画像の上に境界ボックスが表示されています。

7. 完了

これで完了です。ここでは、画像内のオブジェクトを検出するウェブアプリを作成しました。さらに学習するには、CodePen でのアプリの完成バージョンをご覧ください。

詳細