OpenCV与MediaPipe人体姿态检测:从关键点提取到动作计数实战
简介这是一份面向计算机视觉初学者与开发者的OpenCV与MediaPipe人体姿态检测项目源码帮助读者在Windows环境下快速搭建实时姿态识别功能可应用于人机交互、运动分析与安全监控等场景。资源包共5个文件压缩后约7KB包含Python主程序脚本、依赖清单、说明文档及网页预览文件其中py脚本承载核心检测逻辑txt与md提供环境配置与使用说明html与inscode文件便于快速浏览项目结构。目前已有96人学习下载。通过这份源码读者可掌握姿势检测对象初始化、视频流处理、关键点与连接线绘制、FPS计算显示等完整实现思路并对照可运行代码理解各模块作用适合作为入门实践与二次开发的参考模板。1. 从「能跑」到「能用」OpenCV 与 MediaPipe 人体姿态检测到底解决了什么很多人第一次接触人体姿态检测是拿一张健身照片跑个 Demo看着骨架线叠在人身上觉得挺酷然后就没了。真正落到项目里问题立刻变成视频流能不能实时关键点抖动怎么压遮挡和侧身为什么直接崩OpenCV 与 MediaPipe 这套组合恰好卡在「轻量、可落地、不挑硬件」这个位置上——它不需要独立显卡普通笔记本 CPU 就能跑到实时这让它成为姿态检测从实验室走向工程的第一站。这个标题背后真正要解决的是把一段视频或摄像头流稳定地转成一组带时序的人体关键点坐标并且这套坐标要能直接喂给后续的动作判断、计数或告警逻辑。适合谁做健身计数、坐姿提醒、手势交互、动作比对这类应用的开发者尤其是那些不想一上来就训模型、只想先把业务跑通的人。源码层面的价值不在于代码多长而在于它把「读帧—推理—绘制—输出」这条链路完整串起来了你拿到手改几个参数就能接自己的业务。2. 先搞清楚 MediaPipe Pose 的输出结构再谈怎么用2.1 33 个关键点不是随便排的索引错了后面全废MediaPipe Pose 输出的是 33 个关键点每个点带x、y、z和visibility四个值。x、y是归一化坐标范围 0 到 1不是像素值z是相对深度以髋部中心为原点数值越小表示越靠近镜头visibility是可见性置信度遮挡时这个值会掉下来。很多人第一次画骨架发现线全糊在一起就是因为直接把归一化坐标当像素用了。这 33 个点的索引是固定的常用的几个必须记住0 是鼻子11 和 12 是左右肩13 和 14 是左右肘15 和 16 是左右腕23 和 24 是左右髋25 和 26 是左右膝27 和 28 是左右踝。做动作判断时你基本只关心这十几个。索引写错一位肘部角度算出来就是反的这种错误在调试时非常隐蔽因为画面看起来「差不多对」。import mediapipe as mp mp_pose mp.solutions.pose # 常用关键点索引建议直接定义成常量避免代码里到处写魔法数字 NOSE 0 LEFT_SHOULDER, RIGHT_SHOULDER 11, 12 LEFT_ELBOW, RIGHT_ELBOW 13, 14 LEFT_WRIST, RIGHT_WRIST 15, 16 LEFT_HIP, RIGHT_HIP 23, 24 LEFT_KNEE, RIGHT_KNEE 25, 26 LEFT_ANKLE, RIGHT_ANKLE 27, 28 # 初始化时几个关键参数 pose mp_pose.Pose( static_image_modeFalse, # 视频流必须为 False否则每帧都做检测慢且抖 model_complexity1, # 0 最快最糙1 均衡2 最准最慢 smooth_landmarksTrue, # 开启关键点平滑视频场景必开 min_detection_confidence0.5, # 首次检测阈值 min_tracking_confidence0.5 # 跟踪阈值低于它会重新检测 )static_image_mode是最容易踩的坑。处理视频时如果设成TrueMediaPipe 会退化成「每帧独立检测」不仅慢关键点还会帧间跳变看起来像在抽搐。设成False后它会启用跟踪帧间稳定性提升非常明显。model_complexity从 0 到 2精度和耗时大致是线性关系普通 CPU 上建议先用 1跑不动再降到 0。smooth_landmarks只在视频模式下生效它会对关键点做时间维度的滤波代价是快速动作会有轻微延迟做高帧率动作分析时要权衡。2.2 归一化坐标转像素这一步不做后面全是错的拿到landmark.x和landmark.y之后必须乘以帧的宽和高才能得到像素坐标。这一步看起来简单但有两个细节一是要取整OpenCV 画线和画圆都要求整数坐标二是要判断visibility低于阈值的关键点不应该参与绘制和计算否则遮挡时会出现「幽灵骨架」。import cv2 def to_pixel(landmark, w, h): # 归一化坐标转像素坐标并做边界裁剪 x int(landmark.x * w) y int(landmark.y * h) x max(0, min(x, w - 1)) y max(0, min(y, h - 1)) return x, y def draw_skeleton(frame, landmarks, vis_threshold0.5): h, w frame.shape[:2] # 只画可见性达标的关键点 for idx, lm in enumerate(landmarks.landmark): if lm.visibility vis_threshold: continue cx, cy to_pixel(lm, w, h) cv2.circle(frame, (cx, cy), 4, (0, 255, 0), -1) return framevis_threshold设多少要看场景。室内正面拍摄0.5 够用侧身或快速运动可以降到 0.3但会引入更多噪声点。我的经验是先用 0.5 跑一遍看哪些点在动作过程中频繁闪烁再针对性调整。边界裁剪那两行别省摄像头边缘的人体关键点偶尔会算出负数或超出宽高的值不裁剪的话cv2.circle会直接报错或者画到画面外。3. 用 OpenCV 把摄像头流接进 MediaPipe 的最小闭环3.1 读帧、转换、推理、绘制四步顺序不能乱整条链路是cv2.VideoCapture读帧BGR 转 RGB送进pose.process拿结果绘制再imshow显示。顺序错一步就出问题最常见的是忘了 BGR 转 RGB导致检测结果偏移或者干脆检测不到人因为 MediaPipe 内部按 RGB 处理。import cv2 import mediapipe as mp mp_pose mp.solutions.pose mp_draw mp.solutions.drawing_utils cap cv2.VideoCapture(0) # 0 是默认摄像头换成视频路径就是离线处理 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) with mp_pose.Pose( static_image_modeFalse, model_complexity1, smooth_landmarksTrue, min_detection_confidence0.5, min_tracking_confidence0.5 ) as pose: while cap.isOpened(): ret, frame cap.read() if not ret: break # OpenCV 读进来是 BGRMediaPipe 要 RGB rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) rgb.flags.writeable False # 只读减少一次内存拷贝 results pose.process(rgb) rgb.flags.writeable True if results.pose_landmarks: mp_draw.draw_landmarks( frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, landmark_drawing_specmp_draw.DrawingSpec(color(0, 255, 0), thickness2, circle_radius3), connection_drawing_specmp_draw.DrawingSpec(color(255, 0, 0), thickness2) ) cv2.imshow(Pose, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()rgb.flags.writeable False这行是官方示例里的优化作用是告诉 MediaPipe 这块内存只读避免内部再做一次拷贝实测能省几个毫秒。waitKey(1)里的 1 是毫秒别设太大否则画面卡顿按q退出是惯例。draw_landmarks的两个DrawingSpec分别控制关键点和连接线的颜色粗细调试阶段建议把关键点画大一点方便观察哪些点在抖。3.2 分辨率与帧率的取舍640×480 是甜点区摄像头分辨率直接决定推理耗时。1080p 下单帧推理可能是 640×480 的四倍以上而姿态检测对分辨率并不敏感640×480 已经足够识别全身关键点。如果业务需要更精细的手部或面部再单独提高分辨率不要为了「看起来清晰」把整条链路拖慢。分辨率单帧推理耗时CPU复杂度1适用场景320×240约 8-12ms低配设备、高帧率需求640×480约 15-25ms通用场景推荐起点1280×720约 40-60ms需要精细关键点1920×1080约 80-120ms离线处理不追求实时这张表是经验值具体数字因 CPU 型号差异很大但比例关系是稳定的。如果你的业务要求 30fps单帧预算就是 33ms640×480 加复杂度 1 基本能卡住1080p 就别想了。离线处理视频时没有实时压力可以上高分辨率加复杂度 2把精度拉满。4. 关键点抖动、遮挡与侧身三个最常翻车的地方4.1 抖动不是模型问题是滤波没做对即使开了smooth_landmarks快速动作时关键点仍会有明显抖动尤其是手腕和脚踝这些末端点。原因是 MediaPipe 的平滑是轻量的它优先保证低延迟。如果你的业务对稳定性要求高比如坐姿监测可以在它之上再加一层滤波。import numpy as np class LandmarkSmoother: def __init__(self, alpha0.6): # alpha 越大越跟随原始值越小越平滑但延迟越大 self.alpha alpha self.prev None def smooth(self, landmarks): current np.array([[lm.x, lm.y, lm.z] for lm in landmarks.landmark]) if self.prev is None: self.prev current return current # 指数移动平均 smoothed self.alpha * current (1 - self.alpha) * self.prev self.prev smoothed return smoothedalpha取 0.6 左右是个平衡点再低会有明显拖影做动作计数时可能导致峰值被削掉。这个滤波要放在归一化坐标上做不要放在像素坐标上否则分辨率一变参数就失效。另外注意滤波后的坐标不要再写回results.pose_landmarks而是单独维护一份用于业务计算绘制时可以用原始值避免视觉上的延迟感。4.2 遮挡和侧身的处理策略降级而不是硬扛人体被遮挡时MediaPipe 不会「猜」出关键点而是把visibility压低。这时候如果业务逻辑还在用这些点算角度结果就是错的。正确做法是对每个关键点设可见性阈值低于阈值的点标记为无效涉及无效点的计算直接跳过或返回「未知」状态。侧身是另一个难点。侧身时左右两侧的关键点会重叠z值变得不可靠基于左右对称的动作判断会失效。常见做法是判断肩宽和髋宽的像素距离如果小于某个阈值就认为当前是侧身姿态切换到只依赖单侧关键点的判断逻辑。这个阈值需要根据拍摄距离标定没有通用值。提示不要试图用插值去「补」遮挡的关键点除非你的业务能接受误差。姿态检测的工程原则是「宁可报未知不要报错误」。4.3 多人场景MediaPipe Pose 只管一个人这是最容易被忽略的限制。mp.solutions.pose是单人模型画面里有多个人时它只会输出置信度最高的那个人的关键点而且会在人之间跳来跳去。如果你的业务是多人场景需要先用检测模型框出每个人再对每个框单独跑 Pose或者换用支持多人的方案。源码里如果没处理这个多人场景下骨架会乱跳这不是 bug是模型本身的定位。5. 避坑与排查那些让骨架画不出来的原因5.1 画面全黑或没有骨架现象imshow有画面但没有任何骨架线。原因通常是 BGR 没转 RGB或者results.pose_landmarks为None时没做判断直接绘制。解决先确认cv2.cvtColor那行存在再在绘制前加if results.pose_landmarks:判断。如果转了 RGB 还是没骨架检查min_detection_confidence是不是设太高调到 0.3 试试。5.2 骨架帧间闪烁、关键点跳变现象关键点在相邻帧之间位置突变。原因一是static_image_mode设成了True二是min_tracking_confidence设太高导致频繁重新检测。解决视频场景static_image_mode必须为Falsemin_tracking_confidence降到 0.3-0.5 之间。如果还抖加一层 4.1 节的滤波。5.3 推理速度突然变慢现象跑了几分钟后帧率明显下降。原因通常是摄像头缓冲区堆积cap.read()读到的是旧帧。解决在循环里加cap.grab()丢弃缓冲或者把cv2.CAP_PROP_BUFFERSIZE设为 1。另一个可能是内存泄漏检查有没有在循环里反复创建Pose对象正确做法是用with语句只创建一次。5.4 关键点坐标超出画面范围现象cv2.circle报错或骨架画到画面外。原因是归一化坐标乘以宽高后可能得到负数或超过边界的值尤其是人体部分出画时。解决在转像素坐标时做max(0, min(x, w-1))裁剪如 2.2 节代码所示。5.5 摄像头打不开或读到空帧现象cap.isOpened()返回False或ret一直是False。原因可能是摄像头被其他程序占用或者索引不对。解决换索引试 0、1、2关闭其他占用摄像头的程序。Linux 下还要检查当前用户有没有视频设备权限。离线视频则检查路径和编码格式OpenCV 对某些编码支持不好可以先用 ffmpeg 转成 H.264 再读。6. 从关键点到业务角度计算与动作计数的落地技巧骨架画出来只是第一步真正产生价值的是从关键点里算出业务指标。最常用的是关节角度比如肘部角度、膝部角度用三点坐标算向量夹角即可。import numpy as np def calc_angle(a, b, c): # a、b、c 是三个关键点的像素坐标b 是顶点 a, b, c np.array(a), np.array(b), np.array(c) ba a - b bc c - b # 防止零向量导致除零 norm np.linalg.norm(ba) * np.linalg.norm(bc) if norm 0: return None cosine np.dot(ba, bc) / norm cosine np.clip(cosine, -1.0, 1.0) # 浮点误差可能让 cosine 略超范围 return np.degrees(np.arccos(cosine))np.clip那行是血泪经验浮点运算会让cosine偶尔变成 1.0000001arccos直接返回nan然后整个角度计算链路崩掉。这个错误在特定帧才出现极难复现加个裁剪就解决了。动作计数的通用套路是「状态机 阈值」。以俯卧撑为例肘部角度从大于 160 度降到小于 90 度再回到大于 160 度算一次完整动作。关键是设两个阈值形成迟滞区间避免在临界值附近反复触发。class RepCounter: def __init__(self, down_thresh90, up_thresh160): self.down_thresh down_thresh self.up_thresh up_thresh self.state up self.count 0 def update(self, angle): if angle is None: return self.count if self.state up and angle self.down_thresh: self.state down elif self.state down and angle self.up_thresh: self.state up self.count 1 return self.countdown_thresh和up_thresh之间的差值就是迟滞区间差值太小会重复计数太大则漏计。俯卧撑场景下 90 和 160 是比较稳的组合但不同人身高臂长不同最好让用户先做一次标定记录他的最大伸展角度和最小收缩角度再按比例设阈值。验证计数是否准确最直接的办法是把关键点角度随时间的变化曲线打出来人工对照视频看每次计数对应的角度波形是否完整。如果波形有毛刺导致误触发就在角度上再加一层中值滤波。这个调试过程没有捷径但一旦调好同一套参数在相似拍摄条件下是稳定的。我自己的习惯是任何姿态检测项目先把关键点可视化和角度曲线这两个调试面板做出来再写业务逻辑。看起来多花半天但后面省下的排查时间远不止半天。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

