计算机视觉进阶教学之Mediapipe库(一)

目录

简介

[一、Mediapipe Python的安装和应用](#一、Mediapipe Python的安装和应用)

二、手部检测

[1. 导入必要的库](#1. 导入必要的库)

[2. 初始化 MediaPipe 组件](#2. 初始化 MediaPipe 组件)

[3. 配置并创建手部检测模型实例](#3. 配置并创建手部检测模型实例)

[4. 启动摄像头并进入主循环](#4. 启动摄像头并进入主循环)

[5. 绘制检测结果](#5. 绘制检测结果)

三、手势识别

[1. 手势识别核心逻辑](#1. 手势识别核心逻辑)

[a. 计算基准距离 (Base Distance)](#a. 计算基准距离 (Base Distance))

[b. 计算各手指尖到手腕的距离](#b. 计算各手指尖到手腕的距离)

[c. 判断手指是否伸直并计数](#c. 判断手指是否伸直并计数)

[d. 处理双手情况](#d. 处理双手情况)


简介

Mediapipe是google的一个开源项目,可以提供开源的、跨平台的常用机器学习(machine learning)方案。Mediapipe实际上是一个集成的机器学习视觉算法的工具库,包含了人脸检测、人脸关键点、手势识别、头像分割和姿态识别等各种模型。
Mediapipe具备的优点有:

1)支持各种平台和语言,包括IOS,Android,C++,Python,JAVAScript,Coral等;

2)速度快,各种模型基本上可以做到实时运行。
Mediapipe在实际应用中的例子:

1)人脸检测;

2)FaceMesh:从图像/视频中重建出人脸的3D Mesh,可以用于AR渲染;

3)人像分割:从图像/视频中把人分割出来,可用于视频会议如Zoom、钉钉;

4)手势识别和跟踪:可以识别标出手部21个关键点的3D坐标;

5)人体姿态识别:可以识别标出人体33个关键点的3D坐标。

一、Mediapipe Python的安装和应用

  1. 安装python 3.7以上版本,下载地址:https://www.python.org/getit
  2. 安装Mediapipe
    1)安装OpenCV,终端执行pip install opencv-contrib-python
    2)安装Mediapipe,终端执行pip install mediapipe,或者使用国内镜像 pip install mediapipe -i https://pypi.tuna.tsinghua.edu.cn/simple/br

具体详细应用可以详细查看官网介绍

Mediapipe

二、手部检测

这段代码实现了一个实时的手部关键点检测系统。它通过电脑的摄像头捕获视频流,然后使用 Google 的 MediaPipe 库来识别画面中的人手,并实时地在视频上绘制出 21 个手部关键点及其连接关系。

1. 导入必要的库
python 复制代码
import cv2
import mediapipe as mp
  • cv2: 这是 OpenCV 库,一个强大的计算机视觉库。在这里,它的主要作用是:
    • 从摄像头捕获视频帧 (cv2.VideoCapture)。
    • 在视频帧上绘制文字和关键点 (cv2.putText, cv2.imshow)。
    • 处理图像颜色空间 (cv2.cvtColor)。
    • 显示视频窗口并等待用户按键 (cv2.imshow, cv2.waitKey)。
  • mediapipe: 这是 Google 开发的一个开源机器学习库,特别擅长处理实时感知任务,如手部、面部、姿势识别等。
2. 初始化 MediaPipe 组件
python 复制代码
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands
  • mp.solutions.drawing_utils: 这是 MediaPipe 提供的一个绘图工具包。它包含了方便的函数,可以直接在图像上绘制出检测到的关键点(landmarks)和它们之间的连接(connections)。
  • mp.solutions.hands: 这是 MediaPipe 的手部检测解决方案。它封装了预训练好的模型,可以直接用于检测图像或视频中的人手
3. 配置并创建手部检测模型实例
python 复制代码
hands = mp_hands.Hands(
    static_image_mode=False,
    max_num_hands=2,
    min_detection_confidence=0.75,
    min_tracking_confidence=0.75)
'''
mp.solutions.drawing_utils是一个绘图模块,将识别到的手部关键点信息绘制道cv2图像中,mp.solutions.drawing_style定义了绘制的风格。
mp.solutions.hands是mediapipe中的手部识别模块,可以通过它调用手部识别的api,然后通过调用mp_hands.Hands初始化手部识别类。
mp_hands.Hands中的参数:
1)static_image_mode=True适用于静态图片的手势识别,Flase适用于视频等动态识别,比较明显的区别是,若识别的手的数量超过了最大值,
True时识别的手会在多个手之间不停闪烁,而False时,超出的手不会识别,系统会自动跟踪之前已经识别过的手。默认值为False;
2)max_num_hands用于指定识别手的最大数量。默认值为2;
3)min_detection_confidence 表示最小检测信度,取值为[0.0,1.0]这个值约小越容易识别出手,用时越短,但是识别的准确度就越差。越大识别的越精准,
但是响应的时间也会增加。默认值为0.5;
4)min_tracking_confience 表示最小的追踪可信度,越大手部追踪的越准确,相应的响应时间也就越长。默认值为0.5。
'''

这是代码中非常关键的一步,我们创建了一个 Hands 类的实例。这个实例就是我们的手部检测器。它接受几个重要的参数来配置其行为:

  • static_image_mode=False:
    • False (动态模式): 适用于视频流。系统会首先尝试检测手,如果成功,就会切换到更快速的 "追踪" 模式。这大大提高了处理视频时的帧率。如果追踪失败,它会自动重新开始检测。
    • True (静态模式): 适用于单张图片。系统会对每一帧都进行完整的检测,而不会追踪。这在视频中会非常慢,但对于分析静态图片更准确。
  • max_num_hands=2: 指定最多可以同时检测的手的数量。这里设置为 2,意味着可以同时识别两只手。
  • min_detection_confidence=0.75: 最小检测置信度。只有当模型对检测结果的置信度(概率)高于这个值(这里是 75%)时,才会认为成功检测到了一只手。这个值越高,误检越少,但可能会漏掉一些手势。
  • min_tracking_confidence=0.75: 最小追踪置信度。在追踪模式下,只有当模型对追踪结果的置信度高于这个值时,才会继续追踪。如果低于这个值,系统会认为追踪丢失,并重新启动检测。
4. 启动摄像头并进入主循环
python 复制代码
cap = cv2.VideoCapture(0)
while True:
    flag = 0
    ret, frame = cap.read()
    h,w=frame.shape[:2]
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    # 因为摄像头是镜像的,所以将摄像头水平翻转
    # 不是镜像的可以不翻转
    frame = cv2.flip(frame, 1)
    results = hands.process(frame)
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
5. 绘制检测结果
python 复制代码
 if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            # print('hand_landmarks:', hand_landmarks)
            # 计算关键点的距离,用于判断手指是否伸直
            for i in range(len(hand_landmarks.landmark)):
                x = hand_landmarks.landmark[i].x
                y = hand_landmarks.landmark[i].y
                z = hand_landmarks.landmark[i].z
                # print(x,y,z)
                cv2.putText(frame, str(i), (int(x*w),int(y*h)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0),2)
            # 关键点可视化
            mp_drawing.draw_landmarks(frame,
                                      hand_landmarks,
                                      mp_hands.HAND_CONNECTIONS)

    cv2.imshow('MediaPipe Hands', frame)
    if cv2.waitKey(1) & 0xFF == 27:
        break
cap.release()
cv2.destroyAllWindows()

最后我们实现了一个这样的功能

三、手势识别

这段代码实现了一个实时的手势识别系统,能够通过摄像头识别出0 到 10的数字手势(即伸出几个手指,就识别为相应的数字)。它通过分析手部关键点之间的距离来判断每个手指是否伸直,然后根据伸直手指的数量来确定手势。

python 复制代码
import cv2
import mediapipe as mp
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()
1. 手势识别核心逻辑

这是本代码最核心、最复杂的部分。

python 复制代码
    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            # ... 距离计算 ...
            # ... 手指判断 ...
  • if results.multi_hand_landmarks:: 判断是否检测到了手。
  • for hand_landmarks in results.multi_hand_landmarks:: 遍历每一只检测到的手。如果是双手,这个循环会执行两次。
a. 计算基准距离 (Base Distance)
python 复制代码
            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
  • p0 是手腕根部的关键点,p5 是拇指根部的关键点。
  • distance_0_5: 计算 p0p5 之间的平方距离 (注意:这里用的是 pow 计算平方和,没有开根号,因为比较大小时,平方距离和实际距离效果一样,但计算更快)。
  • base = distance_0_5 / 0.6: 这是一个非常巧妙的归一化技巧
    • distance_0_5 的值会随着手离摄像头的远近而变化(手近则大,手远则小)。
    • 通过将这个距离除以一个常数(0.6),我们得到一个与手的大小相关的基准值 base
    • 后续所有的距离判断都将基于这个 base 值,这样就可以消除手的远近对判断结果的影响,使得在不同距离下的手势识别都能保持准确。
b. 计算各手指尖到手腕的距离
python 复制代码
            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)

            # ... 同样的方式计算 distance_0_12 (中指), distance_0_16 (无名指), distance_0_20 (小指)
  • p4: 拇指指尖
  • p8: 食指指尖
  • p12: 中指指尖
  • p16: 无名指指尖
  • p20: 小指指尖
  • distance_5_4: 计算拇指指尖 p4 到拇指根部 p5 的平方距离。
  • distance_0_8, distance_0_12, distance_0_16, distance_0_20: 计算各个手指尖到手腕 p0 的平方距离。
c. 判断手指是否伸直并计数
python 复制代码
            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 distance_0_8 > base:: 如果食指指尖 p8 到手腕 p0 的距离大于基准值 base,就认为食指是伸直的,flag 计数器加 1。
  • 这个判断逻辑同样适用于中指、无名指和小指。
  • 拇指的判断略有不同if distance_5_4 > base * 0.3:
    • 拇指的运动方式和其他四指不同,它是向外侧张开,而不是向上伸直。
    • 所以这里比较的是拇指指尖 p4 和拇指根部 p5 的距离。
    • 阈值也更小 (base * 0.3),因为拇指张开的幅度通常没有其他手指伸得那么远。
d. 处理双手情况
python 复制代码
            if flag >= 10:
                flag = 10
  • 这个判断是为了处理双手的情况。
  • 当两只手都被检测到时,for 循环会执行两次。
  • 第一次处理左手,flag 累加了左手伸直的手指数量(比如 5)。
  • 第二次处理右手,flag 在之前的基础上继续累加(5 + 5 = 10)。
  • if flag >= 10: flag = 10 确保了当两只手都伸出所有手指时(总共 10 个手指),flag 的值被限制在 10,以对应 gesture 列表中的 "ten"。
相关推荐
羊羊小栈2 小时前
基于知识图谱(Neo4j)和大语言模型(LLM)的图检索增强(GraphRAG)的智能音乐推荐系统(vue+flask+AI算法)
人工智能·毕业设计·neo4j·大作业
缘友一世2 小时前
机器学习决策树与大模型的思维树
人工智能·决策树·机器学习
缘友一世2 小时前
机器学习中的决策树
人工智能·决策树·机器学习
2401_841495643 小时前
【计算机视觉】分水岭实现医学诊断
图像处理·人工智能·python·算法·计算机视觉·分水岭算法·医学ct图像分割
罗小罗同学3 小时前
虚拟细胞赋能药物研发:AI驱动的“细胞模拟器”如何破解研发困局
人工智能·医学ai·虚拟细胞
艾醒4 小时前
探索大语言模型(LLM):参数量背后的“黄金公式”与Scaling Law的启示
人工智能·算法
艾醒4 小时前
探索大语言模型(LLM):使用EvalScope进行模型评估(API方式)
人工智能·算法
艾醒5 小时前
探索大语言模型(LLM):大模型微调方式全解析
人工智能·算法
IvanCodes5 小时前
RTX 4090 加速国产 AIGC 视频生成:腾讯混元与阿里千问开源模型
人工智能·开源·aigc·音视频