基于mediapipe实现的挥手识别及手势识别(Linux系统下安装)

一、mediapipe介绍

mediapipe集成了手部21个关键点和人体33个关键点的识别等功能,其具有以下优点:

端到端加速:内置快速 ML 推理和处理加速,即使在普通硬件上也是如此

一次构建,随处部署:统一解决方案适用于 Android、iOS、桌面/云、Web 和 IoT

即用型解决方案:展示框架全部功能的尖端 ML 解决方案

免费和开源: Apache 2.0 下的框架和解决方案,完全可扩展和可定制

二、mediapipe具有c++API和pythonAPI,如何选择?

        若使用C++API下的mediapipe,如果利用bazel编译出可执行程序,对于已经利用qmake、cmake构建的外部应用来说需要启用多进程来使用mediapipe可执行程序,除非利用bazel重新编译,然而bazel需要在线联网,国内使用较为困难,常常出现无法连接的问题。

        如果打算在外部程序中调用mediapipe,方案①是将利用mediapipe开发好的各项功能封装成动态链接库,供程序直接调用动态链接库实现多种功能,优点是可以嵌入到已开发的c/cpp程序中使用。缺点是相对于使用pyAPI下的mediapipe,如果想要简单验证方案是否可行时开发时间更长,维护难度更高,而且须重新编译,不利于效果的快速验证。方案②则是利用python.h库在c++程序中通过python解释器运行py脚本,从而进行一定的功能验证,当功能能够满足要求时,再使用bazel编译成动态链接库添加至项目中。

三、python环境中安装opencv和mediapipe

①首先安装运行环境:

pip install opencv-contrib-python

sudo apt-get install -y libatlas-base-dev libhdf5-dev libhdf5-serial-dev libatlas-base-dev libjasper-dev libqtgui4 libqt4-test libwebp-dev

②接着安装这两个库,很简单,直接使用pip进行安装即可

pip install opencv-python
pip install mediapipe

三、功能实现

这里直接贴上手势识别的程序,如果需要进行挥手识别,调用handwave_recognize传入两个具有时间差的关键点列表即可

import cv2
import mediapipe as mp
import math
import time


def points_cos_angle(point1, point2):
    # 计算两个坐标点的余弦值
    try:
        angle_ = math.degrees(math.acos(
            (point1[0] * point2[0] + point1[1] * point2[1]) / (
                    ((point1[0] ** 2 + point1[1] ** 2) * (point2[0] ** 2 + point2[1] ** 2)) ** 0.5)))
        # math.acos返回一个数的反余弦值(单位为弧度)此处为向量a、b之积(x1*x2+y1*y2)除以向量a、b模的积;math.degrees将弧度值转换为角度
    except:
        angle_ = 65535.  # 将未检测到角度时(数据溢出)的情况排除,容错处理
    if angle_ > 180.:
        angle_ = 65535.
    return angle_


