一、项目简介
本文基于 Google 开源的 MediaPipe 手部识别方案,结合 OpenCV 调用本地摄像头,通过手掌 21 个手部关键点坐标计算距离,判断伸直手指数量,实现实时 0-10 数字手势识别。 无需深度学习模型训练,开箱即用,运行速度快,适合手势交互、简易数字识别、课堂手势计数等轻量化场景。
二、环境依赖安装
只需要安装两个库,命令行执行:
bash
运行
pip install opencv-python mediapipeopencv-python:摄像头读取、画面渲染、图像色彩转换mediapipe:Google 手部关键点检测框架,内置轻量化手部识别模型,支持单 / 双手检测
三、核心原理讲解
1. MediaPipe 手部关键点分布
手掌一共输出 21 个归一化坐标点(x/y 范围 0~1),本代码用到关键点位:
- 0 号点:手掌根部(腕关节)
- 4 号点:大拇指指尖
- 5 号点:食指根部关节
- 8 号点:食指指尖
- 12 号点:中指指尖
- 16 号点:无名指指尖
- 20 号点:小指指尖
2. 手指伸直判断逻辑
- 基准长度校准用腕关节 0 点与食指根部 5 点的距离作为手掌基准长度,消除远近、手掌大小带来的坐标偏差:
base = distance_0_5 / 0.6 - 四根常规手指(食 / 中 / 无名 / 小指)计算腕部 0 点到各指尖的欧式距离平方,若距离大于基准值,判定手指伸直,flag 计数 + 1;
- 大拇指特殊判定大拇指活动方式与其余四指不同,改用食指根部 5 点到大拇指 4 点距离做阈值判断,阈值缩小为
base*0.3; - 手势映射flag 代表伸直手指总数,对应列表
gesture = ["none", "one", "two"...,"ten"],实时打印对应数字手势。
3. 图像预处理说明
MediaPipe 模型要求输入 RGB 图像,但 OpenCV 读取摄像头默认为 BGR 格式,因此需要两次色彩转换:
- 摄像头画面 BGR → RGB 送入手部模型推理
- 推理完成 RGB → BGR 用于 OpenCV 绘图、显示 额外增加
cv2.flip(frame,1)水平镜像,符合人眼正常视觉习惯。
四、完整可运行代码
python
运行
import cv2 import mediapipe as mp # 手势标签,flag对应伸直手指数量 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, # False视频流模式,True静态图片模式 max_num_hands=2, # 最多检测2只手 min_detection_confidence=0.75,# 手部检测最低置信度 min_tracking_confidence=0.75 # 手部跟踪最低置信度 ) # 打开本地摄像头 cap = cv2.VideoCapture(0) while True: flag = 0 # 每帧重置手指计数 ret, frame = cap.read() if not ret: print("无法读取摄像头画面") break # BGR转RGB供mediapipe处理 frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # 水平翻转,消除镜像 frame = cv2.flip(frame, 1) # 手部关键点推理 results = hands.process(frame) # 转回BGR用于opencv绘图显示 frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) # 如果检测到手部关键点 if results.multi_hand_landmarks: for hand_landmarks in results.multi_hand_landmarks: # 0:腕部坐标,5:食指根部坐标,计算手掌基准长度 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 # 大拇指4号指尖距离计算 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) # 食指8、中指12、无名指16、小指20指尖距离 p8_x, p8_y = hand_landmarks.landmark[8].x, hand_landmarks.landmark[8].y distance_0_8 = pow(p0_x - p8_x, 2) + pow(p0_y - p8_y, 2) p12_x, p12_y = hand_landmarks.landmark[12].x, hand_landmarks.landmark[12].y distance_0_12 = pow(p0_x - p12_x, 2) + pow(p0_y - p12_y, 2) p16_x, p16_y = hand_landmarks.landmark[16].x, hand_landmarks.landmark[16].y distance_0_16 = pow(p0_x - p16_x, 2) + pow(p0_y - p16_y, 2) p20_x, p20_y = hand_landmarks.landmark[20].x, 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 # 最大限制10根手指(双手) if flag >= 10: flag = 10 # 绘制手部21个关键点与连接线 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. MediaPipe 手部检测初始化参数
python
运行
hands = mp_hands.Hands( static_image_mode=False, max_num_hands=2, min_detection_confidence=0.75, min_tracking_confidence=0.75 )static_image_mode=False:视频流模式,启用手部跟踪加速推理,帧率更高;图片识别改为 True;max_num_hands=2:支持左右手同时识别,最高可检测双手 10 根手指;- 两个 confidence 阈值:过滤低置信度误识别,阈值越高识别越严格。
2. 距离平方代替欧式距离
代码全部使用pow(x差,2)+pow(y差,2)计算距离平方,不开根号,减少浮点运算,提升实时推理速度,不影响大小比较逻辑。
3. 自适应基准 base 解决远近干扰
直接使用固定距离阈值会出现:手靠近镜头判定全伸直、远离镜头判定全弯曲。 代码利用手掌自身尺寸做自适应基准,无论手掌远近,判定标准统一,鲁棒性更强。
4. 双手兼容逻辑
单只手最多 5 根手指,双手最多 10 根,flag >=10限制最大计数为 10,对应 gesture 列表 ten。
5. 可视化与退出逻辑
mp_drawing.draw_landmarks:自动绘制手掌 21 个关键点 + 骨骼连线,直观观察手部识别效果;cv2.putText:红色粗字体实时展示当前识别数字;waitKey(1)监听键盘,按下 ESC(27)关闭摄像头窗口。
六、运行效果说明
- 运行代码自动弹出摄像头窗口,画面左右镜像;
- 伸出对应数量手指,左上角实时显示 one/two/three...ten;
- 无手进入画面显示 none;
- 双手同时伸出最多显示 ten;
- 画面上会绘制彩色手掌骨骼关键点,方便调试识别异常。
七、常见问题优化方案
- 识别不准、手指计数乱跳
- 调高
min_detection_confidence至 0.85; - 调整
base = distance_0_5 / 0.6中的 0.6 阈值,根据自己摄像头微调;
- 调高
- 大拇指识别错误修改大拇指阈值
base * 0.3,增大 / 缩小系数适配手势; - 画面卡顿
- 降低摄像头分辨率;
- 将
max_num_hands改为 1,只检测单手;
- 不需要镜像画面删除
frame = cv2.flip(frame, 1)这一行即可。
八、拓展改进方向
- 增加手势分类:剪刀、石头、布等自定义手势;
- 保存手势截图,制作手势数据集;
- 结合串口 / 单片机,手势控制硬件设备;
- 替换距离判断逻辑,使用关键点角度判断手指弯曲程度,识别精度更高;
- 增加左右手区分,分别计数。
九、总结
MediaPipe 手部检测上手成本极低,无需训练模型,依靠内置预训练模型即可快速完成手部关键点提取。本文通过简单几何距离计算实现 0~10 数字手势识别,代码轻量化、实时性强,适合入门学习手部视觉交互开发,可拓展到人机交互、体感控制、课堂答题计数等场景。