介绍 mediapipe

MediaPipe 提供了一套库和工具,供用户在应用程序中快速应用人工智能 (AI) 和机器学习 (ML) 技术 – Github/MediaPipe

这些灵活的工具构建在 TensorFlow Lite 之上 – mediapipe homepage

Mediapipe 提供了一系列预训练的模型和处理工具,用于处理各种媒体数据。这些模型可以用于各种用途,如姿态检测、手部追踪、人脸检测、手势识别等 – ChatGPT

将 mediapipe 引入 Snap!

视频讲解

目标

我们希望引入 mediapipe 的所有工作都完全在积木环境中完成!

这样有如下好处:

  1. 无需更新平台, 不需要开发者介入, 所有工作都在用户环境中完成(只是一个 Snap! 项目), 这意味着普通用户可以继续扩展这些能力。 (这是终端用户编程的一个例子)
  2. 可以充分利用 Snap! 的活性(liveness), 享受高效而愉快的开发体验。

动态导入

mediapipe 提供了 JavaScript API, 因此我们似乎可以通过 JavaScript function 将 mediapipe 引入到 Snap! 中。

import() 可以将 ECMAScript 模块异步动态加载到非模块环境中, 它正是我们需要的。

让我们试着引入 mediapipe 库。mediapipe有丰富的功能, 我们打算制作的一个例子是 Gesture Recognition(点击体验)。参考了 mediapipe 的文档使用案例

项目代码:

以下介绍关键的部分.

首先是使用 import() 动态引入模块:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 避免重复导入
if (window.FilesetResolver) {return}

// https://developers.google.com/mediapipe/api/solutions/js/tasks-vision
url = "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.13" // @latest
import(url).then(vision => {
  // 将模块中的函数, 放到全局变量里
  window.FilesetResolver = vision.FilesetResolver;
  window.GestureRecognizer = vision.GestureRecognizer;
  window.DrawingUtils = vision.DrawingUtils;
});

导入后就可以使用模块中的函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// https://codepen.io/mediapipe-preview/details/zYamdVd
// url 中的资源可托管到国内
if (window.gestureRecognizer) {return}
const createGestureRecognizer = async () => {
  const vision = await FilesetResolver.forVisionTasks(
    "https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.13/wasm"
  );
  gestureRecognizer = await GestureRecognizer.createFromOptions(vision, {
    baseOptions: {
      // "https://scratch3-files.just4fun.site/gesture_recognizer.task"
      modelAssetPath: "https://storage.googleapis.com/mediapipe-models/gesture_recognizer/gesture_recognizer/float16/1/gesture_recognizer.task", 
      delegate: "GPU"
    },
    numHands: 2,
  });
  window.gestureRecognizer = gestureRecognizer;
  // demosSection.classList.remove("invisible");
};
createGestureRecognizer();

之后就可以使用 gestureRecognizer 识别图片.

绘制手部关键点

官方示例中不仅给出了手势结果, 还将绘制出了关键点:

手部关键点的含义如下:

从官方示例的代码中可以看出, 绘制工作使用了 drawingUtils, 相比于去阅读 drawingUtils 代码(通常非常乏味), 我更愿意基于原始数据在 Snap! 中绘图, 这样我们可以充分享受 Snap! 活性(liveness)的好处。Snap! 的画笔功能非常易用和强大, 我们可以通过它来画图:

具体而言, 将 results.landmarks 包含的坐标信息使用画笔绘制出来。在这个过程中需要将 mediapipe 的坐标系转化(左上角为原点, y 轴正方向向下)为 Snap! 坐标系。

在绘制的过程中, Snap! 作为交互式个人计算愿景的产物, 其包含的特性(liveness 等)特别有助于探索数据, 用户可以交互式地观察数据:

对当前的某一桢感兴趣时, 可以暂停程序, 将系统定格在这个瞬间:

仿佛冻结了时间, 探索完当前时间截面的数据, 继续恢复程序。

具体参考 项目代码

在示例项目中, 我们只绘制了 1 只手的数据, results.landmarks 有可能会包含 2 只手的数据。

与 MicroBlocks 的互操作

mediapipe 可以充分利用边缘设备(手机/平板)的计算能力, 来提供 AI 功能。 边缘设备的算力越来越强, 苹果最近推出的 M4 芯片 拥有苹果有史以来最快的神经引擎,每秒能够执行高达 38 万亿次操作。 苹果的人工智能战略似乎是在边缘设备上进行推理/学习。

随着 WebGPU 的标准化, Snap! 将可以充分利用到这些性能, 由于 Snap! 也可以基于蓝牙无线连接 MicroBlocks 设备, 以此我们似乎可以将手机用作单片机的算力, 使用 mediapipe 这样的框架, 使用手机制作一个无线 AI 摄像头(就像二哈识图(HuskyLens)摄像头)似乎是很容易的。

参考