使用 MediaPipe 创建自定义对象检测 Web 应用

1. 准备工作

借助 MediaPipe Solutions,您可以为应用采用机器学习 (ML) 解决方案。它提供了一个框架,可让您配置预构建的处理流水线,用于为用户提供即时的、有吸引力的有用输出。您甚至可以使用 Model Maker 自定义这些解决方案,以更新默认模型。

对象检测是 MediaPipe Solutions 提供的多项机器学习视觉任务之一。MediaPipe Tasks 适用于 Android、Python 和 Web。

在此 Codelab 中,您将向 Web 应用添加对象检测功能,以检测图片和实时摄像头视频中的狗。

学习内容

  • 如何使用 MediaPipe Tasks 将对象检测任务整合到 Web 应用中。

您将构建的内容

  • 可检测是否存在狗的 Web 应用。您还可以使用 MediaPipe Model Maker 自定义模型,以检测您选择的对象类别。

所需条件

  • CodePen 帐号
  • 配有网络浏览器的设备
  • 具备 JavaScript、CSS 和 HTML 方面的基础知识

2. 进行设置

此 Codelab 将在 CodePen 中运行您的代码。CodePen 是一个社交开发环境,可让您在浏览器中编写代码并在构建时查看结果。

请按以下步骤完成设置:

  1. 在您的 CodePen 帐号中,转到此 CodePen。您可以使用此代码作为基础来创建您自己的对象检测器。
  2. 在 CodePen 底部的导航菜单中,点击 Fork(创建分支)以生成起始代码的副本。

CodePen 中“Fork”(创建分支)按钮所在的导航菜单

  1. JS 标签页中,点击 f079bd83ad4547c9.png 展开箭头,然后选择 Maximize JavaScript editor(最大化 JavaScript 编辑器)。您只需在此 Codelab 的 JS 标签页中修改代码,因此无需查看 HTMLCSS 标签页。

查看起始应用

  1. 在预览窗格中,请注意有两张狗狗的图片和一个用于运行摄像头的选项。您在本教程中使用的模型是根据两张图片中显示的三只狗训练而成的。

起始代码中的 Web 应用预览

  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";

此代码使用 Skypack 内容分发网络 (CDN) 导入该软件包。如需详细了解如何将 Skypack 与 CodePen 搭配使用,请参阅 Skypack + CodePen

在项目中,您可以将 Node.js 与 npm 或您选择的软件包管理器或 CDN 搭配使用。如需详细了解您需要安装的必需软件包,请参阅 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() 方法会实例化对象检测器。您必须提供用于检测的模型的路径。在此示例中,狗狗检测模型托管在 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 开发者工具,然后查看控制台面板是否存在错误,或检查前面的步骤以确保您没有漏掉任何操作。

在图片中检测到的狗上方带有边界框的 Web 应用预览

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. 将一张狗狗照片下载到您的手机上。
  2. 在预览窗格中,点击 Enable webcam(启用摄像头)。
  3. 如果浏览器显示一个对话框,要求您授予访问摄像头的权限,请授予权限。
  4. 将手机上的狗狗照片放在摄像头前。边界框会显示狗狗的名字和模型的置信度。
  5. 如果没有边界框,请打开 Chrome 开发者工具,然后查看控制台面板是否存在错误,或检查前面的步骤以确保您没有漏掉任何操作。

在实时摄像头前的狗狗图片上的边界框

7. 恭喜

恭喜!您构建了一个可检测图片中对象的 Web 应用。如需了解详情,请参阅 CodePen 上的应用完整版本

了解详情