简介:基于MediaPipe设计实现的人体姿态识别Python源码与模型项目,适合计算机相关专业正在准备毕业设计的学生、需要项目实战练习的学习者,也可用于课程设计或期末大作业。项目来源于大四毕业设计,经导师指导并审核通过,代码在多个场景下测试运行成功,能直接复现姿态检测与动作分类流程,还可在现有逻辑上替换数据或调整模型参数做二次开发。压缩包共138个文件,包含Python源码、h5模型权重、npy动作数据、mp4演示视频以及README说明文档等,整体体积约11MB,结构清晰、下载与部署都很方便。资源中除主体实现外,还提供动作识别模型与演示录屏,便于对照理解MediaPipe关键点提取、数据预处理和分类推理的完整流程,也可用于答辩展示。目前已有205人学习/下载,适合作为毕设、课设或实践项目的快速起步模板。
1. 人体姿态识别,为什么我劝你先从 MediaPipe 下手
提到人体姿态识别,很多人第一反应是 OpenPose 或者自己从头训一个关键点检测网络。但拿「基于 mediapipe 设计实现人体姿态识别 python 源码+模型」这个标题来说,最务实的路线其实是 MediaPipe BlazePose:它用单目 RGB 摄像头就能输出 33 个人体关键点的 3D 坐标,CPU 上也能跑到实时帧率。这意味着你不用先攒 GPU 机器,也不用面对 COCO 数据集 17 个关键点还得自己补骨骼逻辑的尴尬——MediaPipe 直接给你带拓扑的骨架,拿来就能做角度计算、动作分类、康复计数这些上层应用。
这套方案适合三类人:一是课程设计或毕设需要「能跑通的完整人体姿态识别系统」的学生;二是想在本地摄像头流上快速验证动作识别算法的工程师;三是想低成本给现有产品加一个姿态交互原型的团队。它的精度上限比不上重模型,但胜在部署简单、推理速度快、Python 接口成熟。接下来我从选型理由、环境搭建、源码设计到坑点排查,完整走一遍。
2. 准备 MediaPipe 人体姿态识别环境:版本搭配和依赖避坑
2.1 MediaPipe 与 Python 版本怎么配
MediaPipe 的 Python 包不是所有 Python 版本都支持,这是第一个容易翻车的地方。目前主流搭配是 Python 3.8~3.10 配 MediaPipe 0.10.x,Python 3.11 以上部分版本会出现 wheel 安装失败。我一般直接用 3.9 或 3.10,干净省事。OpenCV 用 opencv-python 4.8 以上,numpy 版本别太新,2.0 以下比较稳,因为 MediaPipe 内部某些操作对 numpy 2.x 的兼容性有问题。
conda create -n pose python=3.10 -y conda activate pose pip install mediapipe==0.10.14 opencv-python numpy==1.26.4这段命令创建了一个独立的 Python 3.10 环境,然后安装 MediaPipe、OpenCV 和指定版本的 numpy。注意我把 numpy 锁在 1.26.4,不是保守——MediaPipe 在 numpy 2.x 下有概率报module 'numpy' has no attribute 'bool8'这类错误,属于典型的版本兼容性翻车。如果你用的是 mac M 系列芯片,conda 会帮你装 arm64 版 Python,MediaPipe 0.10.14 在 macOS arm64 上也是能跑的。
装完验证一下:
import mediapipe as mp print(mp.__version__)如果这里不报错,说明基础环境没问题。常见报错是AttributeError: module 'mediapipe' has no attribute 'solutions',这通常是因为你的 Python 脚本文件名写成了mediapipe.py,把真正的包给遮挡了。改文件名就好,这种低级错误每年坑掉不少人。
2.2 拿到源码和模型文件之后先做什么
标题里的 zip 解压后,一般会包含pose_detection.py、utils.py、pose_landmarks.task或pose_landmarker_lite.task这样的模型文件,以及一个requirements.txt。不要急着运行主程序,先把模型文件路径和源码里的加载逻辑对上。常见结构是项目根目录下建一个models/文件夹放模型,源码里用相对路径引用。
import mediapipe as mp from mediapipe.tasks import python as mp_python from mediapipe.tasks.python import vision model_path = "models/pose_landmarker_lite.task" base_options = mp_python.BaseOptions(model_asset_path=model_path) options = vision.PoseLandmarkerOptions( base_options=base_options, running_mode=vision.RunningMode.VIDEO, num_poses=1, min_pose_detection_confidence=0.5, min_tracking_confidence=0.5, ) landmarker = vision.PoseLandmarker.create_from_options(options)这里我用的 MediaPipe Tasks API,这是目前官方推荐的新接口,比旧的mp.solutions.pose更规范。min_pose_detection_confidence控制的是检测置信度阈值,调低能减少漏检但会增加误检;min_tracking_confidence控制关键点跟踪阈值,这个值调太低会让画面抖动时关键点跳来跳去。num_poses=1表示只跟踪一个人,如果需要多人就改成更大的值,但 CPU 负载会明显上升。
3. 人体姿态识别源码的模块设计:从视频流读到关键点坐标
3.1 视频流读取与逐帧推理的循环框架
源码里的主循环通常长这样:用 OpenCV 打开摄像头或视频文件,每帧转成 MediaPipe 需要的 RGB 格式,送入 landmarker,再把结果画回去。这里有个关键细节:MediaPipe 的RunningMode.VIDEO需要传时间戳,不然会报错。
import cv2 import mediapipe as mp from mediapipe.tasks.python import vision cap = cv2.VideoCapture(0) frame_timestamp_ms = 0 while cap.isOpened(): ret, frame = cap.read() if not ret: break frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=frame_rgb) detection_result = landmarker.detect_for_video(mp_image, frame_timestamp_ms) frame_timestamp_ms += 33 # 约 30fps if detection_result.pose_landmarks: for landmark in detection_result.pose_landmarks: print(landmark[0].x, landmark[0].y, landmark[0].z) cv2.imshow("Pose Detection", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()这段代码的核心是detect_for_video方法,它接收图像和时间戳,返回包含pose_landmarks的结果对象。时间戳必须单调递增,否则 MediaPipe 内部的状态机可能出问题;如果你从视频文件读取,用frame_count * (1000 / fps)计算更准确。landmark[0]对应鼻子,landmark[0].x/y/z是归一化坐标,范围大致在 0~1 之间,画图时需要乘回图像宽高。
3.2 关键点索引映射:33 个点的坐标系统
MediaPipe 输出的 33 个关键点有固定顺序,从 0 到 32。0 是鼻子,1 是左眼内侧,2 是左眼,3 是左眼外侧,4 是右眼内侧,5 是右眼,6 是右眼外侧,7 是左耳,8 是右耳,9 是嘴巴左角,10 是嘴巴右角,11 是左肩,12 是右肩,13 是左手肘,14 是右手肘,15 是左手腕,16 是右手腕,17 是左小指,18 是右小指,19 是左食指,20 是右食指,21 是左拇指,22 是右拇指,23 是左髋,24 是右髋,25 是左膝盖,26 是右膝盖,27 是左脚踝,28 是右脚踝,29 是左脚跟,30 是右脚跟,31 是左足尖,32 是右足尖。
这个索引表是后续所有角度计算和动作判断的基础。比如你要判断「右手抬起来了」,就看关键点 12、14、16 组成的夹角;你要判断「深蹲」,就看关键点 24、26、28 的膝关节角度。记不住没关系,写一个映射字典放源码里,随时查。
LANDMARK_NAMES = [ "nose", "left_eye_inner", "left_eye", "left_eye_outer", "right_eye_inner", "right_eye", "right_eye_outer", "left_ear", "right_ear", "mouth_left", "mouth_right", "left_shoulder", "right_shoulder", "left_elbow", "right_elbow", "left_wrist", "right_wrist", "left_pinky", "right_pinky", "left_index", "right_index", "left_thumb", "right_thumb", "left_hip", "right_hip", "left_knee", "right_knee", "left_ankle", "right_ankle", "left_heel", "right_heel", "left_foot_index", "right_foot_index" ]有了这个表,你就能在调试时打印「right_elbow 的坐标是 (x, y)」而不是冷冰冰的 14 号点。这种可读性在源码阅读和二次开发时非常重要,尤其是你要把姿态识别结果接进上层逻辑时,直接landmark[LANDMARK_NAMES.index("right_elbow")]就行。
3.3 画骨架与可视化:别用 OpenCV 硬连线
MediaPipe 除了给你关键点,还给了一套连接关系,mp.solutions.pose.POSE_CONNECTIONS里有完整的骨架连线定义。用mp_drawing可以直接画出来,如果源码里没带画图模块,你自己用 OpenCV 连线时会漏掉手部细节。
mp_drawing = mp.solutions.drawing_utils mp_pose = mp.solutions.pose if detection_result.pose_landmarks: for pose_landmarks in detection_result.pose_landmarks: mp_drawing.draw_landmarks( frame, pose_landmarks, mp_pose.POSE_CONNECTIONS, mp_drawing.DrawingSpec(color=(0, 255, 0), thickness=2), mp_drawing.DrawingSpec(color=(0, 0, 255), thickness=2), )注意这里传入的pose_landmarks是 MediaPipe 的 NormalizedLandmarkList 对象,不是普通 Python 列表。这个对象可以直接被draw_landmarks接受,省去你手动把归一化坐标映射回图像坐标的步骤。画出来之后,如果发现骨架点对不上人的肢体位置,先怀疑是不是帧率太快导致检测没跟上,而不是怀疑画图代码写错了。
4. 人体姿态识别模型选型:Lite、Full 和 Heavy 怎么选
4.1 三种模型文件的差异
MediaPipe 的姿态识别模型按精度从低到高分为 Lite、Full 和 Heavy 三个版本。Lite 适合移动端和低配设备,Full 是桌面端默认选择,Heavy 精度最高但推理时间也最长。我实测下来,在 i5 处理器上 Lite 大约能跑到 30fps 以上,Full 在 20fps 左右,Heavy 可能掉到 10fps 以下。如果源码里带的是.task文件,注意它的文件名——pose_landmarker_lite.task就是 Lite 版,pose_landmarker_full.task就是 Full 版。
模型精度差异主要体现在小目标检测和遮挡场景。半边身体被桌子挡住时,Lite 会明显丢失关键点,Full 还能硬撑几帧,Heavy 的鲁棒性最好但也要付出实时性代价。如果你的使用场景是健身计数(人站在摄像头前,全身基本可见),Lite 完全够用;如果你是做动作识别,需要捕捉细微的手指动作,至少用 Full。
4.2 模型路径和模型格式的坑
下载模型后,最容易出的问题是路径中包含中文或空格。Windows 上尤其明显——C:\用户\张三\pose.zip\models\pose_landmarker_lite.task这种路径会让 MediaPipe 报RuntimeError: Invalid model asset。解决方法是把项目放在纯英文路径下,或者用Path(__file__).parent动态拼接路径。
from pathlib import Path import mediapipe as mp from mediapipe.tasks import python as mp_python model_path = str(Path(__file__).parent / "models" / "pose_landmarker_lite.task") base_options = mp_python.BaseOptions(model_asset_path=model_path)用Path(__file__).parent获取当前脚本所在目录,再拼接模型路径,这样无论项目被移动到哪个纯英文目录都能运行。注意__file__在交互式 Python 环境里不可用,需要os.getcwd()替代。另一个坑是模型文件和源码版本不匹配:老的.pb格式模型配合新的 Tasks API 会报格式错误,需要用对应的.task文件。如果源码里同时有.pb和.task,优先用.task。
4.3 低配机器上的加速方案
如果电脑跑不满帧率,不要急着换 Heavy 模型,先检查有没有用上 GPU。MediaPipe 默认在 CPU 上推理,加上mp_python.BaseOptions(use_gpu=True)会启用 GPU 加速,但需要 CUDA 环境。没有 GPU 的话,可以缩输入分辨率,从 1280x720 降到 640x480,检测速度能提升一倍以上。
cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)然后把每帧送到 MediaPipe 之前,用cv2.resize再压一遍到 512 或 384 宽度,关键点坐标是归一化的,不会受分辨率影响。精度会损失一点,但在 CPU 上换实时性非常值得,这也是 MediaPipe 方案相比重模型的最大优势——你可以在不换硬件的前提下通过调整分辨率找到性能和精度的平衡点。
5. 人体姿态识别落地避坑:五个必踩的经典问题
5.1 摄像头画面翻转和镜像问题
现象:人往左抬手,画面里关键点却显示往右,或者文字反了。原因:摄像头默认输出是镜像的,MediaPipe 检测出的坐标是基于画面内容的,不会自动纠正。解决:用cv2.flip(frame, 1)做水平翻转,注意翻转要在送入 MediaPipe 之前完成,否则检测到的关键点坐标和你肉眼看到的画面还是对不上。
frame = cv2.flip(frame, 1)5.2 置信度阈值调了半天还是漏检或误检
现象:人明明站在摄像头前,有时检测不到,有时背景里的椅子被当成人体。原因是min_pose_detection_confidence设得太高或太低。这个参数控制的是「是否认为画面里存在一个人」的置信度,调高能减少误检但容易漏检;调低能保证检测到人但可能把非人物体框进来。我一般先设 0.5,如果漏检就降到 0.3,如果误检就升到 0.7。注意还要配合min_tracking_confidence——跟踪置信度影响的是前后帧之间关键点的关联稳定性,调低会让关键点跳动,调高会让目标丢失后重新检测变慢。
5.3 关键点抖动导致计算的角度跳变
现象:手臂静止时计算出的肘关节角度在 90 度到 110 度之间乱跳。原因是单帧检测本身有噪声,直接算角度会把噪声放大。解决方法是加一个滑动窗口滤波,取最近 5 帧关键点坐标的平均值再算角度。不要用卡尔曼滤波,那是应付目标跟踪的,对关键点坐标噪声反而容易过拟合。源码里如果没带平滑逻辑,你加一个队列缓存就能显著改善。
from collections import deque import math angle_buffer = deque(maxlen=5) def calculate_angle(a, b, c): radians = math.atan2(c.y - b.y, c.x - b.x) - math.atan2(a.y - b.y, a.x - b.x) return abs(math.degrees(radians)) def smooth_angle(landmarks, a_idx, b_idx, c_idx): angle = calculate_angle(landmarks[a_idx], landmarks[b_idx], landmarks[c_idx]) angle_buffer.append(angle) return sum(angle_buffer) / len(angle_buffer)5.4 多人场景下 num_poses 设为 1 导致关键点混乱
现象:两个人同时出现在画面里,检测结果只在两个人之间跳来跳去,偶尔一个人身上出现另一个人的手臂。原因是num_poses=1时 MediaPipe 只保留检测置信度最高的一副骨架,当两个人都被部分遮挡时,置信度会交替领先。解决:把num_poses调到 2 或更高,然后在业务逻辑里为每个检测到的人分配唯一 ID。但要注意,MediaPipe 不做跨帧的人体 ID 跟踪,你需要自己维护一个最近邻匹配逻辑,不然每帧返回的人的顺序是乱的。
5.5 OpenCV 和 MediaPipe 的颜色通道顺序不一致
现象:检测出的关键点位置偏移,或者画面颜色诡异。原因是 OpenCV 读进来的是 BGR 格式,MediaPipe 接收的是 RGB 格式。很多新手直接把 OpenCV 的帧传给 MediaPipe,出来的关键点坐标会偏,尤其对肤色和服装颜色敏感的场景更明显。务必先cv2.cvtColor(frame, cv2.COLOR_BGR2RGB),处理完再转回 BGR 画图。这是人体姿态识别里最基础也最容易忽视的坑。
6. 进阶用法:把姿态识别接到动作计数与康复评估里
当你能稳定拿到 33 个关键点坐标后,下一步就是把它变成业务价值。最常见的落地方向是健身动作计数和康复评估。以深蹲为例,你只需要监控左髋、左膝、左脚踝三点的角度变化:下蹲时角度变小,站起时角度变大,设定阈值区间就能判定一个完整动作周期。这个逻辑简单可靠,也是这套源码最值得深挖的点。
角度计算的代码在上面已经给出,关键在于避免硬编码阈值。同一个动作,不同人的身体比例差异很大,深蹲角度阈值对 1.9 米的大个子和 1.5 米的女生完全不是一个量级。我一般会把阈值做成可配置的 JSON 文件,上线前用目标用户的样本来标定。另外别忘了加一个「静止判定」:连续 10 帧关键点坐标变化小于阈值,就认为人已经离开画面或站定不动,这时候重置计数器,否则会出现蹲到一半人走开、回来时计数器跳好几下的尴尬场景。
说到教训,我早期做过一个康复训练项目,用户对着手机做肩部外展动作,我用的是关键点 12、14、16 的角度变化来计数。初版完全没做平滑,结果用户稍微晃一下身体计数器就乱跳。后来加上 5 帧均值滤波和角速度限制——每秒角度变化不能超过 120 度,超过就判定为抖动不计数——效果好了很多。这个经验和封装的思路上说到的点一脉相承:姿态关键点本身的精度只是第一步,真正坑人的是动作语义层面的噪声。
最后说一个提升调试效率的习惯:把关键点坐标和角度实时渲染在画面上,用cv2.putText标注在每个关节点旁边。这样你就能直观看到是检测的问题还是逻辑的问题。我做过这么多人体姿态识别项目,最费时间的永远不是模型推理,而是数据标注和阈值调优。希望这套 MediaPipe 方案能帮你把时间花在真正有价值的业务逻辑上。
本文还有配套的精品资源,点击获取