人体姿态识别方案详解

Posted jieqiang3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了人体姿态识别方案详解相关的知识,希望对你有一定的参考价值。

文章目录

姿态迁移简介

目前AR,VR,元宇宙都比较火,需要实际场景和虚拟中进行交互的情况,因此研究了通过摄像头获取图像进行识别,本文主要概述了在人体身体姿势识别跟踪方面的一些调研和尝试。

通过各个方案,我们可以从RGB视频帧中推断出整个身体的关键特征点,从而根据这些关键特征点去做扩展,比如迁移到unity模型中等。

从识别角度来说,我们可以分成两个大方向,一是人体身体关键特征点识别,这里特征点分为2d特征点和3d特征点,部分方案只支持2d特征点;二是人体动作识别,比如用户在做什么动作,举一个很简单的例子,我们可以通过mediapipe识别出用户在做俯卧撑或者深蹲等。

从用户体验角度来说,我们可以分成实时摄像头传输和对视频进行处理两个方向。摄像头实时传输的话就必须要做到对视频每一帧RGB图像做到即时处理,这里就牵扯到优化效率问题。对视频进行处理的话可以采用对视频后期处理的形式去处理,一般动捕方案都是这么做的。

方案详解

Mediapipe

Google出品,可以实现人脸检测、姿态识别,手势识别等很多效果,并且可以在多平台高效的输出。这里强调下Mediapipe检测出来的特征点数据均为3d,有空间感。

Mediapipe检测是基于BlazeFace模型,明确地预测了两个额外的虚拟关键点,这些虚拟关键点将人体中心、旋转和缩放牢固地描述为一个圆圈。受莱昂纳多的维特鲁威人的启发,我们预测了一个人臀部的中点,外接整个人的圆的半径,以及连接肩部和臀部中点的线的倾斜角。

Mediapipe数据获取

从Mediapipe上获取的身体的33个特征点,具体如下图,对此33个特征点进行判断。

主要通过角度来判断:

float Angle(NormalizedLandmark ver1, NormalizedLandmark ver2, NormalizedLandmark ver3, NormalizedLandmark ver4)
 
	return Vector3.Angle(new Vector3(ver1.X, ver1.Y, ver1.Z) - new Vector3(ver2.X, ver2.Y, ver2.Z),
                new Vector3(ver3.X, ver3.Y, ver3.Z) - new Vector3(ver4.X, ver4.Y, ver4.Z));

多人姿态识别方向探索

Mediapipe 在人脸上是支持多人的,但是在姿态识别上目前只支持单人。在实验了网上能搜到的各种方案之后,有一种方案目前是可行的,但是在性能上会比较卡顿。
既然Mediapipe支持单人,那我们把视频画面中的多人画面拆分成多个单人就行了。
这里我采用的是OpenVINO中的行人检测模型。(我尝试了多种方案,YOLO-unity、Barracuda-Image-Classification和OpenVINO后发现OpenVINO效果最佳)OpenVINO ToolKit是英特尔发布的一套深度学习推断引擎,支持各种网络框架。对此不熟悉的同学可以参考OpenVINO开发系列文章汇总进行较为系统的学习。

具体思路就是通过OpenVINO识别出人物范围框,然后使用Opencv进行图像分割,单人图像传递给Mediapipe sdk去做单人姿态识别,然后进行汇总。目前该方案在移动端测试效果较为卡顿,不是很理想。

PoseNet

PoseNet是TensorFlow和谷歌创意实验室合作发布的专门用于姿态估计的一种技术方案。PoseNet可用于估计单个姿势或多个姿势,该算法有一个版本可以仅检测图像/视频中的一个人,另一个版本可以检测图像/视频中的多个人。
流程主要有两个阶段:

  1. 输入RGB 图像通过卷积神经网络馈送。
  2. 单姿势或多姿势解码算法用于解码来自模型输出的姿势、姿势置信度分数、关键点位置 和关键点置信度分数。

同Mediapipe一样,我们来看看PoseNet给到我们的关键姿势点。PoseNet提供了17个关键点,不同于Mediapipe提供的3d数据点,PoseNet提供的是关键点的2d坐标,x和y。以及关键点可信度分数,使用者可以根据实际情况去做判断,范围在0.0-1.0之间,越接近1.0表示识别出来的点越正确。

ok,我们来看下我把posenet这一套通过tf-lite加载的形式在unity上的呈现效果。

MoveNet

