使用 MediaPipe 和 TensorFlow.js 进行人体分割
2022 年 1 月 31 日

发布者 Ivan GrishchenkoValentin BazarevskyAhmed SabieJason Mayes,Google

随着人们对健康和健身的兴趣日益浓厚,我们看到越来越多的 TensorFlow.js 用户在 2021 年开始使用我们现有的与人体相关的 ML 模型,例如 人脸网格人体姿态手部姿态 估计。

今天,我们发布了两个新的高度优化的身体分割模型,这些模型既准确又快,是我们更新的 TensorFlow.js 中人体分割和姿态 API 的一部分。

第一个是 BlazePose GHUM 姿势估计模型,现在它还支持分割。该模型是我们统一的姿态检测 API 的一部分,可以同时进行全身分割和 3D 姿势估计,如下面的动画所示。它非常适合拍摄全景的人体,这些人体距离摄像机更远,可以准确地捕捉到脚和腿部区域,例如。

尝试一下实时演示!

我们发布的第二个模型是自拍分割,它非常适合那些在视频通话(<2 米)时直接面对网络摄像头的人。该模型是我们统一的人体分割 API 的一部分,在整个上半身的准确性更高,如下面的动画所示,但在某些情况下,下半身的准确性可能较低。

尝试一下实时演示!

这两个新模型都可以实现围绕人体的一系列创意应用程序,这些应用程序可以驱动下一代 Web 应用程序。例如,BlazePose GHUM 姿势模型可以为像 将您的数字存在传送到世界上的任何地方 这样的服务提供支持,为虚拟裁缝估计人体尺寸,或者为音乐视频创建特殊效果等等,可能性是无限的。相比之下,自拍分割模型可以为基于 Web 的视频通话提供用户友好的功能,例如上面的演示,您可以准确地更改或模糊背景。

在此发布之前,我们许多用户可能尝试过我们的 BodyPix 模型,该模型在发布时是当时最先进的。随着今天的发布,我们的两个新模型在各种用例中,在所有设备上提供了更高的 FPS 和保真度。

人体分割 API 安装

人体分割 API 为自拍分割模型提供了两种运行时,即 MediaPipe 运行时和 TensorFlow.js 运行时。

要安装 API 和运行时库,您可以使用 html 文件中的<script> 标签或使用 NPM。

通过脚本标签


<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs-backend-webgl">
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow-models/body-segmentation">

<!-- Optional: Include below scripts if you want to use TensorFlow.js runtime. -->
<script src="https://cdn.jsdelivr.net.cn/npm/@tensorflow/tfjs-converter">

<!-- Optional: Include below scripts if you want to use MediaPipe runtime. -->
<script src="https://cdn.jsdelivr.net.cn/npm/@mediapipe/selfie_segmentation">

通过 NPM

yarn add @tensorflow/tfjs-core @tensorflow/tfjs-backend-webgl
yarn add @tensorflow-models/body-segmentation

# Run below commands if you want to use TensorFlow.js runtime.
yarn add @tensorflow/tfjs-converter

# Run below commands if you want to use MediaPipe runtime.
yarn add @mediapipe/selfie_segmentation

在您的 JS 代码中引用 API 取决于您安装库的方式。

如果通过脚本标签安装,您可以通过全局命名空间bodySegmentation 引用库。

如果通过 NPM 安装,您需要先导入库

import '@tensorflow/tfjs-backend-core';
import '@tensorflow/tfjs-backend-webgl';
import * as bodySegmentation from '@tensorflow-models/body-segmentation';

// Uncomment the line below if you want to use TensorFlow.js runtime.
// import '@tensorflow/tfjs-converter';

// Uncomment the line below if you want to use MediaPipe runtime.
// import '@mediapipe/selfie_segmentation';

自己尝试一下!

首先,您需要创建一个分割器

const model = bodySegmentation.SupportedModels.MediaPipeSelfieSegmentation; // or 'BodyPix'

const segmenterConfig = {
  runtime: 'mediapipe', // or 'tfjs'
  modelType: 'general' // or 'landscape'
};

segmenter = await bodySegmentation.createSegmenter(model, segmenterConfig);

选择适合您的应用程序需求的 modelType,您可以选择两种选项:generallandscape。从landscapegeneral,准确性提高,推理速度降低。请尝试我们的 实时演示,以比较不同的配置。

拥有分割器后,您可以将视频流、静态图像或 TensorFlow.js 张量传递进去,以对人进行分割

const video = document.getElementById('video');
const people = await segmenter.segmentPeople(video);

如何使用输出?

上面的people 结果表示图像帧中找到的分割人的数组。但是,每个模型对给定分割的语义不同。

