一个允许在浏览器中进行实时人体姿势判断的机器学习模型

描述

通过与 Google 创意实验室的合作,我很高兴地宣布 TensorFlow.js 版本的 PoseNet¹,² 发布了,这是一个允许在浏览器中进行实时人体姿势判断的机器学习模型。 

PoseNet 可以使用单一姿势或多种姿势算法检测到图像和视频中的人物 - 所有这些均来自浏览器

那么究竟什么是姿势判断呢? 姿势判断是指在图像和视频中检测人物形象的计算机视觉技术,比如可以确定某个人的肘部出现在图像中的位置。 需要澄清的是,这项技术无法识别图像中的人物到底是谁 – 因为没有任何与识别身份相关的个人身份信息。 该运算法则仅仅是判断人体主要关节的位置。

好吧,知道为什么这是一个令人兴奋的开始? 姿势判断有许多用途,从对身体做出反应的交互式装置到增强现实,动画,健身用途等等。 我们希望此模型的可访问性能够激发更多开发人员和制造商尝试将姿态检测应用到他们自己的项目中去。 虽然许多可选的姿势检测系统都已开源,但都需要配备专门的硬件和/或摄像头,以及繁复的系统设置。 

PensNet 在 TensorFlow.js 上运行后,只要配备适合网络摄像头的 PC 或手机,任何人可以在网络浏览器中体验这项技术。 由于我们已经开源了该模型,因此 Javascript 开发人员只要通过几行代码就能修补和使用这项技术。更重要的是,这实际上还能有助保护用户隐私。由于 TensorFlow.js 上的 PoseNet 是在浏览器中运行,因此任何姿势数据都不会留在用户的计算机上。

在深入研究该如何使用这个模型之前,对于那些将该项目付诸实现的人们,让我们大声欢呼向他们致敬:发布在 Wild and PersonLab 上有关于准确的多人姿势判断的文章《自下而上,基于部分的几何嵌入模型进行人物姿势判断及实例分割》背后的谷歌研发人员 George Papandreou 和 Tyler Zhu, 以及 TensorFlow.js 库背后的 Google Brain 团队的工程师 Nikhil Thorat 和 Daniel Smilkov。

PoseNet 入门

PoseNet 可用于判断单一姿势或多个姿势, 这就意味着会有一个检测图像/视频中单人算法版本以及另一个可以检测图像/视频中多个人的版本。 为什么有两个版本? 单人姿势检测器更快更简单,但是需要图像中只有一个主体(稍后会深入探讨)。 我们首先来讲一讲简单易用的单一姿势版本。

姿势判断在上层会分成两个阶段进行:

1. 输入 RGB 图像通过卷积神经网络馈送。

2. 单一姿势或多姿势解码算法用于从模型输出解码姿势,姿势置信度得分,关键点位置和关键点置信度得分。

稍等一下,这些关键字的含义是什么? 让我们回顾一下最重要的内容:

姿势 - 在最高级别,PoseNet 将每个检测到的人的关键点列表和实例级置信度分数反馈给一个姿势对象。

PoseNet 反馈检测到的每个人的置信度值以及检测到的每个姿势关键点。 Image Credit:“Microsoft Coco:Context Dataset 中的公共对象”,https://cocodataset.org。

姿势置信度 – 这决定了姿势判断的整体置信度。 范围介于 0.0 到 1.0 之间。 它可以用来隐藏那些幅度不够大的姿势。

关键点 – 它是判断人体姿势的一部分,例如鼻子,右耳,左膝,右脚等。它包含了位置和关键点置信度分数。 PoseNet 目前可检测到下图所示的 17 个关键点:

PoseNet 检测到的 17 个姿势关键点

关键点置信度得分 - 这决定了估计关键点位置准确的置信度。 范围在 0.0 到 1.0 之间。 它可用于隐藏那些不够强大的关键点。

关键点位置 – 检测到的关键单在原始输入图像中的  x 和 y 二维坐标。

第1步:导入 TensorFlow.js 和 PoseNet 库

将模型的复杂性抽象化并将功能封装为易于使用的方法,这方面已经做了很多的工作。 让我们回顾一下如何设置 PoseNet 项目的基础知识。

该库可以使用 npm 安装:

