利用机器学习,进行人手的21个3D手关节坐标检测

感知手的形状和动作的能力可能是在各种技术领域和平台上改善用户体验的重要组成部分。例如,它可以构成手语理解和手势控制的基础,并且还可以在增强现实中将数字内容和信息覆盖在物理世界之上。虽然自然而然地出现在人们手中,但是强大的实时手感知力无疑是一项具有挑战性的计算机视觉任务,因为手经常相互遮挡自己或彼此(例如手指/手掌遮挡和握手),并且缺乏高对比度模式。

人手识别

MediaPipe Hands是一种高保真手和手指跟踪解决方案。它采用机器学习(ML)来从一个帧中推断出手的21个3D界标。

MediaPipe Hands利用ML管道,该ML管道由多个相互配合的模型组成:

一种手掌检测模型,可在完整图像上运行并返回定向的手边界框。

一个手部界标模型,该模型在由手掌检测器定义的裁剪图像区域上操作并返回高保真3D手部关键点。

将精确裁剪的手部图像提供给手部界标模型可以极大地减少对数据增强(例如旋转,平移和缩放)的需求,并且可以使网络将其大部分功能专用于坐标预测精度。

手掌检测模型

为了检测手的初始位置,设计了模型,该模型针对移动实时使用进行了优化,其方式类似于的人脸检测模型。

通过上述技术,在手掌检测方面达到了95.7%的平均精度。使用规则的交叉熵损失并且没有解码器给出的基线仅为86.22%。

手工地标模型

在整个图像上进行手掌检测之后,我们随后的手界标模型将通过回归(即直接坐标预测)对检测到的手区域内部的21个3D手关节坐标进行精确的关键点定位。该模型可学习一致的内部手部姿势表示,甚至对于部分可见的手部和自我遮挡也具有鲁棒性。

其检测的21个点的坐标位置如下:

21个3D手关节坐标

import cv2
import mediapipe as mp
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
drawing_spec = mp_drawing.DrawingSpec(thickness=2, circle_radius=1)
drawing_spec1 = mp_drawing.DrawingSpec(thickness=2, circle_radius=1,color=(255,255,255))

hands = mp_hands.Hands(
static_image_mode=True,
max_num_hands=2,
min_detection_confidence=0.5)

代码截图

首先我们初始化mp_drawing.DrawingSpec参数,其参数主要是配置我们检测到的坐标时的绘制圆点的大小尺寸以及直线的粗细颜色等参数,在我们介绍人脸468点3D坐标检测以及holistic人脸与手与人体姿态同步检测时都有详细介绍,这里不再介绍此函数的主要参数

然后使用mp_hands.Hands函数建立一个人手检测模型,其函数跟人脸468点3D检测以及holistic检测一样,函数接受4个参数:

STATIC_IMAGE_MODE:

如果设置为false,则解决方案会将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的地标。在随后的图像中,一旦检测到所有手并且定位了相应的手地标,它便会简单地跟踪这些地标,而无需调用另一次检测,直到失去对任何手的跟踪为止。这减少了等待时间,是处理视频帧的理想选择。如果设置为true,则手部检测将在每个输入图像上运行,非常适合处理一批静态的,可能不相关的图像。默认为false。

MAX_NUM_HANDS:

要检测的最大手数。默认为2。

MIN_DETECTION_CONFIDENCE:

[0.0, 1.0]来自手部检测模型的最小置信度值()被认为是成功的检测。默认为0.5。

MIN_TRACKING_CONFIDENCE:

[0.0, 1.0]来自地标跟踪模型的最小置信度值()将被视为已成功跟踪的手部地标,否则将在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的健壮性,但代价是更高的延迟。如果为true,则忽略操作,其中手检测仅在每个图像上运行。默认为0.5。

MULTI_HAND_LANDMARKS:

检测/跟踪的手,其中每个手被表示为21层的手的地标列表以及每个界标由收集x,y和z。x和y分别[0.0, 1.0]通过图像的宽度和高度进行归一化。z代表地标深度,以手腕处的深度为原点,值越小,地标越靠近相机。的z使用量级与大致相同x。

多手:

