EP_手掌动作识别-OpenCV+MediaPipe Hands
EP:Engineering and Project
OpenCV + MediaPipe Hands 手掌动作识别完整方案详解
定位:轻量化端侧手部关键点识别方案,适用于第一视角 Egocentric 头戴 / 胸前相机、工业示教、手势交互、石头剪刀布、手部动作捕捉;只专注手部(单 / 双手)21 点 3D 关键点输出,不输出手臂骨骼,手臂需要额外搭配姿态模型(如 MediaPipe Holistic Pose),正好匹配你当前项目架构。
一、方案整体概述
1. 核心分工
- OpenCV:视频采集、图像预处理、色彩空间转换、画面可视化绘制、截图 / 视频保存、键盘交互。不做 AI 识别,只负责图像管线。
- MediaPipe Hands:Google 自研端侧轻量化 CNN 推理管线,专门做手部检测 + 21 个手部关键点回归。内置两阶段结构:手部检测子网络 + 关键点回归子网络。
MediaPipe Hands 原生支持:单目 RGB 图像、CPU 实时推理,PC / 工控机 / Jetson 嵌入式均可跑;无需 GPU,常规 CPU 可达 25~30FPS。
2. MediaPipe Hands 输出定义(重点)
每检测到一只手,输出21 个归一化 3D 关键点(坐标范围[0,1],以图像左上角为原点)
表格
| 索引 | 部位 | 说明 |
|---|---|---|
| 0 | WRIST 手腕基点 | 手部坐标系原点 |
| 1~4 | 拇指:CMC/MCP/IP/TIP | 拇指根、中节、末节、指尖 |
| 5~8 | 食指:MCP/PIP/DIP/TIP | 掌根、近端、远端、指尖 |
| 9~12 | 中指:MCP/PIP/DIP/TIP | |
| 13~16 | 无名指:MCP/PIP/DIP/TIP | |
| 17~20 | 小指:MCP/PIP/DIP/TIP |
坐标字段:
x:图像水平归一化坐标(左 = 0,右 = 1)y:图像垂直归一化坐标(上 = 0,下 = 1)z:相对深度,以手腕点为基准;数值越小代表该点离相机更近,不是物理毫米距离
⚠️ 重要区别:MediaPipe Hands 关键点没有
visibility属性,和 Holistic Pose 不一样;有效性依靠检测置信度multi_handedness判断。
multi_handedness:返回左右手分类 + 置信度,判断当前识别到的是左手还是右手。
3. 可实现能力
- 实时定位手掌、全部手指关节、指尖位置
- 计算各手指关节夹角、手指伸直 / 弯曲状态
- 基于几何规则实现手势分类:石头 / 剪刀 / 布、OK、数字手势、指向等
- OpenCV 绘制手掌骨架、分色连线、指尖文字标签
- 输出时序关键点,做动作轨迹记录、动作时序分析
4. 识别效果和能力边界
识别效果

