计算机视觉(opencv)——基于 MediaPipe 的手势识别系统 - 实践
基于 MediaPipe 与 OpenCV 的手势识别系统实现详解
在计算机视觉和人机交互领域,手势识别是一项非常重要的技术。它使得计算机能够通过摄像头识别人类手部动作,从而实现自然交互。例如,虚拟鼠标控制、手势命令控制、游戏互动等,都依赖于精准的手势检测与识别。
本文将通过 Python + OpenCV + MediaPipe 的方式,详细讲解一个能够实时识别手势(0~10)的系统实现原理与代码结构。
一、系统原理概述
手势识别的核心在于:
检测手部关键点(如手指、掌心、手腕等位置);
通过关键点之间的距离关系判断手指是否伸展或弯曲;
根据手指伸展数量映射为具体的数字或手势。
Google 的 MediaPipe 提供了高精度的手部检测与关键点跟踪模型,可实时返回 21 个关键点坐标。我们可以基于这些坐标信息,计算手指弯曲程度,从而判断出用户展示的手势。
二、所需库与环境
本项目需要以下 Python 库:
pip install opencv-python mediapipe
OpenCV:用于图像采集与显示;
MediaPipe:用于手部检测与关键点识别;
NumPy:用于数学计算(本例中可选)。
三、完整代码实现
import cv2
import mediapipe as mp
# 手势标签定义(0~10)
gesture = ["none", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine", "ten"]
flag = 0
# 初始化 MediaPipe 模块
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_hand_landmarks:
for hand_landmarks in results.multi_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, p4_y = hand_landmarks.landmark[4].x, hand_landmarks.landmark[4].y
p8_x, p8_y = hand_landmarks.landmark[8].x, hand_landmarks.landmark[8].y
p12_x, p12_y = hand_landmarks.landmark[12].x, hand_landmarks.landmark[12].y
p16_x, p16_y = hand_landmarks.landmark[16].x, hand_landmarks.landmark[16].y
p20_x, p20_y = hand_landmarks.landmark[20].x, hand_landmarks.landmark[20].y
# 计算各指尖到手腕的平方距离
distance_5_4 = pow(p5_x - p4_x, 2) + pow(p5_y - p4_y, 2)
distance_0_8 = pow(p0_x - p8_x, 2) + pow(p0_y - p8_y, 2)
distance_0_12 = pow(p0_x - p12_x, 2) + pow(p0_y - p12_y, 2)
distance_0_16 = pow(p0_x - p16_x, 2) + pow(p0_y - p16_y, 2)
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)
# 按 ESC 退出
if cv2.waitKey(1) & 0xFF == 27:
break
cap.release()
cv2.destroyAllWindows()
四、算法逻辑详解
该程序的核心逻辑可以分为以下几个步骤:
1. 摄像头帧采集
使用 cv2.VideoCapture(0) 读取实时视频流,并进行逐帧处理。
2. 手部检测与关键点提取
通过 hands.process(frame),MediaPipe 会自动检测手部位置,并返回 21 个关键点坐标。
这些关键点在手部不同部位,如:
| 编号 | 名称(英文) | 中文说明 | 所属手指 |
|---|---|---|---|
| 0 | WRIST | 手腕(连接手臂) | —— |
| 1 | THUMB_CMC | 拇指掌骨基部 | 拇指 |
| 2 | THUMB_MCP | 拇指第一关节(掌指关节) | 拇指 |
| 3 | THUMB_IP | 拇指第二关节(指间关节) | 拇指 |
| 4 | THUMB_TIP | 拇指指尖 | 拇指 |
| 5 | INDEX_FINGER_MCP | 食指根部(掌指关节) | 食指 |
| 6 | INDEX_FINGER_PIP | 食指中部关节 | 食指 |
| 7 | INDEX_FINGER_DIP | 食指末端关节 | 食指 |
| 8 | INDEX_FINGER_TIP | 食指指尖 | 食指 |
| 9 | MIDDLE_FINGER_MCP | 中指根部 | 中指 |
| 10 | MIDDLE_FINGER_PIP | 中指中部关节 | 中指 |
| 11 | MIDDLE_FINGER_DIP | 中指末端关节 | 中指 |
| 12 | MIDDLE_FINGER_TIP | 中指指尖 | 中指 |
| 13 | RING_FINGER_MCP | 无名指根部 | 无名指 |
| 14 | RING_FINGER_PIP | 无名指中部关节 | 无名指 |
| 15 | RING_FINGER_DIP | 无名指末端关节 | 无名指 |
| 16 | RING_FINGER_TIP | 无名指指尖 | 无名指 |
| 17 | PINKY_MCP | 小指根部 | 小指 |
| 18 | PINKY_PIP | 小指中部关节 | 小指 |
| 19 | PINKY_DIP | 小指末端关节 | 小指 |
| 20 | PINKY_TIP | 小指指尖 | 小指 |
3. 距离计算与手势判断
通过比较各指尖与手腕(或掌根)的相对距离,判断该手指是否伸直。
例如:
若
distance_0_8 > base,说明食指伸直;若
distance_0_12 > base,说明中指伸直;若四根手指均满足条件,且拇指距离也大,则识别为“5”。
4. 手势编号与映射
程序维护一个手势数组:
gesture = ["none", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten"]
根据检测到的手指数量 flag,直接映射为相应的手势名称。
五、效果展示与扩展
运行程序后,摄像头窗口会实时显示手部关键点与骨架线。当你伸出不同数量的手指时,左上角的文本会显示相应的数字(如 “three”、“five” 等)。
示例效果:
| 手势 | 识别结果 | 描述 |
|---|---|---|
| ✊ | none | 全部弯曲 |
| ☝️ | one | 伸出食指 |
| ✌️ | two | 食指+中指 |
| five | 全部伸展 |
六、改进与优化方向
引入深度信息:
当前算法仅基于二维坐标,容易受角度影响。可利用深度相机或双目视觉增强鲁棒性。增加动态手势识别:
结合时间序列(如 LSTM 网络)可识别挥手、比划等动作。加入多手识别与左右手分类:
通过results.multi_handedness可判断是左手还是右手,扩展为更复杂的交互。与应用系统结合:
例如将手势映射为鼠标点击、音量调节、虚拟键盘输入等功能。
七、总结
本文展示了一个基于 MediaPipe + OpenCV 的实时手势识别系统。
通过检测手部 21 个关键点并计算距离关系,我们能够识别从“0”到“10”的静态手势。
该系统具有 实时性强、部署简单、可扩展性高 的优点,非常适合作为入门级手势识别项目或交互式系统原型。
未来可以结合深度学习模型(如 CNN + LSTM)实现更复杂的手势分类与动作识别,推动自然人机交互的发展。
浙公网安备 33010602011771号