原作者: TensorFlow(Dan Oved,谷歌创意实验室的自由创意技术员,纽约大学ITP的研究生.)
编辑和插图: 艾琳阿尔瓦拉多,创意技术师和亚历克西斯加洛,自由平面设计师,在谷歌创意实验室.
链接: https://medium.com/tensorflow/real-time-human-pose-estimation-in-the-browser-with-tensorflow-js-7dd0bc881cd5

与Google Creative Lab合作,我很高兴地宣布发布TensorFlow.js版本的PoseNet¹,²,一个允许在浏览器中进行实时人体姿势估计的机器学习模型。在这里试一试现场演示。

PoseNet可以使用单一姿势或多姿势算法检测图像和视频中的人物-所有这些都可以在浏览器中进行。

那么什么是姿势估计呢?姿势估计是指计算机视觉技术,它可以检测图像和视频中的人物,以便确定,例如,某人的肘部在图像中出现的位置。更清楚的是,这项技术并没有识别出图像中的人,也没有与姿势检测相关的个人识别信息。算法只是简单地估计关键身体关节的位置。

好吧,为什么一开始就让人兴奋?姿势估计有很多用途,从对身体做出反应的交互式 装置到增强现实、动画、健身用途等等。我们希望这个模型的可访问性能够激励更多的开发者和制作者在他们自己独特的项目中进行姿势检测的实验和应用。虽然许多替代姿态检测系统都是开源的,但所有这些系统都需要专门的硬件和或摄像头,以及相当多的系统设置。随着PoseNet在TensorFlow.js上的运行,任何拥有一个配备了不错的网络摄像头的桌面或手机的人都可以在网络浏览器中体验这项技术。由于我们已经开放了该模型的源代码,Javascript开发人员只需几行代码就可以修补并使用这项技术。此外,这实际上有助于保护用户隐私。由于TensorFlow.js上的PoseNet在浏览器中运行,因此任何姿势数据都不会离开用户的计算机。

在我们深入研究如何使用这个模型之前,首先向所有使这个项目成为可能的人欢呼:George Papandreou和Tyler Zhu,谷歌的研究人员,他们是论文的幕后推动者致力于精确的野外多人姿势估计PersonLab:基于自下而上部分的个人姿势估计和实例分割,几何嵌入模型,Nikhil Thorat和Daniel Smilkov,TensorFlow.js库背后的Google Brain团队的工程师。

PoseNet入门

PoseNet可用于估计单个姿势或多个姿势,这意味着算法的一个版本只能检测图像/视频中的一个人,而另一个版本可以检测图像/视频中的多个人。为什么有两个版本?单人姿势检测器更快更简单,但只需要图像中的一个对象(稍后会详细介绍)。我们先介绍一下单体式,因为它更容易跟随。

在高层次上,姿态估计分为两个阶段:

  1. 通过卷积神经网络输入RGB图像
  2. 使用单姿态或多姿态解码算法对模型输出的姿态(Pose)、姿态置信度得分(Pose confidence score)、关键点位置(Keypoint Position)和关键点置信度得分(Keypoint Confidence Score)进行解码。

等等,这些关键词是什么意思?让我们回顾一下最重要的:

  • 姿态(Pose) — 在最高级别,PoseNet将返回一个pose对象,该对象包含每个检测到的人的关键点列表和实例级置信度分数。

    PoseNet为检测到的每个人以及检测到的每个姿势关键点返回置信值。图片来源:“Microsoft Coco:当前数据集中的公共对象”, https://cocodataset.org.

  • 姿态置信度得分(Pose confidence score) — 这决定了姿态估计的总体置信度。范围在0.0到1.0之间。它可以用来隐藏被认为不够强壮的姿势。

  • 关键点(Keypoint) — 估计的人的姿势的一部分,如鼻子、右耳、左膝、右脚等。它包含一个位置和一个关键点的信心得分。PoseNet目前检测到17个关键点,如下图所示:

    PoseNet检测到1 7个姿势关键点

  • 关键点置信度得分(Keypoint Confidence Score) — 这将确定估计的关键点位置是否准确的置信度。范围在0.0到1.0之间。它可以用来隐藏被认为不够强大的关键点

  • 关键点位置(Keypoint Position) — 检测到关键点的原始输入图像中的二维x和y坐标。

Part 1: 导入TensorFlow.js和PoseNet库

在抽象模型的复杂性和将功能封装为易于使用的方法方面做了大量工作。让我们回顾一下如何设置PoseNet项目的基础知识。

可以使用npm安装库:

npm install @tensorflow-models/posenet

并使用es6模块导入:

   import * as posenet from '@[tensorflow-models/posenet](https://www.npmjs.com/package/@tensorflow-models/posenet)';
   const net = await posenet.load();

或者通过页面中的捆绑:


  
    
    
    
    
    
  

Part 2a: 单人姿势估计

应用于图像的示例单人姿势估计算法。图片来源:“Microsoft Coco:当前数据集中的公共对象”, https://cocodataset.org.

如前所述,单姿态估计算法是两种算法中比较简单和快速的。它的理想用例是当一个输入图像或视频中只有一个人的时候。缺点是,如果一个图像中有多个人,两个人的关键点很可能被估计为同一个姿势的一部分,这意味着,例如,该算法可能将第一个人的左臂和第二个人的右膝合并为属于同一姿势。如果输入图像可能包含多人,则应使用多姿态估计算法。

让我们回顾一下单姿态估计算法的输入

  • 输入图像元素(Input image element) — 一个html元素,它包含一个要预测姿势的图像,例如视频或图像标记。重要的是,输入的图像或视频元素应该是方形的

  • 图像比例因子(Image scale factor) — 介于0.2和1之间的数字。默认为0.50。在通过网络传送图像之前,通过什么来缩放图像。将此数字设置得更低,以缩小图像并在通过网络传输时提高速度,但会降低精度

  • 水平翻转(Flip horizontal) — 默认为false。如果姿势应该水平翻转/镜像。对于默认水平翻转视频(即网络摄像头)的视频,这应该设置为true,并且您希望以正确的方向返回姿势

  • 输出跨距(Output stride) — 必须是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 model
    const 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
        },
        ...
      ]
    }

Part 2b: 多人姿态估计

应用于图像的多人姿态估计算法示例。图片来源:“Microsoft Coco:当前数据集中的公共对象”, https://cocodataset.org

多人姿态估计算法可以估计图像中的多个姿态。它比单一姿势算法更复杂,速度稍慢,但它的优点是,如果一张图片中出现多个人,他们检测到的关键点就不太可能与错误的姿势相关联。因此,即使用例是为了检测一个人的姿势,这个算法可能更可取。

此外,该算法的一个吸引人的特性是性能不受输入图像中的人数的影响。无论是15人检测还是5人检测,计算时间都是一样的。

让我们回顾一下输入:

  • 输入图像元素(Input image element) — 与单姿态估计相同

  • 图像比例因子(Image scale factor) — 与单姿态估计相同

  • 水平翻转(Flip horizontal) — 与单姿态估计相同

  • 输出跨距(Output stride) — 与单姿态估计相同

  • 最大姿态检测(Maximum pose detections) — 一个整数。默认为5。要检测的pose的最大数目

  • 姿势置信得分阈值(Pose confidence score threshold) — 0.0到1.0。默认为0.5。在高水平上,这可以控制返回pose的最小置信分数

  • 非最大抑制(NMS) — 半径 以像素为单位的数字。在高级别上,这控制返回的姿势之间的最小距离。这个值默认为20,这在大多数情况下可能是好的。它应该增加/减少,作为一种过滤不太准确的姿势的方法,但只有在调整姿势信心分数不够好的情况下。

要了解这些参数的效果,最好的方法是使用多姿态估计演示。

让我们回顾一下输出:

  • 输出一系列姿势的Promise

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

此短代码块演示如何使用多姿态估计算法:

const imageScaleFactor = 0.50;
const flipHorizontal = false;
const outputStride = 16;
// get up to 5 poses
const maxPoseDetections = 5;
// minimum confidence of the root part of a pose
const scoreThreshold = 0.5;
// minimum distance in pixels between the root parts of poses
const 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模型,因为它是为在移动设备上运行而设计的。

单姿态估计算法再探讨

处理模型输入:输出步幅(output strides)的解释

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

方便的是,PoseNet模型是图像大小不变的,这意味着无论图像是否缩小,它都可以在与原始图像相同的尺度上预测姿态位置。这意味着PoseNet可以通过在运行时设置上面提到的输出步幅来配置为以牺牲性能为代价具有更高的精度。

输出跨距决定了我们相对于输入图像大小缩小输出的程度。它影响图层的大小和模型输出。输出步长越大,网络层和输出层的分辨率就越小,相应的精度也就越低。在这个实现中,输出跨距的值可以是8、16或32。换句话说,32的输出步幅将导致最快的性能,但精度最低;8的输出步幅将导致最高的精度,但性能最低。我们建议从16开始。

输出跨距决定了我们相对于输入图像大小缩小输出的程度。输出步幅越大,速度越快,但精度越低

在引擎盖下面,当输出跨距设置为8或16时,层中的输入跨距量减少,以创建更大的输出分辨率。然后使用阿托鲁斯卷积使随后层中的卷积滤波器具有更宽的视野(当输出跨距为32时不应用阿托鲁斯卷积)。虽然Tensorflow支持atrus卷积,但Tensorflow.js不支持,因此我们添加了一个PR来包含这个。

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

当PoseNet处理一个图像时,实际上返回的是一个热图偏移向量,这些向量可以被解码以在图像中找到与姿势关键点相对应的高置信度区域。我们将在一分钟内讨论每一个关键点的含义,但现在下面的插图从高层捕捉到每个姿势关键点是如何与一个热图张量和一个偏移向量张量相关联的。

PoseNet返回的17个姿势关键点中的每一个都与一个热图张量和一个用于确定关键点精确位置的偏移向量张量相关联。

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

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

热图

由于17是PoseNet检测到的关键点数量,因此每个热图都是一个大小分辨率x分辨率x 17的3D张量。例如,如果图像大小为225,输出跨距为16,则为15x15x17。三维(共17个)中的每个切片对应于特定关键点的热图。热图中的每个位置都有一个置信分数,这是该关键点类型的一部分存在于该位置的概率。它可以被认为是原始图像被分解成一个15x15的网格,其中热图分数提供了每个网格正方形中每个关键点存在的可能性的分类。

偏移矢量

每个偏移向量是尺寸分辨率x分辨率x 34的3D张量,其中34是关键点的数目*2。图像大小为225,输出跨距为16,这将是15x15x34。由于热图是关键点所在位置的近似值,因此偏移向量对应于热图点的位置,并通过从对应的热图点沿向量移动来预测关键点的准确位置。偏移向量的前17个切片包含向量的x,后17个切片包含向量的y。偏移向量大小与原始图像的比例相同。

根据模型输出估计姿态

在图像通过模型后,我们进行一些计算来估计输出的姿态。例如,单姿态估计算法返回一个姿态置信度得分,它本身包含一个关键点数组(由部件ID索引),每个关键点都有一个置信度得分和x,y位置。要获取姿势的关键点:

  1. 在热图上使用sigmoid激活函数以获得分数。

    scores = heatmap.sigmoid()

  2. argmax2d对关键点信心得分(keypoint confidence scores)进行处理,以获得热图中的x和y指数,每个部分的得分最高,这实际上是该部分最有可能存在的地方。这将产生17x2大小的张量,其中每一行是热图中的y和x索引,每个部分的得分最高。

    heatmapPositions = scores.argmax(y, x)

  3. 通过从对应于该零件的热图中的x和y索引的偏移中获取x和y来检索每个零件的偏移向量。这会产生17x2大小的张量,每一行是对应关键点的偏移向量。例如,对于索引k处的零件,当热图位置为y和d时,偏移矢量为:

    offsetVector = [offsets.get(y, x, k), offsets.get(y, x, 17 + k)]

  4. 为了得到关键点,将每个零件的热图x和y乘以输出跨距,然后将其添加到相应的偏移向量中,该偏移向量的比例与原始图像相同。

    keypointPositions = heatmapPositions \* outputStride + offsetVectors

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

多人姿态估计

多姿态估计算法的细节不在本文的讨论范围之内。该算法的主要区别在于,它使用贪心过程,通过沿着基于零件的图形跟踪位移向量,将关键点分组为姿势。具体来说,它使用了研究论文中的快速贪婪解码算法PersonLab:自下而上、基于部分的几何嵌入模型的人体姿势估计和实例分割.

有关多姿态算法的更多信息,请阅读完整的研究论文或查看代码。我们希望随着更多的模型移植到TensorFlow.js,机器学习的世界对新的程序员和制作者来说变得更容易访问、更受欢迎、更有趣。在TensorFlow.js上的PoseNet只是一个小小的尝试。我们很想看看你做了什么 — 别忘了用#tensorflowjs和#posenet分享你的精彩项目!