npm install @tensorflow-models/posenet

并使用 es6 模块导入:

import * as posenet from '@tensorflow-models/posenet';const net = await posenet.load();

或通过页面中的 bundle:

                                                 

第 2a 步:单人姿态判断

应用于图像的单人姿势判断算法示例

Image Credit:“Microsoft Coco:Context Dataset 中的通用对象”,https://cocodataset.org

如前所述,两个版本中,单一姿势判断算法更简单,更快速。 它的理想场景是只有一个人居于输入图像或视频中间。 缺点是,如果图像中有多个人,那么来自两个人的关键点可能被判断为同一个单一姿势的一部分 – 意思就是,例如,第 1 个人的左臂和第 2 个人的右膝通过算法被判断属于相同的姿势而被混淆。 如果输入图像中包含多人,则应该使用多姿势判断的算法。

让我们回顾一下单一姿势判断算法的输入:

输入图像元素 - 包含用于预测姿势的图像的 html 元素,例如视频或图像标签。 重要的一点是,输入的图像或视频元素必须是方形的。

图像比例系数 – 是介于 0.2 和 1 之间的数字。默认为 0.50。 在输入到网络之前的缩放图像比例。 将此数值设置得较低可以缩小图像,以牺牲精度为代价从而提高速度。

水平翻转 - 默认为 false。 姿势应该是水平翻转/镜像。 对于视频默认水平翻转的视频(即网络摄像头),如果您希望姿势得以正确的方向回馈,应将此设置为 true。

输出步幅 - 必须为 32,16 或 8。默认值为 16。在内部,此参数会影响神经网络中图层的高度和宽度。 在上层来看,它会影响姿势判断的精度和速度。 输出值越低,精度越高但速度越慢;输出值越高,速度越快但精度越低。 查看输出步幅对输出质量影响的最佳方法是尝试使用这个单一姿势判断的实例。

现在让我们回顾一下单一姿势判断算法的输出:

包含姿势置信度得分和 17 个关键点数组的姿势。

每个关键点包含关键点位置和关键点置信度得分。 同样,所有关键点位置在输入图像空间中都有 x 和 y 坐标,并且可以直接映射到图像上。

这个简短的代码块展示了如何使用单一姿势判断算法:

const imageScaleFactor = 0.50;const flipHorizontal = false;const outputStride = 16;

const imageElement = document.getElementById('cat');

// load the posenet modelconst net = await posenet.load();

const pose = await net.estimateSinglePose(imageElement, scaleFactor, flipHorizontal, outputStride)

示例输出姿势如下所示:

{  "score": 0.32371445304906,  "keypoints": [    { // nose      "position": {        "x": 301.42237830162,        "y": 177.69162777066      },      "score": 0.99799561500549    },    { // left eye      "position": {        "x": 326.05302262306,        "y": 122.9596464932      },      "score": 0.99766051769257    },    { // right eye      "position": {        "x": 258.72196650505,        "y": 127.51624706388      },      "score": 0.99926537275314    },    ...  ]}

第 2b 步: 多人姿势判断

应用于图像的示例多人姿势判断算法

Image Credit: “Microsoft Coco: Common Objects in Context Dataset”, https://cocodataset.org

多人姿势判断算法可以判断图像中许多的姿势/人。 它比单一姿势算法更为复杂,且速度稍慢,不过它的优点是,如果图片中出现多个人,他们检测到的关键点不太可能与错误的姿势相关联。 因此,即使用例是检测单个人的姿势,这个算法可能更合乎需要。

此外,该运算法则一个引人入胜的特性是其性能不会因输入图像中的人数多少而受到影响。 不管是检测 15 个人或 5 个人,计算的时间是相同的。

我们来看一下输入:

输入图像元素 - 与单一姿势判断相同

图像比例系数 - 与单一姿势判断相同

水平翻转 - 与单一姿势判断相同

输出步幅 - 与单一姿势判断相同

幅度最大的姿势检测 - 整数。 默认为 5. 要检测的幅度最大的姿势数值。

姿势置信度得分阈值 - 0.0 到 1.0。 默认为 0.5。 在较高级别,这将控制回馈的姿势最低置信度分数。

非最大抑制(NMS)半径 – 这是一个以像素为单位的数字。在上层,它会控制返回姿势之间的最小距离。其默认值为 20,这在大多数情况下均表现良好。只有在调整姿势置信度得分不够好的情况下,为了过滤掉不太准确的姿势,该数值应该增加或减少。

查看这些参数影响的最佳方法是尝试一下多姿势判断的演示。

让我们再看一下输出:

一个通过一系列姿势来解决的承诺。

每个姿势包含与单人判断算法中相同描述的信息。

这个短代码块展示了如何使用多姿势判断算法:

const imageScaleFactor = 0.50;const flipHorizontal = false;const outputStride = 16;// get up to 5 posesconst maxPoseDetections = 5;// minimum confidence of the root part of a poseconst scoreThreshold = 0.5;// minimum distance in pixels between the root parts of posesconst nmsRadius = 20;

const imageElement = document.getElementById('cat');// load posenet

const net = await posenet.load();

const poses = await net.estimateMultiplePoses(      imageElement, imageScaleFactor, flipHorizontal, outputStride,          maxPoseDetections, scoreThreshold, nmsRadius);

姿势的示例输出数组如下所示:

// array of poses/persons[  { // pose #1    "score": 0.42985695206067,    "keypoints": [      { // nose        "position": {          "x": 126.09371757507,          "y": 97.861720561981         },        "score": 0.99710708856583      },      ...    ]  },  { // pose #2    "score": 0.13461434583673,    "keypositions": [      { // nose        "position": {          "x": 116.58444058895,          "y": 99.772533416748        },      "score": 0.9978438615799      },      ...    ]  },  ... ]

如果您已经阅读过这篇文章,那么您就已经了解了 PoseNet 演示的全部内容。 这也许是一个很好的停止点。 如果您想深入了解有关模型和实施的技术细节的更多信息,我们邀请您继续阅读下文。

致孜孜不倦的钻研者:技术深潜

在本节中,我们将详细介绍单一姿势判断算法。在上层,该过程如下所示:

使用 PoseNet 的单人姿势探测器通道

需要注意的一个重要细节是,研究人员同时训练了 PoseNet 上的一个 ResNet 模型和一个 MobileNet 模型。 虽然 ResNet 模型具有更高的准确性,但对于实时应用程序来说,其大尺寸和多层面使页面加载时间和推理时间变得不太理想。我们使用 MobileNet 模型,因为它是专为移动设备上运行而设计的。

注: ResNet 模型链接

https://arxiv.org/abs/1512.03385

MobileNet 模型链接

https://arxiv.org/abs/1704.04861

再看一下单一姿势判断算法

处理模型输入:输出步幅的解释

首先,我们将讨论如何通过输出步幅来获得 PoseNet 模型输出(主要是热图和偏移矢量)。

PoseNet 模型可以方便地保持图像大小恒定,意思是不管图像是否缩小,它能够以与原始图像相同的比例预测姿势位置。 这就意味着通过设置我们在运行上面提到的输出步幅时,在牺牲了性能的情况下,可以将 PoseNet 配置成更高的精度。

输出步幅决定了我们相对于输入图像大小缩小输出的程度。 它会影响图层的大小和模型输出。 输出步幅越高,网络中的层的分辨率和输出越小,相应地它们的精度也越高。 在此应用中,输出步幅可以取值 8,16 或 32。换句话说,在输出步幅为 32 的情况下,能够取得最快的表现但是精度却是最低,而 8 则相反,精度最高但表现最慢。 我们建议从 16 开始。

输出步幅决定了我们相对于输入图像大小缩小输出的程度。 输出步幅更高,表现更快,但精度更低

在后台中,当输出步幅设置为 8 或 16 时,减少层中步幅的输入量就能创建更大的输出分辨率。 然后使用带孔卷积核使后续层中的卷积滤波器具有更宽的视场(当输出步幅为 32 时不适用带孔卷积核)。 虽然 Tensorflow 支持带孔卷积核,但 TensorFlow.js 却不支持,为此我们添加了一个 PR 来包含它。

模型输出: 热图和偏移矢量

当 PoseNet 处理图像时,实际上反馈的是热图以及偏移矢量,可以对其进行解码,用以在图像中找到与姿势关键点对应的高置信度区域。 我们可以快速讨论其中每一个意味着什么,但眼下我们在高级别情况下捕获的如下图演示了每个姿势关键点如何与一个热图张量和偏移矢量张量相关联。

PoseNet 反馈的 17 个姿势关键点中的每一个都与一个热图张量和一个偏移矢量张量相关联,用于确定关键点的确切位置

这两个输出都是具有高度和宽度的 3D 张量,我们将其称为分辨率。 分辨率根据以下公式由输入图像大小和输出步幅决定:

Resolution = ((InputImageSize - 1) / OutputStride) + 1

// Example: an input image with a width of 225 pixels and an output// stride of 16 results in an output resolution of 15// 15 = ((225 - 1) / 16) + 1

热图

每个热图是尺寸为分辨率 x 分辨率 x 17 的 3D 张量,因为 17 是 PoseNet 检测到的关键点的数量。 例如,图像大小为 225,输出步幅为 16,那么就是 15x15x17。 第三维(17)中的每个切片与特定关键点的热图对应。 该热图中的每个位置都有一个置信度分数,即该关键点类型的一部分存在于该位置的概率。 可以视作原始图像被分解成 15x15 网格,而热图分数将对每个网格方块中每个关键点存在的可能性进行分类。

偏移矢量

每个偏移矢量是尺寸分辨率 x 分辨率 x 34 的 3D 张量,其中 34 是关键点的数量 * 2。图像尺寸为 225,输出步幅为 16,那么就是 15x15x34。 由于热图是关键点所在的近似值,因此偏移矢量在位置上对应于热图点,并用于通过沿相应热图点的矢量行进来预测关键点的确切位置。 偏移矢量的前 17 个切片包含矢量的 x,最后的 17 个 y。 偏移矢量大小与原始图像的比例相同。

从模型的输出判断姿势

图像通过模型后,我们会执行一系列计算来判断输出的姿势。 例如,单一姿势判断算法会回馈姿势置信度得分,其本身包含关键点阵列(由部件 ID 索引),每个关键点具有置信度得分和 x,y 位置。

要获得姿势的关键点:

在热图上进行 S 形激活以获得分数。得分= heatmap.sigmoid()

argmax2d 在关键点置信度得分上完成,以获得热图中的x和y索引,每个部分的得分最高,这实际上是该部分最可能存在的位置。这会产生一个大小为 17x2 的张量,每一行都是热图中的 y 和 x 索引,每个部分的得分最高。heatmapPositions = scores.argmax(y,x)

通过从对应于该部件的热图中的 x 和 y 索引的偏移量获取 x 和 y 来检索每个部件的偏移矢量。这会产生一个大小为 17x2 的张量,每行是相应关键点的偏移量。例如,对于索引 k 处的部分,当热图位置为 y 和 d 时,偏移矢量为:offsetVector = [offsets.get(y,x,k),offsets.get(y,x,17 + k)]

为了得到关键点,每个部件的热图 x 和 y 乘以输出步幅,然后加到它们相应的偏移矢量,该矢量与原始图像的比例相同。keypointPositions = heatmapPositions * outputStride + offsetVectors

最后,每个关键点置信度得分是其热图位置的置信度得分。姿势置信度得分是关键点得分的平均值。

多人姿势判断

多姿势判断运算法则的详细内容就不在本文中一一赘述。 大体来说,该运算法则的不同之处在于它使用了贪婪过程,通过沿着基于部分的图形来跟随位移矢量来将关键点分组成姿势。 确切地说,就是它使用了研究论文 PersonLab 中的快速贪婪解码算法:人物姿势判断和实例分割与自下而上,基于部分的几何嵌入模型。 有关多姿势运算法则的更多信息,请阅读完整的研究论文或查看 代码。

注:代码链接

https://github.com/tensorflow/tfjs-models/tree/master/posenet/src

最后

我们希望随着越来越多的模型被移植导入到 TensorFlow.js,机器学习的世界变得对新的编码员和制造者来说更容易接近,更受欢迎,更有趣。 TensorFlow.js 上的 PoseNet 是一个小尝试,使之成为可能。 我们将很乐意看到你的制作 - 并且不要忘记使用 #tensorflowjs 和 #posenet 来分享你的精彩项目!

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分