简介:该代码包是面向Python开发者的OpenCV与MediaPipe实时人体姿态检测工程,适合正在学习计算机视觉,或需要在项目中快速接入人体关键点识别功能的读者。压缩包仅7KB,共5个文件,包含Python主程序、txt依赖清单、Markdown说明、HTML展示页与inscode配置文件,结构清晰,可直接参考运行。资源围绕完整检测流程展开,读者可从中学习初始化姿态检测模型、读取摄像头视频流、绘制人体关键点与骨骼连线、统计并显示FPS等核心步骤,同时配有环境安装与命令行验证说明,便于从零复现。对于已有基础的开发者,也能快速提取代码片段并迁移到自己的项目中;代码量不大,但关键点覆盖完整,适合作为人体姿态识别项目的起步模板。目前已有89人学习下载,是一份入门门槛低、实用性强的姿态检测项目源码。
1. 项目概述
1.1 这套源码能做什么
先说我为什么做这个项目。去年年中接手了一个体感交互的活,甲方要求通过普通摄像头识别人体姿态,用来做互动大屏展示。最开始考虑过用深度相机,但对方预算有限,还得兼容他们现有的USB摄像头。后来我翻了一圈方案,发现OpenCV加MediaPipe这对组合是最务实的路子,于是就有了这套源码。
这套项目源码解决的核心问题就三个:第一,用普通RGB摄像头实时识别人体33个关键点;第二,把关键点坐标转成可视化的骨架连线,叠加到视频帧上;第三,通过关键点之间的几何关系判断简单动作,比如举手、下蹲、左右摇摆。说白了就是用最便宜的硬件方案,做到接近专业动捕设备七八成的效果。
适合参考这套代码的人包括:正在学习计算机视觉的学生、想快速给项目加人体交互功能的开发者、做体感互动装置的创客。不需要你懂深度学习原理,只要会Python基础语法,就能把整个流程跑起来。
1.2 技术选型背后的考量
技术选型这块我纠结过一段时间。当时市面上人体姿态检测的主流方案有OpenPose、MediaPipe、MoveNet、PoseNet四类。我逐一踩过坑之后才锁定MediaPipe,原因有三点:
OpenPose精度确实高,但模型体积大得吓人,CPU上跑起来特别吃力,CPU占用直接拉满,风扇狂转,而且配置环境过程极其折磨,光是依赖库就能让你装一晚上。MoveNet是TensorFlow家的,速度和精度平衡不错,但关键点只有17个,而且TensorFlow Lite的部署链路比较绕。PoseNet则是老一代方案,精度和速度放到今天都已经不够看了。
MediaPipe的优势在于:关键点数量多,人体有33个,手部有21个,这个密度对后续做动作分析非常关键;端到端的pipeline封装完善,不需要自己写图像预处理;CPU推理速度够快,普通笔记本跑30帧稳定,GPU都快溢出了。
OpenCV在这里扮演的是搭档角色——负责图像采集、格式转换、画面绘制、视频输出。MediaPipe负责姿态估计,OpenCV负责前后端这些脏活累活,两者各管一段,代码结构清晰,扩展性好。
2. 环境搭建与依赖处理
2.1 版本选择是个大学问
这套源码在Python 3.8到3.10之间测试过,建议别用3.11以上的版本,原因后面说。依赖库很少,核心就三个:
pip install opencv-python pip install mediapipe pip install numpy这里插一句,光是opencv-python这个包,很多新手会在装的过程中栽跟头。如果遇到ModuleNotFoundError: No module named 'cv2',大概率是环境没激活,或者pip装到了系统的全局Python里。建议用虚拟环境,别嫌麻烦。
python -m venv pose_env source pose_env/bin/activate # Windows下用 pose_env\Scripts\activate pip install opencv-python mediapipe numpy关于MediaPipe的版本,我强烈建议固定版本号,别用最新版。目前实测最稳的组合是mediapipe==0.10.9,这个版本对Python 3.10支持得很好,而且API稳定,网上大部分教程都基于这个版本写的。
顺带提一句老生常谈的问题:OpenCV的GPU版本。cuDNN和CUDA编译OpenCV,确实能大幅提升图像处理的吞吐量,但姿态估计的推理部分跑在MediaPipe里,这部分对CUDA的依赖不强。所以普通项目直接装CPU版就够了,没必要折腾CUDA编译那堆事。真正的性能瓶颈在MediaPipe推理,不在OpenCV的图像处理。
2.2 摄像头与视频源的兼容性
源码里写了一个通用的视频源初始化函数,既能打开USB摄像头,也能读本地视频文件。这样你在开发调试的时候,不必每次开着摄像头对着自己尬舞,可以用录好的视频反复测试。
import cv2 def init_capture(source=0): cap = cv2.VideoCapture(source) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) cap.set(cv2.CAP_PROP_FPS, 30) if not cap.isOpened(): raise IOError(f"无法打开视频源: {source}") return cap摄像头初始化这一行,cap.set的参数设置顺序有讲究。我见过不少人在网上提问为什么设了分辨率不生效,其实是因为部分摄像头驱动必须把宽高设置放在isOpened()检查之后,或者需要先cap.open()再设置参数。最稳妥的做法是像上面这样,先创建对象,再设参数,最后检查状态。另外,有些摄像头默认输出是YUYV格式,OpenCV里自动转换没问题,但如果画面颜色异常偏绿偏紫,就需要用cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))强制切成MJPG格式。
3. 核心实现逻辑拆解
3.1 MediaPipe姿态检测管线的完整流程
MediaPipe的检测流程比很多人想象中的要复杂,但封装得好,你只管调接口就行。整个管线分三阶段:输入帧预处理、姿态估计模型推理、输出后处理。
输入帧预处理这块,MediaPipe接受RGB格式的图片。但OpenCV读出来的是BGR,这是一个极其经典的坑。每次都是RGB、BGR搞混,导致画面里人物像中毒了一样,蓝色变橙色。所以在送入检测器之前必须做一次颜色空间转换。
姿态估计模型推理是核心部分,MediaPipe内部用的是BlazePose模型,一个轻量级卷积神经网络架构。它分为两段:第一段做人物检测,先找到画面里的人在哪儿,框出人体区域;第二段对该区域做关键点回归,输出33个关键点的三维坐标(x, y, z)和可见度。两段式设计的目的是提速,第一段可以跳过背景区域,不用每帧都跑全图检测。
输出后处理是把归一化坐标还原成图像坐标。MediaPipe输出的x和y都是归一化坐标,范围0到1,需要乘以画面的宽和高,才能得到具体的像素位置。z坐标是深度信息,表示该关键点相对臀部中心点的距离,这个量级和人体的宽度有关,可以直接用来做前后倾的判断。
下面是核心检测代码:
import cv2 import mediapipe as mp mp_pose = mp.solutions.pose pose = mp_pose.Pose( static_image_mode=False, model_complexity=1, smooth_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) def process_frame(frame): rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) rgb.flags.writeable = False # 提升推理性能 results = pose.process(rgb) return resultsrgb.flags.writeable = False这行是很多教程忽略的细节。MediaPipe内部做了性能优化,当你把数组设为只读时,推理过程中能减少一次数据拷贝。实测下来,这行代码能让FPS提升3到5帧左右。
还有model_complexity这个参数,可选0、1、2。0最轻量速度最快但精度低,2最重精度最高但速度慢。我的实测数据是:复杂度0在CPU上跑约30帧,复杂度1约20帧,复杂度2直接掉到10帧以下。所以普通项目选1就够了。
3.2 关键点可视化与骨架绘制
拿到关键点坐标之后,要画到画面上。直接画点当然简单,但更直观的是画出骨架连线。MediaPipe官方文档里给出了每个人体部位的连接关系,我把它们整理成两组列表:一组是12个上半身关键点,一组是18个上下半身混合的关键点。
LANDMARK_CONNECTIONS = [ (11, 12), (11, 13), (13, 15), (12, 14), (14, 16), # 双臂 (11, 23), (12, 24), (23, 24), # 躯干 (23, 25), (25, 27), (24, 26), (26, 28), # 双腿 (15, 17), (15, 19), (15, 21), # 左手 (16, 18), (16, 20), (16, 22), # 右手 ]画线的时候,我习惯用cv2.line加抗锯齿,线条粗细和颜色可以自定义。关键点用cv2.circle实心圆绘制,左右半身用不同颜色区分,这样调试时一眼就能看出来左右是否识别反了。
def draw_landmarks(frame, landmarks, h, w): for idx, lm in enumerate(landmarks): x, y = int(lm.x * w), int(lm.y * h) color = (0, 255, 0) if idx % 2 == 0 else (0, 165, 255) cv2.circle(frame, (x, y), 3, color, -1, cv2.LINE_AA) for a, b in LANDMARK_CONNECTIONS: x1, y1 = int(landmarks[a].x * w), int(landmarks[a].y * h) x2, y2 = int(landmarks[b].x * w), int(landmarks[b].y * h) cv2.line(frame, (x1, y1), (x2, y2), (255, 255, 255), 2, cv2.LINE_AA) return frame这里有个细节要注意,access landmarks[a]之前,先判断该关键点的可见度visibility是否大于0.5。因为MediaPipe在人物部分被遮挡时,会输出不可靠的坐标,如果直接画上去,骨架会出现各种诡异扭曲。加一个过滤条件能有效减少这个问题。
4. 完整项目源码解析
4.1 主循环与关键参数计算
整个项目的主循环负责三件事:读帧、检测、绘制。逻辑不复杂,但性能优化上有些细节值得说。
import time import cv2 import mediapipe as mp import numpy as np mp_pose = mp.solutions.pose mp_drawing = mp.solutions.drawing_utils mp_drawing_styles = mp.solutions.drawing_styles def main(): cap = init_capture(0) pose = mp_pose.Pose( static_image_mode=False, model_complexity=1, smooth_landmarks=True, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) fps_time = 0 frame_count = 0 fps = 0 while cap.isOpened(): success, frame = cap.read() if not success: break frame = cv2.flip(frame, 1) # 镜像翻转,交互场景更自然 h, w = frame.shape[:2] results = process_frame(frame, pose) if results.pose_landmarks: # 绘制关键点和骨架 draw_landmarks(frame, results.pose_landmarks.landmark, h, w) # 计算身体倾斜角度 angle = calculate_tilt_angle(results.pose_landmarks.landmark, w, h) cv2.putText(frame, f"Tilt: {angle:.1f} deg", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) # 显示FPS frame_count += 1 if frame_count >= 10: fps = frame_count / (time.time() - fps_time) frame_count = 0 fps_time = time.time() cv2.putText(frame, f"FPS: {fps:.1f}", (50, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.imshow("Pose Detection", frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()镜像翻转cv2.flip(frame, 1)这里多说一句,这个功能在普通动作识别项目里可加可不加,但在交互场景里必须加。因为摄像头对着你,画面里你的左右手是反的,你要抬右手,屏幕上却是左手抬起来,这对用户体验是致命的。加上镜像翻转后,画面像照镜子一样,符合直觉。
倾斜角度计算这个功能是我后加的,它通过两个肩部关键点和两个髋部关键点的坐标,计算身体相对于垂直轴的倾斜角度。这类计算在动作判断里很常见,所以单独抽了一个函数。
4.2 手势识别与动作判断扩展
人体姿态检测的下一层需求就是动作判断。比如做手势交互,判断举手、挥手这些。MediaPipe还有个专门的手部检测模块,mp.solutions.hands,能识别21个手部关键点,两者可以同时跑。
mp_hands = mp.solutions.hands hands = mp_hands.Hands( static_image_mode=False, max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5 ) def detect_hand_gesture(hand_landmarks): # 提取指尖和指根坐标 tips = [4, 8, 12, 16, 20] # 拇指到小指的指尖关键点索引 bases = [2, 5, 9, 13, 17] # 对应的指根关键点索引 fingers_up = [] for tip, base in zip(tips, bases): # 比较指尖和指根的y坐标(归一化坐标,注意y轴向下) if hand_landmarks.landmark[tip].y < hand_landmarks.landmark[base].y: fingers_up.append(1) else: fingers_up.append(0) # 手势判断 if fingers_up == [1, 1, 1, 1, 1]: return "OPEN_PALM" elif fingers_up == [0, 1, 1, 1, 1]: return "FOUR" elif fingers_up == [0, 1, 0, 0, 0]: return "POINTING" elif fingers_up == [0, 0, 0, 0, 0]: return "FIST" return "UNKNOWN"手部检测和姿态检测并行跑,性能会有明显下降,CPU直接翻倍。我的建议是:如果你的应用场景只需要手势,就别开姿态检测;如果两个都要,尽量把输入帧分辨率降到640x480,能省不少计算量。
还要注意一个坑,手部关键点的坐标归一化方式和人体一样,但在检测到手之前,hand_landmarks是空的,所以调用前必须先判断非空。MediaPipe的手部检测在背景复杂时容易误判,建议把min_detection_confidence调高到0.6以上。
5. 常见问题与排查技巧实录
5.1 我踩过的坑和解决办法
这套源码我前前后后迭代了四五个版本,踩过的坑整理一下,基本覆盖网上被问最多的问题。
问题一:MediaPipe检测不到人
症状是画面里有人,但results.pose_landmarks一直是空。排查思路从易到难:先确认摄像头画面正常、人物在画面里完整可见;再看光线,光线不足时检测率骤降;最后检查模型参数,min_detection_confidence设置太高也会导致检测不到。实测中把阈值从0.7降到0.5,检测率有明显提升,误检率也没有显著增加。
问题二:画面卡顿,FPS只有个位数
先别急着怪算法,检查一下是不是分辨率设太高了。1280x720和640x480的性能差距是成倍的。建议开发阶段用640x480跑通逻辑,上线前再根据目标机型的算力调整。另外,关闭cv2.imshow的实时预览能大幅提升帧率,因为窗口渲染本身消耗不小。
问题三:人体关键点抖动
MediaPipe本身有smoothen_landmarks参数,默认是开的,但实际效果有限。如果想进一步平滑,可以在输出端做一阶低通滤波,也就是对每个关键点的坐标做指数平滑:
smoothed = alpha * current + (1 - alpha) * previousalpha一般取0.3到0.5之间。太小响应慢,太大没效果。我在做交互项目时用0.4,既不觉得迟滞,骨架也不抖。
问题四:最容易被忽略的小毛病
cv2.waitKey(1)参数填0会导致界面卡住不刷新,这个很多人上课都讲过,但还是有人踩。再一个是视频帧的格式,MediaPipe只接受连续的三通道RGB图,如果你从视频文件读出来的帧是灰度图或带透明通道的图,记得先转换。
5.2 性能对比与调优建议
用i5-1240P这个档次的CPU笔记本实测,640x480输入,model_complexity=1,关掉窗口预览的情况下,稳定在25到30帧。开了窗口预览,掉到18到22帧。如果换model_complexity=0,能到35帧以上,但腿部关键点的精度明显下降,尤其是做下蹲动作时,膝盖位置经常飘。
如果对精度要求高且预算允许,可以考虑Intel的OpenVINO插件来加速MediaPipe,但配置略麻烦。普通场景下,最好的优化策略就是降分辨率、降模型复杂度、减少不必要的绘制操作,这三板斧下来基本能翻倍帧率。
5.3 项目后续还能怎么扩展
这套源码的扩展方向我梳理一下:
- 动作计数:结合关键点角度变化,判断深蹲、俯卧撑的完成次数
- 交互控制:用左右手关键点位置映射鼠标坐标,实现隔空操作电脑
- 跌倒检测:计算人体中心点的高度和速度,瞬时突变则触发报警
- 健身辅助:对比标准动作骨架和用户骨架,实时指导姿势纠正
- 多路视频源:同时接入多个摄像头,做多视角的姿态融合
我做这套源码时最大的体会是:MediaPipe把姿态估计的门槛拉得非常低,真正拉开差距的是你如何利用这33个关键点做有价值的应用。骨架数据拿到手之后,角度计算、距离计算、比例计算这些几何分析才是核心玩法。比如判断一个人是否含胸驼背,只需要计算肩膀和髋部连线的偏转角;判断是否在挥手,只需要看手腕关键点在时间轴上的位移变化。这些能力配合简单的阈值判断,就能组合出很多实用功能。
本文还有配套的精品资源,点击获取