def get_fingers_angle(handPoints_list):
    # 利用mediapipe的手部关键点数组构建相关向量并传入函数中计算两个二维向量之间的角度,最后将结果置入列表中
    angle_list = []
    # ---------------------------- thumb 大拇指角度
    angle_ = points_cos_angle(
        ((int(handPoints_list[0][0]) - int(handPoints_list[2][0])),
         (int(handPoints_list[0][1]) - int(handPoints_list[2][1]))),
        ((int(handPoints_list[3][0]) - int(handPoints_list[4][0])),
         (int(handPoints_list[3][1]) - int(handPoints_list[4][1])))
    )
    angle_list.append(angle_)
    # ---------------------------- index 食指角度
    angle_ = points_cos_angle(
        ((int(handPoints_list[0][0]) - int(handPoints_list[6][0])),
         (int(handPoints_list[0][1]) - int(handPoints_list[6][1]))),
        ((int(handPoints_list[7][0]) - int(handPoints_list[8][0])),
         (int(handPoints_list[7][1]) - int(handPoints_list[8][1])))
    )
    angle_list.append(angle_)
    # ---------------------------- middle 中指角度
    angle_ = points_cos_angle(
        ((int(handPoints_list[0][0]) - int(handPoints_list[10][0])),
         (int(handPoints_list[0][1]) - int(handPoints_list[10][1]))),
        ((int(handPoints_list[11][0]) - int(handPoints_list[12][0])),
         (int(handPoints_list[11][1]) - int(handPoints_list[12][1])))
    )
    angle_list.append(angle_)
    # ---------------------------- ring 无名指角度
    angle_ = points_cos_angle(
        ((int(handPoints_list[0][0]) - int(handPoints_list[14][0])),
         (int(handPoints_list[0][1]) - int(handPoints_list[14][1]))),
        ((int(handPoints_list[15][0]) - int(handPoints_list[16][0])),
         (int(handPoints_list[15][1]) - int(handPoints_list[16][1])))
    )
    angle_list.append(angle_)
    # ---------------------------- pink 小拇指角度
    angle_ = points_cos_angle(
        ((int(handPoints_list[0][0]) - int(handPoints_list[18][0])),
         (int(handPoints_list[0][1]) - int(handPoints_list[18][1]))),
        ((int(handPoints_list[19][0]) - int(handPoints_list[20][0])),
         (int(handPoints_list[19][1]) - int(handPoints_list[20][1])))
    )
    angle_list.append(angle_)
    return angle_list


def get_hand_gesture(fingers_angle_List):
    # 利用二维约束的方法定义手势
    thr_angle_others_bend = 60.  # 规定该角度为其余四个手指弯曲时的角度
    thr_angle_thumb_bend = 45.  # 规定该角度为拇指弯曲时的角度
    thr_angle_straight = 20.  # 规定该角度为手指伸直时的角度
    gesture_str = None
    if 65535. not in fingers_angle_List:
        if (fingers_angle_List[0] > thr_angle_thumb_bend):  # 拇指弯曲时
            if (fingers_angle_List[1] > thr_angle_others_bend) and (fingers_angle_List[2] > thr_angle_others_bend) and (
                    fingers_angle_List[3] > thr_angle_others_bend) and (fingers_angle_List[4] > thr_angle_others_bend):
                gesture_str = "fist"  # 拳头(四指聚拢)
            elif (fingers_angle_List[1] < thr_angle_straight) and (fingers_angle_List[2] < thr_angle_straight) and (
                    fingers_angle_List[3] < thr_angle_straight) and (fingers_angle_List[4] < thr_angle_straight):
                gesture_str = "four"  # 四
            elif (fingers_angle_List[1] < thr_angle_straight) and (fingers_angle_List[2] < thr_angle_straight) and (
                    fingers_angle_List[3] < thr_angle_straight) and (fingers_angle_List[4] > thr_angle_others_bend):
                gesture_str = "three"  # 三
            elif (fingers_angle_List[1] < thr_angle_straight) and (fingers_angle_List[2] < thr_angle_straight) and (
                    fingers_angle_List[3] > thr_angle_others_bend) and (fingers_angle_List[4] > thr_angle_others_bend):
                gesture_str = "two"  # 二
            elif (fingers_angle_List[1] < thr_angle_straight) and (fingers_angle_List[2] > thr_angle_others_bend) and (
                    fingers_angle_List[3] > thr_angle_others_bend) and (fingers_angle_List[4] > thr_angle_others_bend):
                gesture_str = "one"  # 一
        elif (fingers_angle_List[0] < thr_angle_straight):  # 拇指伸直时
            if (fingers_angle_List[1] < thr_angle_straight) and (fingers_angle_List[2] < thr_angle_straight) and (
                    fingers_angle_List[3] < thr_angle_straight) and (fingers_angle_List[4] < thr_angle_straight):
                gesture_str = "five"  # 五
            elif (fingers_angle_List[1] > thr_angle_others_bend) and (
                    fingers_angle_List[2] > thr_angle_others_bend) and (
                    fingers_angle_List[3] > thr_angle_others_bend) and (fingers_angle_List[4] > thr_angle_others_bend):
                gesture_str = "thumbUp"  # 点赞
    return gesture_str