高速低功耗工控外扩SRAM存储芯片存储解决方案

高速低功耗工控外扩SRAM存储芯片存储解决方案

EMI外扩SRAM存储芯片2Mb快速异步SRAM为2,095,152位,提供两种组织:128K16与256K8。前者地址线A16A0,后者A17A0,数据位宽分别为16位与8位,均支持UB/LB高低字节控制。 EMI高速外扩SRAM存储芯片访问时间分三档:…

2026/10/14 3:45:39 阅读更多 →
智能体Skills技能包开发实战:从结构设计到调试全解析

智能体Skills技能包开发实战:从结构设计到调试全解析

我研究了大半年的智能体开发,今天想聊聊一个容易被忽略、但直接影响智能体上限的东西——Skills。什么是Skills?在智能体生态里,Skills通常被称为“技能包”,它是一套标准的配置与代码集合,让助手不再只会“聊”&#…

2026/10/14 3:45:39 阅读更多 →
从内容洪流到可信资产:内部内容治理与智能分发全流程解析

从内容洪流到可信资产:内部内容治理与智能分发全流程解析

“在大厂内部广泛传播的内容,谨慎传播”这话我太熟了。干过团队内容治理的人都知道,真正值钱的不是某一篇被转来转去的文章,而是把散落在群聊、收藏夹、网盘、邮件里的信息,沉淀成一套可以检索、可以分级、可以追溯的内容资产。这…