被检测/被追踪的手的手性的集合(即它是左手还是右手)。每只手由label和组成score。label是”Left”或”Right”。score是预计左右手的估计概率,并且始终大于或等于0.5

由于要检测图片,我们这里STATIC_IMAGE_MODE设置为true,然后我们导入一张照片进行人手的检测

file = 'images/right-frontal.jpg'
image = cv2.imread(file)
image = cv2.flip(image, 1)
results = hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
print('Handedness:', results.multi_handedness)
image_hight, image_width, _ = image.shape
annotated_image = image.copy()

代码截图

我们对图片进行flip翻转,以便增强图片数据,然后转换图片到RGB颜色空间,最后便可以使用人手检测函数进行图片的人手检测,检测结果保存在results中,其会保存一个人手坐标list以及左右手的标签以及标签的置信度等,我们可以使用print函数打印其检测结果

for hand_landmarks in results.multi_hand_landmarks:
  print('hand_landmarks:', hand_landmarks)
  print(
  f'Index finger tip coordinates: (',
  f'{hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].x * image_width}, '
  f'{hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y * image_hight})'
  )
  mp_drawing.draw_landmarks(
  annotated_image,
  hand_landmarks,
  mp_hands.HAND_CONNECTIONS,
  landmark_drawing_spec=drawing_spec,
  connection_drawing_spec=drawing_spec1)

代码截图

检测完成后,我们使用for循环遍历检测的结果(主要是遍历有多少人手,其参数可以使用max_num_hands来定义)

并使用mp_drawing.draw_landmarks函数把人手检测21点坐标标注,以及把21点坐标连接起来

annotated_image = cv2.flip(annotated_image, 1)
cv2.imshow('annotated_image',annotated_image)
cv2.waitKey(0)
cv2.imwrite( 'images/hand.png', annotated_image)
hands.close()

代码截图

最后,我们显示我们检测的结果,并把处理完成的图片保存下来,方便查看

人手21点检测

人手视频实时检测

介绍了图片人手检测,我们接下来看看如何在视频中实时进行人手的检测

import cv2
import mediapipe as mp
import time
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
drawing_spec = mp_drawing.DrawingSpec(thickness=2, circle_radius=1)
drawing_spec1 = mp_drawing.DrawingSpec(thickness=2, circle_radius=1,color=(255,255,255))

hands = mp_hands.Hands(
min_detection_confidence=0.5, min_tracking_confidence=0.5)
cap = cv2.VideoCapture(0)
time.sleep(2)

代码截图

首先我们按照图片检测一样,定义一个人手检测模型,并设置mp_drawing.DrawingSpec函数对检测结果进行标注,由于这里需要实时进行视频检测,static_image_mode参数默认即可,此处省略

最后我们利用cv2.VideoCapture(0)函数打开电脑的默认摄像头,当然这里也可以传入一个视频的路径,当视频摄像头打开后,我们实时进行图片的检测,并实时显示

while cap.isOpened():
  success, image = cap.read()
  if not success:
    print("Ignoring empty camera frame.")
    continue
  image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
  image.flags.writeable = False
  results = hands.process(image)
  image.flags.writeable = True
  image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

代码截图

当打开摄像头后,我们从视频帧中提取实时的图片,并转换图片到RGB颜色空间,然后进行图片的的人手检测,检测完成后,我们再转换图片到BGR空间,以方便我们的图片保持一致,检测完成后,我们便可以遍历检测到的人手数据,进行实时的图片的标注

if results.multi_hand_landmarks:
  for hand_landmarks in results.multi_hand_landmarks:
    mp_drawing.draw_landmarks(
    image, hand_landmarks, mp_hands.HAND_CONNECTIONS,
    landmark_drawing_spec=drawing_spec,
    connection_drawing_spec=drawing_spec1)
  cv2.imshow('MediaPipe Hands', image)
  if cv2.waitKey(5) & 0xFF == ord('q'):
    break
hands.close()
cap.release()

代码截图

检测完成后,我们遍历检测结果,并标注到原始的图片上,最后使用imshow函数,实时显示我们检测到的结果

人手21点检测

https://m.toutiao.com/is/iLjn9d26/ 人工智能研究所: 视频动画详解Transformer模型–Attention is all you need.