def handwave_recognize(list1, list2):
    # 计算两组关键点的坐标差与规定间隔'ds'进行比对来区分手势
    ds = 0.2 * 640  # 规定坐标差比对距离,可根据工作位置进行调整,第一个乘数不建议改,第二个乘数为摄像头像素宽度
    x1_8, y1_8 = list1[8][0], list1[8][1]
    x1_12, y1_12 = list1[12][0], list1[12][1]
    x1_16, y1_16 = list1[16][0], list1[16][1]
    x1_20, y1_20 = list1[20][0], list1[20][1]
    x2_8, y2_8 = list2[8][0], list2[8][1]
    x2_12, y2_12 = list2[12][0], list2[12][1]
    x2_16, y2_16 = list2[16][0], list2[16][1]
    x2_20, y2_20 = list2[20][0], list2[20][1]
    gesture_str = None
    if x2_8 - x1_8 > ds and x2_12 - x1_12 > ds and x2_16 - x1_16 > ds and x2_20 - x1_20 > ds:
        gesture_str = "right"  # 向右挥手
        return gesture_str
    elif x1_8 - x2_8 > ds and x1_12 - x2_12 > ds and x1_16 - x2_16 > ds and x1_20 - x2_20 > ds:
        gesture_str = "left"  # 向左挥手
        return gesture_str
    elif y1_8 - y2_8 > ds and y1_12 - y2_12 > ds and y1_16 - y2_16 > ds and y1_20 - y2_20 > ds:
        gesture_str = "up"  # 向上挥手
        return gesture_str
    elif y2_8 - y1_8 > ds and y2_12 - y1_12 > ds and y2_16 - y1_16 > ds and y2_20 - y1_20 > ds:
        gesture_str = "down"  # 向下挥手
        return gesture_str
    else:
        return gesture_str


def main():
    mp_holistic = mp.solutions.holistic
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
    holistic = mp_holistic.Holistic(model_complexity=0)  # 设置模型复杂度为最小,减小机器性能消耗
    cooling_time = 1
    previous_time_fps = 0
    previous_time_cooling = 0
    gesture_str = None
    while True:
        success, img = cap.read()
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 转换为rgb,提高识别性能
        h, w, c = img.shape  # 摄像头所拍摄图像的高,宽,通道
        results = holistic.process(img)
        hand_clmList = []
        current_time = time.time()  # 获取当前时间
        if results.right_hand_landmarks:
            for id, lm in enumerate(results.right_hand_landmarks.landmark):  # 获取手指关节点
                cx, cy = lm.x * w, lm.y * h  # 计算关键点在图像上对应的像素坐标
                hand_clmList.append((cx, cy))
                if id == 8:
                    cv2.circle(img, (int(cx), int(cy)), 3, (0, 0, 255), 20)  # 标记食指位置
            angle_list = get_fingers_angle(hand_clmList)  # 获取手指弯曲角度
            gesture_str = get_hand_gesture(angle_list)  # 获取当前手势
            if current_time - previous_time_cooling >= cooling_time:  # 冷却机制
                if gesture_str == "fist" : 
                    print("hello!!")  # 以fist手势为例,如果为fist手势时执行某个任务,这里用print代指该任务
                    previous_time_cooling = current_time

        fps = 1 / (current_time - previous_time_fps)
        previous_time_fps = current_time
        img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
        img = cv2.flip(img, 1)  # 转换回BGR进行显示并镜像翻转
        cv2.putText(
            img, f"{int(fps)} FPS", (10, 30), cv2.FONT_HERSHEY_PLAIN, 2, (255, 0, 0), 2,
        )  # 显示fps
        if gesture_str is not None:
            cv2.putText(
                img, gesture_str, (10, 50), cv2.FONT_HERSHEY_PLAIN, 2, (255, 0, 0), 2,
            )  # 显示手势
        cv2.imshow("image", img)
        if cv2.waitKey(2) & 0xFF == 27:
            break
    cap.release()