对于自拍分割,数组的长度将正好为 1,其中单个分割对应于图像帧中的所有人。对于每个segmentation,它包含maskValueToLabelmask 属性,具体说明如下。

mask 字段存储一个对象,该对象提供对分割的底层结果的访问。然后,您可以利用提供的异步转换函数(例如toCanvasImageSourcetoImageDatatoTensor),具体取决于您想要的输出类型,以提高效率。

需要注意的是,不同的模型具有不同的数据内部表示形式。因此,从一种形式转换为另一种形式可能会很昂贵。为了提高效率,您可以调用getUnderlyingType 来确定分割的实际形式,以便您可以选择将其保留为相同形式以获得更快的结果。

mask 的 RGBA 值的语义如下:图像蒙版与输入图像的大小相同,其中绿色和蓝色通道始终设置为 0。不同的红色值表示不同的身体部位(请参阅下面的 maskValueToLabel 键)。不同的 alpha 值表示像素是身体部位像素的概率(0 表示最低概率,255 表示最高概率)。

maskValueToLabel 将像素的红色通道值映射到该像素的分割部位名称。这并不一定在不同的模型中相同(例如,SelfieSegmentation 将始终返回“person”,因为它不区分单个身体部位,而像 BodyPix 这样的模型将返回它可以区分的每个分割像素的单个身体部位的名称)。请参阅下面的输出片段示例

[
  {
    maskValueToLabel: (maskValue: number) => { return 'person' },
    mask: {
      toCanvasImageSource(): ...
      toImageData(): ...
      toTensor(): ...
      getUnderlyingType(): ...
    }
  }
]

我们还提供了一个可选的实用程序函数,您可以使用它来呈现分割结果。使用toBinaryMask 函数将分割转换为 ImageData 对象。

该函数接受 5 个参数,后 4 个参数是可选的

  1. 来自上面 segmentPeople 调用的分割结果。
  2. 前景颜色 - 一个对象,表示用于呈现前景像素的 RGBA 值。
  3. 背景颜色 - 包含背景像素的 RGBA 值的对象
  4. 绘制轮廓 - 布尔值,表示是否在找到的人的周围绘制轮廓线。
  5. 前景阈值 - 像素应被视为前景像素还是背景像素的临界点。这是一个介于 0 到 1 之间的浮点值。

获得toBinaryMask 的 imageData 对象后,您可以使用drawMask 函数将其呈现到您选择的画布上。

下面展示了使用这两个函数的示例代码

const foregroundColor = {r: 0, g: 0, b: 0, a: 0};
const backgroundColor = {r: 0, g: 0, b: 0, a: 255};
const drawContour = true;
const foregroundThreshold = 0.6;

const backgroundDarkeningMask = await bodySegmentation.toBinaryMask(people, foregroundColor, backgroundColor, drawContour, foregroundThreshold);

const opacity = 0.7;
const maskBlurAmount = 3; // Number of pixels to blur by.
const canvas = document.getElementById('canvas');

const people = await bodySegmentation.drawMask(canvas, video, backgroundDarkeningMask, opacity, maskBlurAmount);

姿态检测 API 使用

要加载和使用 BlazePose GHUM 模型,请参考统一的 姿态 API 文档。该模型有三个输出

  1. 2D 关键点
  2. 3D 关键点
  3. 每个找到的姿态的分割。

如果您需要从姿态结果中获取分割,您可以简单地获取对该姿态的分割属性的引用,如下所示

const poses = await detector.estimatePoses(video);
const firstSegmentation = poses.length > 0 ? poses[0].segmentation : null;


模型深入分析

BlazePose GHUM 和 MediaPipe 自拍分割模型会分割帧中突出的人。两者都在笔记本电脑和智能手机上实时运行,但它们的预期应用程序有所不同,如本文开头所述。自拍分割侧重于自拍效果和会议,适用于近距离情况(<2 米),而 BlazePose GHUM 专门针对全身情况,例如瑜伽、健身、舞蹈,并且在距离摄像头 4 米以内的地方也能正常工作。

自拍分割

自拍分割模型预测人类前景的二进制分割掩码。该管道被构建为完全在 GPU 上运行,从图像采集到神经网络推理,再到在屏幕上渲染分割结果。它避免了缓慢的 CPU-GPU 同步,并实现了最高性能。该模型的变体为 Google Meet 中的背景替换 提供支持,现在 TensorFlow.jsMediaPipe 中也提供了一个更通用的模型。

BlazePose GHUM 2D 标记和人体分割