2026/10/14 3:45:39 阅读更多 →

最新新闻

Dropout 正则化原理与实战:从过拟合到泛化能力提升

Dropout 正则化原理与实战:从过拟合到泛化能力提升

1. 为什么一个"随手关掉神经元"的操作能成为深度学习的基石第一次在代码里写下nn.Dropout(p0.5)的时候,我其实没太当回事——不就是训练时随机扔掉一半神经元嘛,听起来像个正则化的小把戏。直到有一次复现一篇图像分类论文,模型在训…

2026/10/14 4:24:51 阅读更多 →
从60到150 t/s:Strata推理栈与INT4量化在48G显存上的实战调优

从60到150 t/s:Strata推理栈与INT4量化在48G显存上的实战调优

1. 先说说那三秒:149.7 t/s 砸到眼前那天晚上我蹲在测试机前面,盯着终端里不断滚动的数字,一时没回过神来。屏幕上清清楚楚写着“149.7 t/s”。按标题里的说法,就是那套组合——内部代号 Strata 的推理栈,加上一个叫 q…

2026/10/14 4:24:51 阅读更多 →
Java毕设实战:基于爬虫的题库自动生成系统完整解析

Java毕设实战:基于爬虫的题库自动生成系统完整解析

简介:毕设项目-利用爬虫的题库自动生成系统 是面向计算机相关专业本科生毕业设计场景的完整工程,适用于需要快速搭建可运行题管理平台的Java开发者。针对题库建设中题目来源单一、手工录入效率低等问题,整体以Java为后端核心、Vue为管理端页面…