if __name__ == '__main__':
    main()

### 使用MediaPipe实现实时手势识别 为了实现基于MediaPipe手势识别功能,需先理解MediaPipe的基础架构及其工作原理。MediaPipe是一个模块化的框架,用于构建多模态应用管道,特别是处理视觉数据的任务,如手部追踪和手势识别[^1]。 #### 安装必要的软件包 要开始使用MediaPiPe进行开发,首先需要安装MediaPipe以及辅助工具Rerun SDK或者cvzone来简化操作流程。可以通过pip命令快速完成这些依赖项的安装: ```bash pip install mediapipe opencv-python rerun-sdk cvzone ``` #### 初始化摄像头输入与创建窗口显示 为了让程序能够接收视频流作为输入源,在Python脚本里初始化OpenCV读取设备默认摄像机的数据,并设置好一个窗口用来实时预览捕捉的画面帧。 ```python import cv2 cap = cv2.VideoCapture(0) while cap.isOpened(): success, image = cap.read() if not success: print("Ignoring empty camera frame.") continue # 显示图像 cv2.imshow('Hand Gesture Recognition', image) if cv2.waitKey(5) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows() ``` 这段简单的循环结构将持续获取来自相机的新帧直到按下键盘上的`'q'`键为止[^4]。 #### 配置MediaPipe Hands解决方案 引入MediaPipe hands组件后,可以在每一帧上执行检测逻辑,从而定位出手掌中的各个关键点坐标信息。下面是如何配置hands对象的例子: ```python import mediapipe as mp from mediapipe.tasks import python from mediapipe.framework.formats import landmark_pb2 mp_hands = mp.solutions.hands.Hands( static_image_mode=False, max_num_hands=2, min_detection_confidence=0.7 ) ``` 这里设置了最大可跟踪双手数量为两支(`max_num_hands`),并且只有当置信度超过一定阈值时才会认为是一次有效的手掌捕获(`min_detection_confidence`)。 #### 处理每帧并绘制结果 对于每一个新到来的图片帧,都需要通过上述定义好的hands实例来进行分析;一旦成功解析出了手形,则会在原始画面上叠加描绘出相应的骨骼线条图样以便观察者更容易辨认所做出的动作姿态。 ```python results = mp_hands.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) if results.multi_hand_landmarks is not None: for hand_landmarks in results.multi_hand_landmarks: mp_drawing.draw_landmarks( image=image, landmark_list=hand_landmarks, connections=mp_hands.HAND_CONNECTIONS ) ``` 此段代码片段展示了如何利用MediaPipe内置的帮助函数draw_landmarks()将计算所得的关键部位连接起来形成完整的骨架图形表示形式[^5]。 #### 执行手势分类算法 最后一步就是根据之前获得的所有手指尖端的位置关系去判断当前用户正在做什么样的动作——这可能涉及到复杂的几何运算或者是机器学习模型预测过程。简单起见,可以考虑采用欧氏距离公式测量特定几个指节之间的间距变化情况来粗略区分几种常见的静态姿势类别。 ```python def get_label(index, hand, results): output = None for idx, classification in enumerate(results.multi_handedness): if classification.classification[0].index == index: label = classification.classification[0].label score = round(classification.classification[0].score, 2) text = '{} {}'.format(label, score) coords = tuple(np.multiply( np.array((hand.landmark[mp_hands.HandLandmark.WRIST].x, hand.landmark[mp_hands.HandLandmark.WRIST].y)), [image.shape[1], image.shape[0]]).astype(int)) output = text, coords return output for i, (hand_landmarks, handedness) in enumerate(zip(results.multi_hand_landmarks, results.multi_handedness)): ... # Add your gesture recognition logic here based on landmarks and handedness. ``` 以上即为整个基于MediaPipe手势识别系统的概貌描述及其实现要点概述[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值