其中双手的十个手指都能够得到很好的识别,但是对于上肢的小臂和大臂的识别需要增加新的模块。
能力边界
需要加强点1: MediaPipe Hands 本身不识别小臂、大臂、肩、肘。它的输出只到手腕(关键点 0);手腕向上的小臂 / 肘关节,可以额外使用人体姿态模型(MediaPipe Holistic Pose / Pose)。
需要加强点2: 厚手套、大面积手部遮挡、暗光、严重运动模糊,识别会失效
需要加强点3: z 是相对深度,不是真实物理距离,不能直接测量手掌真实尺寸
二、MediaPipe Hands 内部推理原理(两阶段架构)
- 阶段 1:手部检测器(Hand Detection)
轻量 SSD 类目标检测网络,在整图上搜索手部,输出手部矩形 ROI(感兴趣区域)。
静态模式(static_image_mode=True):每一帧都跑一次全局手部检测,精度更高、速度慢,适合图片。
视频流模式(static_image_mode=False):首帧检测手部 ROI;后续帧不做全局检测,直接在上一帧手部 ROI 小窗口内追踪关键点,大幅提速。只有追踪丢失时,才重新全局检测。视频流必须用static_image_mode=False。
- 阶段 2:手部关键点回归网络(Hand Landmark Regression)
把 ROI 裁剪、归一化送入 CNN,回归 21 个 3D 关键点。模型训练数据包含大量自遮挡、手握物体场景,对第一视角近距离手掌适配性强。 - 后处理:左右手分类 + 坐标反投影
将 ROI 内归一化关键点映射回原图坐标系,输出整图归一化 x/y/z,同时输出左右手置信。
三、完整技术管线(OpenCV + MediaPipe Hands)
摄像头/视频文件 → OpenCV VideoCapture读取BGR帧
→ OpenCV预处理:BGR转RGB(MediaPipe只接收RGB图像),设置图像不可写锁(提升推理速度)
→ MediaPipe Hands推理:手部检测 + 21关键点回归
→ 结果解析:提取multi_hand_landmarks(21点)、multi_handedness(左右手置信)
→ 业务逻辑层:
① 过滤低置信手部结果
② 计算手指关节向量、弯曲角度
③ 手势分类(石头剪刀布等)
→ OpenCV可视化:绘制骨架连线、关键点、中文指尖标签、HUD状态信息
→ 存储:关键点时序数据写入JSONL/CSV/SQLite
→ 循环读取下一帧
四、核心参数详解(适配第一视角 Egocentric 场景)
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
static_image_mode=False, # 视频流模式:开启追踪,加速
max_num_hands=2, # 最多检测2只手(双手场景)
min_detection_confidence=0.6, # 全局手部检测置信阈值。低于0.6不触发手部检测
min_tracking_confidence=0.5 # 帧间追踪置信阈值;追踪结果低于该值,触发重新全局检测
)
参数调优说明(第一视角场景):
min_detection_confidence=0.6:第一视角手掌离镜头近、容易误检出背景,阈值不能太低;如果手部经常漏检,可下调至 0.5;容易误识别则上调 0.7。min_tracking_confidence=0.5:动态手部移动场景,平衡追踪稳定性和重新检测触发频率;手部快速晃动可上调。max_num_hands=2:双手操作场景固定为 2;单手场景可以改为 1,减少算力。
五、手势识别实现原理(石头剪刀布举例)
拿到 21 个关键点后,不使用深度学习分类,采用几何向量规则判断
- 计算向量:每个手指使用两段骨骼向量(如 PIP→DIP、DIP→TIP)
- 计算关节夹角:向量点积求关节弯曲角度
- 角度大:手指伸直;角度小:手指弯曲收拢
- 规则判定:
- ✊石头:五指全部关节大角度弯曲
- ✌剪刀:食指、中指伸直;无名指、小指、拇指弯曲
- ✋布:五指全部伸直张开
- 时序防抖:单帧识别容易抖动,增加滑动窗口滤波,连续 3~5 帧判定一致,才输出手势结果。
进阶方案:提取 21 点坐标向量作为特征,训练 KNN / 随机森林轻量分类器,比纯几何规则抗干扰更强。
六、可视化实现(OpenCV 绘制手掌骨架,分色 + 中文指尖标签)
visualize.py模块实现逻辑:
- 遍历每只手 21 个关键点,将归一化 x,y 转换为图像像素坐标:
px = int(lm.x * img_width)
py = int(lm.y * img_height) - 绘制骨架连线:不同手指设置不同颜色(拇指红色、食指绿色、中指蓝色、无名指黄色、小指紫色,分色骨架)
- 在 TIP 指尖像素位置绘制中文文字标签:拇指、食指、中指、无名指、小指
- HUD 叠加信息:检测到手数量、手势类别、推理 FPS
七、工程优化方案(适配你的项目)
1. EMA 时序平滑(tracking.py)
手部快速运动时,模型输出关键点会有抖动。对 21 点 x/y/z 使用指数移动平均 EMA 滤波:
(P_{smooth} = \alpha\cdot P_{raw} + (1-\alpha)\cdot P_{prev_smooth})
(\alpha):平滑系数(0~1),α 越小越平滑,但延迟变大;一般取 0.2~0.4。
只平滑关键点坐标,不修改原始 raw 数据,原始数据保留入库,方便溯源。
2. 手部置信过滤
multi_handedness[0].classification[0].score,低于阈值(0.5)直接丢弃该手所有关键点,避免无效点送入可视化与手势识别。
3. 手腕点位融合(重点,对接小臂骨架)
MediaPipe Hands 的手腕点(索引 0)精度 > Holistic Pose 手腕点。
工程策略:
- 手指骨架:采用独立 Hands 输出手腕点 0
- 小臂 / 大臂:使用 Holistic Pose 的肘、肩点位
- 做加权融合,消除两套模型手腕坐标错位,保证手指骨架无缝连接小臂。
八、完整最小可运行示例代码(OpenCV+MediaPipe Hands)
import cv2
import mediapipe as mp
import numpy as np
# 初始化mediapipe hands
mp_hands = mp.solutions.hands
mp_drawing = mp.solutions.drawing_utils
# 初始化手部模型(第一视角推荐参数)
hands = mp_hands.Hands(
static_image_mode=False,
max_num_hands=2,
min_detection_confidence=0.6,
min_tracking_confidence=0.5
)
# 摄像头读取
cap = cv2.VideoCapture(0)
# 手指骨架绘制配色
finger_colors = [
(255,0,0), # 拇指 红
(0,255,0), # 食指 绿
(0,0,255), # 中指 蓝
(255,255,0), # 无名指 青
(255,0,255) # 小指 品红
]
# 指尖中文标签
tip_names = ["拇指","食指","中指","无名指","小指"]
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
h,w = frame.shape[:2]
# OpenCV BGR -> RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame_rgb.flags.writeable = False
# 推理
result = hands.process(frame_rgb)
frame_rgb.flags.writeable = True
frame = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2BGR)
# 解析双手关键点
if result.multi_hand_landmarks:
for hand_idx, hand_lms in enumerate(result.multi_hand_landmarks):
# 绘制手部骨架
mp_drawing.draw_landmarks(
frame, hand_lms, mp_hands.HAND_CONNECTIONS,
mp_drawing.DrawingSpec(color=(100,100,100), thickness=2, circle_radius=3),
mp_drawing.DrawingSpec(color=(200,200,200), thickness=2)
)
# 绘制指尖中文标签
tip_ids = [4,8,12,16,20] # 5个指尖索引
for i, tip_id in enumerate(tip_ids):
lm = hand_lms.landmark[tip_id]
px, py = int(lm.x*w), int(lm.y*h)
cv2.putText(frame, tip_names[i], (px+5,py), cv2.FONT_HERSHEY_SIMPLEX, 0.4, finger_colors[i],1)
cv2.imshow("OpenCV + MediaPipe Hands 手掌识别", frame)
if cv2.waitKey(5) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
hands.close()
九、方案优缺点总结
1、 优点
- 开箱即用,CPU 实时,跨平台(Windows/Linux/ 工控机 / Jetson)
- 21 点完整手指关节,对第一视角近距离手掌、手抓物体场景鲁棒性强
- 可直接计算手指夹角,实现石头剪刀布、手势交互,无需额外训练深度学习模型
- 轻量,依赖少,和你现有代码架构完全兼容
- 支持时序追踪,配合 EMA 平滑,抑制关键点抖动
2、 缺点
- 仅输出手部,无小臂 / 大臂 / 肩肘,必须搭配姿态模型
- z 为相对深度,不能直接得到真实物理尺寸
- 没有 visibility 字段,遮挡后只能依靠检测置信过滤
- 完全遮挡、厚手套、严重暗光场景识别失效
- 快速大幅度运动时,关键点会产生小幅抖动(需要 EMA 平滑)
更多推荐


所有评论(0)