OpenCV项目实战——基于MediaPipe的手部关键点检测

MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架,可以直接调用其API完成目标检测、人脸检测以及关键点检测等。本篇文章介绍其手部21个关键点检测。
OpenCV项目实战——基于MediaPipe的手部关键点检测_第1张图片
官方文档:https://google.github.io/mediapipe/solutions/hands.html

一. 关键函数解释

(1)mediapipe.solutions.hands.Hands(): 手部关键点检测方法

参数:

  • static_image_mode: 默认为 False,将输入图像视为视频流。它将尝试在第一个输入图像中检测手,并在成功检测后进一步定位手的坐标。在随后的图像中,一旦检测到所有 max_num_hands 手并定位了相应的手的坐标,它就会跟踪这些坐标,而不会调用另一个检测,直到它失去对任何一只手的跟踪。这减少了延迟,非常适合处理视频帧。如果设置为 True,则在每个输入图像上运行手部检测,用于处理一批静态的、可能不相关的图像。
  • max_num_hands: 最多检测几只手,默认为2
  • min_detection_confidence: 手部检测模型的最小置信值(0-1之间),超过阈值则检测成功。默认为 0.5
  • min_tracking_confidence: 坐标跟踪模型的最小置信值 (0-1之间),用于将手部坐标视为成功跟踪,不成功则在下一个输入图像上自动调用手部检测。将其设置为更高的值可以提高解决方案的稳健性,但代价是更高的延迟。如果 static_image_mode 为真,则忽略这个参数,手部检测将在每个图像上运行,默认为 0.5

返回值:

  • MULTI_HAND_LANDMARKS: 被检测/跟踪的手的集合,其中每只手被表示为21个手部地标的列表,每个地标由x, y, z组成。x和y分别由图像的宽度和高度归一化为[0,1]。Z表示地标深度。
  • MULTI_HANDEDNESS: 被检测/追踪的手是左手还是右手的集合。每只手由label(标签)和score(分数)组成。 label 是 ‘Left’ 或 ‘Right’ 值的字符串。 score 是预测左右手的估计概率。

(2)mediapipe.solutions.drawing_utils.draw_landmarks(): 绘制手部关键点的连线

参数:

  • image: 需要画图的原始图片

  • landmark_list: 检测到的手部关键点坐标

  • connections: 连接线,需要把那些坐标连接起来

  • landmark_drawing_spec: 坐标的颜色,粗细

  • connection_drawing_spec: 连接线的粗细,颜色等

二. 手部关键点检测并显示伸出的手指数

import cv2
import mediapipe as mp
import cmath

gesture = ["none","one","two","three","four","five","six","seven","eight","nine","ten"]
flag = 0

mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands

hands = mp_hands.Hands(
        static_image_mode=False,
        max_num_hands=2,
        min_detection_confidence=0.75,
        min_tracking_confidence=0.75)

cap = cv2.VideoCapture(0)
while True:
    flag = 0
    ret,frame = cap.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    # 因为摄像头是镜像的,所以将摄像头水平翻转
    # 不是镜像的可以不翻转
    frame= cv2.flip(frame,1)
    results = hands.process(frame)
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
    
#    if results.multi_handedness:
#        for hand_label in results.multi_handedness:
#            print(hand_label)
            
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            #print('hand_landmarks:', hand_landmarks)
            #计算关键点的距离,用于判断手指是否伸直
            p0_x = hand_landmarks.landmark[0].x
            p0_y = hand_landmarks.landmark[0].y
            p5_x = hand_landmarks.landmark[5].x
            p5_y = hand_landmarks.landmark[5].y
            distance_0_5 = pow(p0_x-p5_x,2)+pow(p0_y-p5_y,2)
            base = distance_0_5 / 0.6
            
            p4_x = hand_landmarks.landmark[4].x
            p4_y = hand_landmarks.landmark[4].y
            distance_5_4 = pow(p5_x-p4_x,2)+pow(p5_y-p4_y,2)
            
            p8_x = hand_landmarks.landmark[8].x
            p8_y = hand_landmarks.landmark[8].y
            distance_0_8 = pow(p0_x-p8_x,2)+pow(p0_y-p8_y,2)
            
            p12_x = hand_landmarks.landmark[12].x
            p12_y = hand_landmarks.landmark[12].y
            distance_0_12 = pow(p0_x-p12_x,2)+pow(p0_y-p12_y,2)
            
            p16_x = hand_landmarks.landmark[16].x
            p16_y = hand_landmarks.landmark[16].y
            distance_0_16 = pow(p0_x-p16_x,2)+pow(p0_y-p16_y,2)
            
            p20_x = hand_landmarks.landmark[20].x
            p20_y = hand_landmarks.landmark[20].y
            distance_0_20 = pow(p0_x-p20_x,2)+pow(p0_y-p20_y,2)
            
            
            if distance_0_8 > base:
                flag += 1
            if distance_0_12 > base:
                flag += 1
            if distance_0_16 > base:
                flag += 1
            if distance_0_20 > base:
                flag += 1
            if distance_5_4 > base*0.3:
                flag += 1
            if flag>=10:
                flag = 10
            # 关键点可视化
            mp_drawing.draw_landmarks(frame, 
                                      hand_landmarks, 
                                      mp_hands.HAND_CONNECTIONS)
            
          
    cv2.putText(frame,gesture[flag],(50,50),0,1.3,(0,0,255),3)
    cv2.imshow('MediaPipe Hands', frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break
cap.release()
cv2.destroyAllWindows()

你可能感兴趣的:(OpenCV,opencv,目标跟踪,计算机视觉)