
简介基于Python、MediaPipe与OpenCV构建的手势识别工程包面向计算机专业毕设与图像处理初学者解决手部关键点检测、手势分类与手指计数的完整落地需求。包内共5个文件压缩包仅7KB2个Python脚本分别承载主程序与手部追踪模块2个Markdown文档提供环境配置与使用说明另含1个.gitignore便于工程管理整体轻量且结构清晰。项目通过MediaPipe提取21个手部关键点配合OpenCV完成实时图像绘制与指尖计数逻辑可直接运行适合作为手势交互Demo或毕业设计基础。目前已有1584人学习下载对希望快速复现手势识别效果、理解计算机视觉实践流程的读者具有直接参考价值。1. 手势识别毕设项目不是调库而是把 mediapipe 用透做计算机视觉毕设的同学应该都有体会手势识别这个题目看着不难真正动手才发现采集、标注、训练一套深度学习模型的工作量足够拖掉一个假期。这个资源走的不是那条路它用 mediapipe 的 hand landmark 模型直接输出手部 21 个关键点坐标再配合 opencv 做图像读取和绘制代码量控制在两个 Python 文件以内打开摄像头就能看到实时的手指计数结果。适合三类人计算机视觉方向的毕设学生、想快速验证手势交互原型的开发者、以及刚接触 opencv 和 mediapipe 想找个完整项目入门的初学者。如果你需要的是「能跑、能改、能讲清楚原理」的起步代码而不是自己从零训练一个手部检测网络这份资源正好接得住。2. 环境准备opencv 与 mediapipe 的版本匹配是第一个门槛2.1 为什么选 mediapipe 而不是传统肤色检测传统的手势识别思路是先做肤色分割YCrCb 空间阈值分割或 HSV 色彩过滤提取轮廓后再数凸包缺陷来判定手指。这套方案在纯色背景下效果尚可但遇到复杂背景、不同肤色、强光变化时分割结果会大片翻车。mediapipe 采用的是基于深度学习的 21 点手部关键点回归模型不依赖肤色假设输入 RGB 帧后直接输出每个关键点的归一化坐标和可见性置信度鲁棒性高出一个量级。这个项目把模型推理封装在 HandTrackingModule.py 里业务逻辑在 main.py两层分离。你不需要理解模型的内部结构只需要掌握两个 APImediapipe 的 Hands 类用于创建检测器drawing_utils 用于把关键点和连接线画到画面上。对毕设来说能说清这两个 API 的输入输出和在做什么答辩的算法部分就立得住。2.2 安装步骤pip 安装与 Anaconda 环境隔离如果你用的是 Anaconda建议新建一个独立环境避免把 base 环境搞乱。我一般这样做conda create -n gesture python3.8 conda activate gesture pip install opencv-python pip install mediapipe这里有个容易忽略的点mediapipe 对 Python 版本有要求。老版本 mediapipe 在 Python 3.9 以上会出现安装后 import 失败的问题所以建环境时我习惯固定 Python 3.8。如果你用的是系统 Python直接用 pip 安装即可但要注意 pip 对应的 Python 版本用python -m pip比裸pip更可靠。安装完成后做一次最小验证避免后面跑 main.py 时才暴露环境问题python -c import cv2; import mediapipe as mp; print(cv2.__version__, mp.__version__)如果正常打印两个版本号说明环境就绪。这一步看起来多余实际上能过滤掉一半的环境类报错后面避坑章节里第一条坑就是从这里引出来的。2.3 依赖关系核对cv2 与 mediapipe 的分工这个项目的分工很清楚cv2opencv-python负责读取摄像头帧、BGR 到 RGB 的颜色空间转换、图像显示、按键捕获mediapipe负责手部检测与关键点回归返回的坐标是归一化的0~1 之间的比例值numpymediapipe 的坐标处理实际上依赖 numpy虽然你的代码里没显式 import但 mediapipe 内部会用到一个常见的认知偏差是「opencv 负责识别」实际上在这个项目里 opencv 只做图像 IO 和绘制真正的识别是 mediapipe 干的。答辩时讲清楚这个边界比笼统说「用了 AI 技术」更有说服力。3. 项目源码拆解从 HandTrackingModule 到 main.py 的完整链路3.1 文件结构与运行入口解压拿到的是这样一组文件文件作用main.py主程序入口负责摄像头读取、调用跟踪器、显示画面HandTrackingModule.py封装 HandTracker 类包含检测、绘制、坐标获取方法readme.md项目说明文档.gitignore版本管理配置与运行无关测试数据目录用于离线验证的图片或视频素材运行方式很简单cd python-gesture-recognition-master python main.py如果你的电脑没有摄像头或者想在调试阶段不占用摄像头可以把 main.py 里的视频源从0换成测试数据里的视频文件路径。这是这份资源最实用的地方之一——testdata 允许你先跑通流程再切摄像头。3.2 HandTrackingModule封装的核心类项目把真正干活的代码放在 HandTrackingModule.py 里这是正确的模块化思路。核心代码结构大致如此import cv2 import mediapipe as mp class HandTracker: def __init__(self, static_image_modeFalse, max_num_hands1, min_detection_confidence0.5, min_tracking_confidence0.5): self.mp_hands mp.solutions.hands self.mp_draw mp.solutions.drawing_utils self.hands self.mp_hands.Hands( static_image_modestatic_image_mode, max_num_handsmax_num_hands, min_detection_confidencemin_detection_confidence, min_tracking_confidencemin_tracking_confidence) def find_hand_landmarks(self, frame, drawTrue): rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) self.results self.hands.process(rgb_frame) if self.results.multi_hand_landmarks: for hand_landmarks in self.results.multi_hand_landmarks: if draw: self.mp_draw.draw_landmarks( frame, hand_landmarks, self.mp_hands.HAND_CONNECTIONS) return frame逻辑说明static_image_mode如果是处理单张图片而不是视频流要设为 True否则模型会依赖跟踪来加速检测max_num_hands限制检测的手的数量毕设演示单手计数时设为 1 可以降低误检率min_detection_confidence检测置信度阈值调高可以减少误检但也会让手稍远时就丢失rgb_framemediapipe 要求的输入是 RGB 顺序opencv 读出来是 BGR必须转换results.multi_hand_landmarks检测到手的列表每只手有 21 个关键点参数速查表参数默认值调大场景调小场景min_detection_confidence0.5背景复杂、误检多手经常检测不到min_tracking_confidence0.5画面抖动导致关键点跳变手势快速变化时max_num_hands1需要双手同时识别只做单手计数static_image_modeFalse处理图片文件处理视频流3.3 main.py 主循环摄像头帧的完整生命周期import cv2 from HandTrackingModule import HandTracker def main(): cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) tracker HandTracker(max_num_hands1) while True: success, frame cap.read() if not success: print(摄像头读取失败) break frame cv2.flip(frame, 1) # 镜像翻转操作更自然 frame tracker.find_hand_landmarks(frame) cv2.imshow(Hand Gesture Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() if __name__ __main__: main()逻辑说明cv2.VideoCapture(0)0 表示默认摄像头多摄像头设备可能需要改为 1 或 2cv2.flip(frame, 1)翻转画面是很多 opencv 交互项目的惯例不然你抬手时画面里的手是反的waitKey(1)必须要有这个延时否则画面刷新过快导致窗口无响应ord(q)键盘 q 键退出循环这里要记得释放摄像头资源这层代码的运行逻辑是每一帧读进来 → 翻转为镜像 → 调用 HandTracker 检测并绘制 → 显示 → 等待按键。整个循环体只有 6 行核心操作但每一行少掉都会出问题。3.4 跑通后的验证方法程序启动后你的手出现在画面里时应该能看到 21 个关键点用连线连成手部骨架。手指尖有单独的点手腕处也标了。这时候挥动手掌骨架应该跟随移动。如果你的画面里出现多套骨架说明max_num_hands参数需要调整但先别急着改——多数情况是你的手离镜头太近或者背景里有类似肤色的物体干扰。距离镜头 40~60 厘米手正对镜头是最容易检测到的姿态。4. 手指计数从 21 个关键点到可读数字的坐标逻辑4.1 关键点编号规则与坐标含义mediapipe 返回的 21 个关键点是有固定编号的从 0 号手腕到 20 号小指指尖。编号规则如下0手腕1-4拇指1 为掌根4 为指尖5-8食指5 为掌根8 为指尖9-12中指13-16无名指17-20小指每个关键点的坐标是归一化的x、y 值在 0~1 之间需要乘以图像宽高才能得到像素坐标。这个归一化设计的好处是画面分辨率变化不影响后续逻辑。def get_finger_tips(self): tips [4, 8, 12, 16, 20] landmarks self.results.multi_hand_landmarks[0].landmark tip_positions [(landmarks[i].x, landmarks[i].y) for i in tips] return tip_positions4.2 判定规则指尖与指根的相对位置比较手指伸开与握拳的本质区别在于伸直时指尖比指根更远离手掌中心握拳时指尖回缩到接近指根的位置。所以计数的常见做法是比较指尖和同手指指根关键点的 y 坐标或 x 坐标。finger_tips [4, 8, 12, 16, 20] finger_dips [3, 6, 10, 14, 18] # 各手指的中间关节或指根 def count_fingers(landmarks): count 0 # 拇指比较 x 坐标因为拇指横向运动 if landmarks[4].x landmarks[3].x: count 1 # 其余四指比较 y 坐标指尖在上则 y 更小 for tip, dip in zip([8, 12, 16, 20], [6, 10, 14, 18]): if landmarks[tip].y landmarks[dip].y: count 1 return count这里有一个容易踩的细节图像坐标系中 y 轴向下指尖朝上时它的 y 值比指根小。所以判断「伸直」的条件是tip.y dip.y反过来写得到的是「弯曲」的判定。但是左右手会影响拇指判定——左手拇指朝右伸、右手拇指朝左伸如果画面经过镜像翻转判定条件也要跟着换方向。常见的做法是固定画面镜像并把拇指判定写为与食指指尖的 x 相对位置比较。4.3 参数调节经验误判多为「指尖与指根太近」实操中最常见的计数不准发生在手指并拢或半弯曲时——指尖和指根的 y 坐标差值接近零阈值判断左右摇摆。我的调整方法是不要只比指根而是比指尖与第二关节比如食指的 6 号点、中指的 10 号点的坐标差因为弯曲时第二关节先动区分度更大增加一个偏移量常数比如if landmarks[tip].y landmarks[dip].y - 0.02用这个 0.02 作为容忍区间避免临界抖动如果还出现误判考虑取最近 5 帧的多数表决而不是每帧独立计数这段逻辑就是整个项目里最值得你花时间改的部分。改好了它你在答辩时能现场演示「我调整了判定阈值现在对半握拳状态也能准确计数」。5. 避坑与常见问题排查毕设答辩前必看的五个真实翻车点5.1 ModuleNotFoundError: No module named cv2现象双击 main.py 或命令行运行直接报错找不到 cv2。原因多数是 opencv-python 没装到当前 Python 解释器所在环境。最常见的情况是——你用pip install opencv-python装到了 base 环境但命令行运行 python 时激活的是另一个 conda 环境。解决conda activate gesture python -m pip install opencv-python python -c import cv2; print(cv2.__version__)先激活目标环境再用python -m pip而不是pip确保装到同一个解释器。5.2 AttributeError: module mediapipe has no attribute solutions现象import mediapipe 成功但调用mp.solutions.hands报错。原因mediapipe 版本过旧或过新导致solutions属性不可用。Python 3.10 以上的环境安装的 mediapipe 新版本曾经出现过 API 变化老代码直接失效。解决先确认版本再决定升级或降级。pip show mediapipe pip install mediapipe0.10.3这个版本是我在多个项目里稳定踩过的点如果你遇到的是solutions相关报错先固定版本再说。5.3 摄像头打不开cap.isOpened() 返回 False现象程序启动后黑屏控制台没有任何报错窗口一直不刷新。原因摄像头被其他程序占用比如 Zoom、微信视频或者笔记本摄像头索引不是 0或者 opencv-python 版本太老不支持当前的摄像头驱动。解决检查占用是第一优先。关掉所有调用摄像头的程序。如果仍然不行把cap cv2.VideoCapture(0)改为cap cv2.VideoCapture(1)。还不行就在代码里加上if not cap.isOpened(): print(摄像头无法打开检查索引号和占用情况) exit()5.4 手指计数总是差一根现象明明伸出四根手指计数显示三或者握拳时显示 1。原因拇指判定方向写反了。画面经过cv2.flip镜像翻转后左右手关系被反转用固定的 x 坐标比较判断拇指方向必然出错。另外手离镜头太近时关键点坐标置信度下降指尖容易抖动。解决拇指判定改为基于手腕点的相对位置。由于项目画面固定为镜像模式可以把手腕0 号点作为参照比较拇指指尖与食指掌根之间的位置关系而不是机械地写x 小于或x 大于。同时把计数结果叠加在画面上实时观察cv2.putText(frame, fFingers: {count}, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)5.5 画面卡顿检测延迟明显动作跟不上现象手快速移动时画面里的关键点拖影严重帧率明显下降。原因mediapipe 的 21 点回归在同一时刻只处理单帧但如果设置的画面分辨率过高1280x720 以上或者电脑没有 GPU模型推理耗时成倍增加。另一个隐性原因是在循环里反复创建 Hands 对象。解决把摄像头分辨率降到 640x480并把 HandTracker 的实例化移出主循环保证hands.process()在每一帧只调用一次。如果仍然卡把min_detection_confidence从 0.5 降到 0.4跳过低置信度的帧不绘制体验提升明显。注意mediapipe 在 CPU 上的推理速度大约是每帧 20~50ms这个项目本身就是为实时视频流设计的。如果你打算处理视频文件做离线测试务必把static_image_mode设为 True否则跟踪模式会对视频中的每一帧做前后文关联出现奇怪的关键点漂移。6. 进阶玩法把计数结果接到你自己的应用流程上当你把 demo 跑通、核心参数调稳之后接下来值得做的一件事把「手指计数」从显示层剥离变成可以被其他代码调用的返回值。这个改动只有十几行但会让你的项目从「演示程序」变成「可集成模块」。常见的接法有三种。第一种是串口输出把手势计数通过 pyserial 发给 Arduino驱动舵机或 LED 灯这在硬件方向的毕设里非常加分。第二种是键盘模拟用 pyautogui 在特定手势时触发快捷键比如比出两根手指就切换窗口。第三种是文件输出把每次手势变化的结果和时间戳写入 CSV作为后续数据分析和可视化展示的素材。我倾向于第三种做数据分析、第二种做交互展示。import csv import time from HandTrackingModule import HandTracker cap cv2.VideoCapture(0) tracker HandTracker() with open(gesture_log.csv, w, newline) as f: writer csv.writer(f) writer.writerow([time, finger_count]) while cap.isOpened(): success, frame cap.read() if not success: break frame tracker.find_hand_landmarks(frame) # 这里的 count_fingers 指向你在第 4 章封装好的计数函数 count count_fingers(tracker.results) writer.writerow([time.time(), count])把结果落盘之后回放数据时你能看到不同手势在时间轴上的连续变化而不是每帧孤立的数字。做毕设报告时Excel 或 matplotlib 画一条时间曲线比单帧截图直观得多也能证明你的代码是持续稳定运行的。我自己的教训是最初做这个项目时只顾着让画面里的数字动起来忽略了对结果做数据化沉淀导致后期写报告时缺少过程性证据临时补录视频费了不少时间。从那以后我每次跑这种视觉 demo都会顺手加一句 CSV 落盘五秒钟的事后面谁问起来都有数据说话。希望这个习惯对你也有用。本文还有配套的精品资源点击获取