MoveNet是一种超快速且准确的模型,可检测身体的 17 个关键点。该模型在TF Hub上提供,有两种变体,称为 Lightning 和 Thunder。Lightning 适用于延迟关键的应用程序,而 Thunder 适用于需要高精度的应用程序。在大多数现代台式机、笔记本电脑和手机上,这两种模型的运行速度都比实时 (30+ FPS) 快,这对于现场健身、健康和保健应用至关重要。

我们可以看到movenet的识别出来的点的效果如下:

官方从tf hub 加载模型代码:

model_name = "movenet_lightning"

if "tflite" in model_name:
  if "movenet_lightning_f16" in model_name:
    !wget -q -O model.tflite https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/float16/4?lite-format=tflite
    input_size = 192
  elif "movenet_thunder_f16" in model_name:
    !wget -q -O model.tflite https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/float16/4?lite-format=tflite
    input_size = 256
  elif "movenet_lightning_int8" in model_name:
    !wget -q -O model.tflite https://tfhub.dev/google/lite-model/movenet/singlepose/lightning/tflite/int8/4?lite-format=tflite
    input_size = 192
  elif "movenet_thunder_int8" in model_name:
    !wget -q -O model.tflite https://tfhub.dev/google/lite-model/movenet/singlepose/thunder/tflite/int8/4?lite-format=tflite
    input_size = 256
  else:
    raise ValueError("Unsupported model name: %s" % model_name)

  # Initialize the TFLite interpreter
  interpreter = tf.lite.Interpreter(model_path="model.tflite")
  interpreter.allocate_tensors()

  def movenet(input_image):
    """Runs detection on an input image.

    Args:
      input_image: A [1, height, width, 3] tensor represents the input image
        pixels. Note that the height/width should already be resized and match the
        expected input resolution of the model before passing into this function.

    Returns:
      A [1, 1, 17, 3] float numpy array representing the predicted keypoint
      coordinates and scores.
    """
    # TF Lite format expects tensor type of uint8.
    input_image = tf.cast(input_image, dtype=tf.uint8)
    input_details = interpreter.get_input_details()
    output_details = interpreter.get_output_details()
    interpreter.set_tensor(input_details[0]['index'], input_image.numpy())
    # Invoke inference.
    interpreter.invoke()
    # Get the model prediction.
    keypoints_with_scores = interpreter.get_tensor(output_details[0]['index'])
    return keypoints_with_scores

else:
  if "movenet_lightning" in model_name:
    module = hub.load("https://tfhub.dev/google/movenet/singlepose/lightning/4")
    input_size = 192
  elif "movenet_thunder" in model_name:
    module = hub.load("https://tfhub.dev/google/movenet/singlepose/thunder/4")
    input_size = 256
  else:
    raise ValueError("Unsupported model name: %s" % model_name)

  def movenet(input_image):
    """Runs detection on an input image.

    Args:
      input_image: A [1, height, width, 3] tensor represents the input image
        pixels. Note that the height/width should already be resized and match the
        expected input resolution of the model before passing into this function.

    Returns:
      A [1, 1, 17, 3] float numpy array representing the predicted keypoint
      coordinates and scores.
    """
    model = module.signatures['serving_default']

    # SavedModel format expects tensor type of int32.
    input_image = tf.cast(input_image, dtype=tf.int32)
    # Run model inference.
    outputs = model(input_image)
    # Output is a [1, 1, 17, 3] tensor.
    keypoints_with_scores = outputs['output_0'].numpy()
    return keypoints_with_scores

实际生产过程中,movenet主要通过tf-lite去加载计算,可以分为单人和多人。tf-hub地址如下:地址。测试发现,从帧率和效果上来说,movenet比mediapipe效果要好一点。
但是在unity使用中,我发现movenet的tf-lite模型在unity中遇到了unity barracuda插件转换的问题,单人的tf-lite可用,但是多人无法转换。已知是unity barracuda插件问题,已提交issues,但目前还没走通。

不熟悉unity barracuda的同学可以移步这里
主要用于在untiy中集成神经网络算法。

OpenPose

OpenPose是我在github上搜到的一个人体姿态识别的一个算法方案。
主要功能:

  1. 2D实时多人关键点检测:15、18 或25 关键点身体/足部关键点估计,包括6 个足部关键点。运行时不受检测到的人数的影响。2x21-keypoint 手部关键点估计。运行时间取决于检测到的人数。70个关键点人脸关键点估计。运行时间取决于检测到的人数。
  2. 3D实时单人关键点检测