BlazePose GHUM 模型现在除了之前介绍的 2D3D 标记 之外,还提供了一个人体分割掩码。拥有一个预测这两个输出的单一模型,让我们获得了两个好处。首先,它允许输出相互监督和改进,因为标记提供语义结构,而分割侧重于边缘。其次,它保证预测的掩码和点属于同一个人,而这很难用单独的模型来实现。由于 BlazePose GHUM 模型只在 人的 ROI 裁剪(而不是整个图像)上运行,因此分割掩码质量仅取决于 ROI 内的有效分辨率,并且在离摄像头更近或更远时不会发生很大变化。


会议

ASL

瑜伽

舞蹈

HIIT

BlazePose GHUM(完整)

95.50%

96.52%

94.73%

94.55%

95.16%

自拍分割(256x256)

97.60%

97.88%

80.66%

86.33%

85.53%

BlazePose GHUM 和自拍分割在不同领域中的 IOU

MediaPipe 和 TensorFlow.js 运行时

使用每个运行时都有一些优点和缺点。如下面的性能表所示,MediaPipe 运行时在台式机、笔记本电脑和安卓手机上提供了更快的推理速度。TensorFlow.js 运行时在 iPhone 和 iPad 上提供了更快的推理速度。

此处显示的 FPS 数是模型执行推断并等待 GPU 和 CPU 同步所需的时间。这样做是为了确保 GPU 在基准测试目的下完全完成,但在纯 GPU 生产管道中不需要等待,因此您的数字可能更高。对于纯 GPU 管道,如果使用 MediaPipe 运行时,只需使用 await mask.toCanvasImageSource(),如果使用 TF.js 运行时,请参考 此示例,了解如何直接使用纹理以保持 GPU 用于渲染效果。

基准测试

自拍分割模型


MacBook Pro 15” 2019. 

Intel core i9. 

AMD Radeon Pro Vega 20 Graphics.

(FPS)

iPhone 11

(FPS - MediaPipe 的 CPU 专用)

Pixel 6 Pro

(FPS)

台式机 

Intel i9-10900K. Nvidia GTX 1070 GPU.

(FPS)

MediaPipe 运行时

使用 WASM 和 GPU 加速。

125 | 130

31 |  21

35 | 33

185 | 225

TFJS 运行时

使用 WebGL 后端。

74 | 45

42 | 30

25 | 23

80 | 62

自拍分割在不同设备和运行时下的推理速度。每个单元格中的第一个数字表示横向模型,第二个数字表示通用模型。

BlazePose GHUM 模型


MacBook Pro 15” 2019. 

Intel core i9. 

AMD Radeon Pro Vega 20 Graphics.

(FPS)

iPhone 11

(FPS - MediaPipe 的 CPU 专用)

Pixel 6 Pro

(FPS)

台式机 

Intel i9-10900K. Nvidia GTX 1070 GPU.

(FPS)

MediaPipe 运行时

使用 WASM 和 GPU 加速

70 | 59 | 31

8 | 5 | 1

22 | 19 | 10

123 | 112 |  70

TFJS 运行时

使用 WebGL 后端。

42 | 36 | 22

14 | 12 | 8

12 | 10 | 6

35  | 33 | 26

BlazePose GHUM 全身分割在不同设备和运行时下的推理速度。每个单元格中的第一个数字表示轻量级模型,第二个数字表示完整模型,第三个数字表示模型的重量级版本。请注意,可以通过在模型参数中将 enableSegmentation 设置为 false 来关闭分割输出,这将提高模型性能。

展望未来

我们一直在努力改进技术的各项功能和质量(例如,这是去年在初始的 2D 版本 和随后的 3D 更新 后,BlazePose GHUM 的第三次更新),因此请期待未来即将推出的令人兴奋的新更新。

致谢

我们要感谢参与或赞助创建自拍分割、BlazePose GHUM 和构建 API 的同事:Siargey Pisarchyk、Tingbo Hou、Artsiom Ablavatski、Karthik Raveendran、Eduard Gabriel Bazavan、Andrei Zanfir、Cristian Sminchisescu、Chuo-Ling Chang、Matthias Grundmann、Michael Hays、Tyler Mullen、Na Li、Ping Yu。

下一篇文章
Body Segmentation with MediaPipe and TensorFlow.js

作者:Ivan GrishchenkoValentin BazarevskyAhmed SabieJason Mayes,Google 随着人们对健康和健身的兴趣日益浓厚,我们看到越来越多的 TensorFlow.js 用户在 2021 年开始使用我们现有的与人体相关的 ML 模型,例如 面部网格人体姿态手势 估计。今天,我们发布了两个高度优化的身体分割模型,这些模型是…