import mediapipe as mp
import cv2
import time

gestures = ['None', 'rock', 'paper', 'scissors']
gestures_text = {'Rock', 'Paper', 'Scissors'}
user_gesture = None
computer_gesture = None
cap = cv2.VideoCapture(0)
mpHands = mp.solutions.hands  # 选择策略 -- 监测手的模型
mpDraw = mp.solutions.drawing_utils
handLmsStyle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 设定点的样式  颜色 粗细,半径
handConStyle = mpDraw.DrawingSpec(color=(0, 255, 2), thickness=10)  # 设定线的样式  颜色 粗细,半径
pTime = 0
cTime = 0
hands = mpHands.Hands(static_image_mode=False,  # 影片 false  图片 True
                      max_num_hands=2,  # 最大手识别数量
                      model_complexity=1,  # 模型的复杂度 只能是 0 1 越高越准
                      min_detection_confidence=0.9,  # 识别的精准度
                      min_tracking_confidence=0.5)  # 追踪的严谨度


def get_gesture(Lms):
    # 获取手指的位置
    p0_x = Lms.landmark[0].x
    p0_y = Lms.landmark[0].y

    p5_x = Lms.landmark[5].x
    p5_y = Lms.landmark[5].y

    p8_x = Lms.landmark[8].x
    p8_y = Lms.landmark[8].y
    p9_x = Lms.landmark[9].x
    p9_y = Lms.landmark[9].y

    p12_x = Lms.landmark[12].x
    p12_y = Lms.landmark[12].y
    p13_x = Lms.landmark[13].x
    p13_y = Lms.landmark[13].y

    p16_x = Lms.landmark[16].x
    p16_y = Lms.landmark[16].y
    p17_x = Lms.landmark[17].x
    p17_y = Lms.landmark[17].y

    p20_x = Lms.landmark[20].x
    p20_y = Lms.landmark[20].y

    # 通过两点距离公式,计算出两个点之间的距离
    distance_0_5 = ((p0_x - p5_x) ** 2 + (p0_y - p5_y) ** 2) ** 0.5
    distance_0_8 = ((p0_x - p8_x) ** 2 + (p0_y - p8_y) ** 2) ** 0.5
    distance_0_9 = ((p0_x - p9_x) ** 2 + (p0_y - p9_y) ** 2) ** 0.5
    distance_0_12 = ((p0_x - p12_x) ** 2 + (p0_y - p12_y) ** 2) ** 0.5
    distance_0_13 = ((p0_x - p13_x) ** 2 + (p0_y - p13_y) ** 2) ** 0.5
    distance_0_16 = ((p0_x - p16_x) ** 2 + (p0_y - p16_y) ** 2) ** 0.5
    distance_0_17 = ((p0_x - p17_x) ** 2 + (p0_y - p17_y) ** 2) ** 0.5
    distance_0_20 = ((p0_x - p20_x) ** 2 + (p0_y - p20_y) ** 2) ** 0.5
    # 通过距离并不能准确地判断手指是否屈伸,因为手可以前后移动,在屏幕中手的大小会改变
    # 所以通过从手的0点到手指的距离与食指与手掌的距离的比值进行判断,这样就可根据一个固定值来判断时候手指屈伸
    # 食指
    base_1 = distance_0_5 / 0.8
    # 中指
    base_2 = distance_0_9 / 0.8
    # 无名指
    base_3 = distance_0_13 / 0.8
    # 小指
    base_4 = distance_0_17 / 0.8

    if distance_0_8 > base_1 and distance_0_12 > base_2:
        if distance_0_16 > base_3 and distance_0_20 > base_4:
            user_gesture = 2
        elif distance_0_16 < base_3 and distance_0_20 < base_4:
            user_gesture = 3
        else:
            user_gesture = 0
    elif distance_0_8 < base_1 and distance_0_12 < base_2:
        if distance_0_16 < base_3 and distance_0_20 < base_4:
            user_gesture = 1
        else:
            user_gesture = 0
    else:
        user_gesture = 0
    return user_gesture


while True:
    ret, img = cap.read()  # 读取到的图片都是bgr的图片 ,故需要将其转换为RGB的图片

    if ret:
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        result = hands.process(imgRGB)  # 侦测
        # print(result.multi_hand_landmarks)  结果为 三个坐标 XYZ
        imgHeight = img.shape[0]  # 获取视窗的高度
        imgWidth = img.shape[1]  # 获取视窗的宽度
        if result.multi_hand_landmarks:
            for handLms in result.multi_hand_landmarks:  # 画出所有点和线
                mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStyle, handConStyle)
                gesture1 = get_gesture(handLms)
                cv2.putText(img, 'you: ' + gestures[gesture1], (int(0.1 * imgWidth), int(0.5 * imgHeight)),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.4, (50, 150, 50), 1)
                # enumerate函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,常用于for循环中
                for i, lm in enumerate(handLms.landmark):
                    xPos = int(lm.x * imgHeight)
                    yPos = int(lm.y * imgWidth)
                    cv2.putText(img, str(i), (xPos - 1, yPos + 1), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 200, 50), 1)
                    print(i, xPos, yPos)

        cTime = time.time()
        fps = 1 / (cTime - pTime)
        pTime = cTime
        cv2.putText(img, f'FPS : {int(fps)}', (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
        cv2.imshow('img', img)
    if cv2.waitKey(1) == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

# 检测到几只手内容:可以通过result.multi_hand_landmarks来获取检测到的手的数量,它返回一个列表,列表中的每个元素都是一个HandLandmark对象,表示检测到的一只手的21个关键点信息。因此,列表的长度就是检测到的手的数量。可以使用len(result.multi_hand_landmarks)来获取检测到的手的数量。
手势识别:使用 MediaPipe 和 OpenCV 识别摇滚、剪刀、布

原文地址: https://www.cveoy.top/t/topic/op4q 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录