其实可以理解成在效果产出和姿态识别角度来说,是mediapipe和posenet的综合体。

效果如下:

OpenMMD

OpenMMD是一个可以直接分析现成视频(各种MP4, AVI等视频格式),自动生成vmd动作文件的工具。
这个方案无法做到实时转换,即使你的输入源是摄像头的话,也必须录完以后生成动捕文件,然后再把动捕文件绑定到模型上才可以完成。
OpenMMD具体使用教程在b站上有一个大佬总结的很清楚了,但有一个问题,他只能绑定在mmd模型上,mmd模型在游戏里的通用性并不大,属于比较小范围的应用。尝试了几种模型之间互转的方案,Blender等,但无果,转换起来没那么简单。
这是我迁移后的效果,左边是mmd模型,成功跑通。

总结

以上五种方案,简单总结如下:

参考链接

  1. BlazeFace
  2. Mediapipe Pose
  3. PoseNet
  4. MoveNet
  5. OpenPose
  6. OpenMMD

个人微信公共账号已上线,欢迎关注:

二维已经 OUT 了?3DPose 实现三维人体姿态识别真香 | 代码干货

作者|李秋键

出品|AI科技大本营(ID:rgznai100)

引言

人体姿态估计是计算机视觉领域很多研究工作的基础,也是研究的热点问题,在行为识别、人机交互、姿态跟踪等领域有着广泛的应用前景。

按照人体姿态维度的差异,可以将人体姿态估计任务分为二维人体姿态估计和三维人体姿态估计。2D人体姿态估计的目标是定位并识别出人体关键点,将这些关键点按照关节顺序相连形成在图像二维平面的投影,从而得到人体骨架。3D人体姿态估计的主要任务是预测出人体关节点的三维坐标位置和角度等信息。

在实际应用中,由于3D姿态估计在2D姿态估计的基础上加入了深度信息,其对于人体姿态的表述比2D更为精准,因此其应用范围和研究价值都要高于2D人体姿态估计,但是3D姿态估计的难度也更高,存在着遮挡,单视角2D到3D的映射中固有的深度模糊性、不适定性,缺少大型的室外数据集等挑战。

本项目通过使用3DPose模型实现对人体的三维实时姿态识别。其最终实现的效果如下图可见:

1、基本介绍

在深度学习方法得到广泛应用之前,3D人体姿态标注数据集和具有高运算能力的GPU还没有普及,研究人员主要通过一些应用在传统计算机视觉或机器学习领域的方法来进行3D人体姿态的估计。传统三维人体姿态估计和基于深度学习的姿态估计之间最明显的特征在于是否使用了多层神经网络的学习方法,因为建模方式不同,在估计精确性、计算复杂度等方面也有着较大的差别。其中建模是三维人体姿态估计一个很重要的方面,目的是表示从输入数据中提取的关键点和特征。在解决实际问题时由于实验个体所处环境的复杂性,很大程度上增加了模型的建立难度,因此选取适当且有效的图像特征来简化模型建立过程十分重要。

1.1 环境要求:

本次环境使用的是python3.6.5+windows平台。

主要用的库有:ONNX Runtime库、opencv-python模块、Numpy模块。

ONNX Runtime库

ONNX Runtime库是一个用于ONNX(Open Neural Network Exchange)模型推理的引擎。微软联合Facebook等在2017年搞了个深度学习以及机器学习模型的格式标准--ONNX,顺路提供了一个专门用于ONNX模型推理的引擎。

opencv-python模块

opencv-python是一个Python绑定库,旨在解决计算机视觉问题。其使用Numpy,这是一个高度优化的数据库操作库,具有MATLAB风格的语法。所有Opencv数组结构都转换为Numpy数组。这也使得与使用Numpy的其他库(如Scipy和Matplotlib)集成更容易。

Numpy模块

Numpy是应用Python进行科学计算时的基础模块。它是一个提供多维数组对象的Python库,除此之外,还包含了多种衍生的对象(比如掩码式数组(masked arrays)或矩阵)以及一系列的为快速计算数组而生的例程,包括数学运算,逻辑运算,形状操作,排序,选择,I/O,离散傅里叶变换,基本线性代数,基本统计运算,随机模拟等。

1.2 方法总结:

传统方法很多是采用基于人体模型的方法来描述和推断人体姿态,通过算法提取图像姿态特征,因此对特征表示和关键点的空间位置关系这两个维度有比较高的要求,除去边界、颜色这类低层次特征,典型的有尺度不变特征变换、梯度直方图等表达能力更强、可有效压缩特征空间维度的高层次特征,它们虽然在时间效率方面具有优势,但依然存在着较大的不足。

而深度学习模型操作过程相对简单且对特征的表示能力强大,对输入信息自动进行特征提取而无需人为手动提取特征。

基于深度学习的人体姿态估计可以通过建立网络模型在图像数据上进行训练和学习,直接得到最有效的表征方法,其核心是深度神经网络,主要是利用卷积神经网络从图像中提取出比人工特征语义信息更丰富、准确性更高和更具鲁棒性的卷积特征,并且网络模型的表达能力会因网络堆叠数量的增加而呈指数增长,因此相较于传统方法可以进一步提升复杂环境下的人体姿态的精度和鲁棒性。深度学习在人体姿态估计任务中的应用已经取得了显著的进展,然而像遮挡、训练数据不足和深度模糊等挑战仍然是难以克服的。

2.三维人体可视化

传统方法与深度学习方法

其中3DPose算法提供的模型架构如下图可见:

2.1 图片预处理:

其中图片处理代码如下,分别为读取图片,显示图片,BGR转RGB以及尺寸变换等:

img=cv2.imread("1.jpg")
cv2.imshow("test", img)
cv2.waitKey(1)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (448, 448))
img = img.astype(np.float32) / 255.0
img = img.transpose(2, 1, 0)
img = img[np.newaxis, ...]

2.2 模型训练:

首先通过使用onnxruntime,

然后读取Resnet34_inputs_448x448_20200609.0nnx模型文件,实时需要识别的图片数据,获取每一张图片的offset图和heatmap图。通过找到第j个关节的28个特征图,并找到最大值的索引来获取个点坐标。并把坐标按照一定比例缩放。使得图像变形较为符合人体规律。

for j in range(0, 24):
    # 找到第j个关节的28个特征图,并找到最大值的索引
    joint_heat = heatMap3D[j * 28:(j + 1) * 28, ...]
    if np.max(joint_heat)>0.1:
        print(np.max(joint_heat))
        [x, y, z] = np.where(joint_heat == np.max(joint_heat))
        x = int(x[-1])
        y = int(y[-1])
        z = int(z[-1])
        # 通过heatmap的索引找到对应的offset图,并计算3D坐标的xyz值
        pos_x = offset3D[j * 28 + x, y, z] + x
        pos_y = offset3D[24 * 28 + j * 28 + x, y, z] + y
        pos_z = offset3D[24 * 28 * 2 + j * 28 + x, y, z] + z

        kps[j, 0] = pos_x
        kps[j, 1] = pos_y
        kps[j, 2] = pos_z
    else:
        try:
            kps[j, 0] = kps[j-1, 0]
            kps[j, 0] = kps[j-1, 0]
            kps[j, 2] = kps[j-1, 2]
        except:
            pass

parent = np.array([15, 1, 2, 3, 3, 15, 6, 7, 8, 8, 12, 15, 14, 15, 24, 24, 16, 17, 18, 24, 20, 21,   22, 0]) - 1;
    for i in range(len(kps)):
        if (parent[i] != -1):
            ax.plot3D(kps[[i, parent[i]], 0], -kps[[i, parent[i]], 1], -kps[[i, parent[i]], 2], 'gray')

完整代码:

链接:https://pan.baidu.com/s/1pb0uG9Uy36sOBuWNTsZUpA

提取码:0ahr

作者简介:李秋键,CSDN博客专家,CSDN达人课作者。硕士在读于中国矿业大学,开发有taptap竞赛获奖等。

在评论区留言你的看法

AI科技大本营将选出三名优质留言

携手【北京大学出版社】送出

《人工智能数学基础》一本

截至7月19日14:00点

更多精彩推荐
深度学习三巨头共同发文,聊聊深度学习的过去、现在与未来

基于 Python 的 8 种常用抽样方法

GitHub 遭抵制!AI 代码生成神器竟成“抄袭工具”?

以上是关于人体姿态识别方案详解的主要内容,如果未能解决你的问题,请参考以下文章

基于Mediapipe与Unity的人体姿态捕捉系统

AR人体姿态识别,实现无边界的人机交互

AR人体姿态识别,实现无边界的人机交互

二维已经 OUT 了?3DPose 实现三维人体姿态识别真香 | 代码干货

基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统

Mediapipe的Unity实现