2026/10/14 4:24:51 阅读更多 →
机械原理课程设计洗瓶机:凸轮机构实现推头匀速回程三倍速

机械原理课程设计洗瓶机:凸轮机构实现推头匀速回程三倍速

简介:基于机械原理的洗瓶机课程设计资料面向机械专业本科生,完整呈现从功能分解、运动方案设计到执行机构选型的全过程,适合完成课程设计或复习机构学知识时参考。内容围绕瓶子推进、旋转与刷洗三大动作展开,明确了推进距离600mm、…

2026/10/14 4:24:51 阅读更多 →
旅游线路查询接口分页漂移导致数据抓取错位的排查与规避

旅游线路查询接口分页漂移导致数据抓取错位的排查与规避

1. 一个看似普通的查询需求,怎么就翻车了事情是这样的,前阵子帮一个做旅游内容聚合的朋友看他们后台的数据对账问题。他们有一个模块是抓取某旅游线路平台(就是那种典型的“黑马旅游网”风格的线路查询站点)的线路数据&#xff0c…

2026/10/14 4:24:51 阅读更多 →
西门子RPC开发实战:从通信接口到产线落地的完整路径

西门子RPC开发实战:从通信接口到产线落地的完整路径

简介:这份西门子RPC开发功能文档面向工业自动化领域的开发者、设备集成工程师与数控系统调试人员,聚焦SINUMERIK 840D/840Di/810D平台下远程过程调用的接口规范与开发方法。文档系统梳理了主机接口、PLC/NCK接口、HMI与NCK/PLC的OEM交互接口等核心模块&a…

2026/10/14 4:23:50 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →