ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Mediapipe姿态识别与匹配计算:从关键点提取到角度相似度实战

Mediapipe姿态识别与匹配计算:从关键点提取到角度相似度实战 简介一套基于Mediapipe的人体姿态识别与匹配计算Python项目源码面向计算机相关专业学生、开发者适用于毕业设计、课程设计及动作比对学习场景。资源共11个文件包含5个Python源码主程序、姿态匹配、帧率检测等、4个JSON数据文件标准动作与实时动作序列、1个Markdown项目使用说明及1个测试文件压缩包仅70KB目录结构清晰便于快速定位代码模块。代码提供超详细注释内置标准动作序列pose_std.json可调用摄像头实时捕捉人体关键点通过dtaidistance等库完成动作相似度匹配当偏差较大时画面左上角显示红色WRONG提示方便直观理解姿态识别与匹配计算全过程。项目已在Python 3.7.6、Mediapipe 0.8.3环境下测试通过可作为计算机视觉方向入门进阶的实践参考也可直接在此代码基础上扩展二次开发。目前已有1013人学习下载。1. 姿态识别只是前半场真正的活在于“匹配”拿到一份“基于Mediapipe的人体姿态识别与匹配计算”的 Python 源码先别急着跑 demo。姿态识别pose estimation在 Mediapipe 里已经是成熟到接近“开箱即用”的程度——模型负责从一帧图像里吐出一组关键点坐标而真正的工程难点也是这份代码的核心价值落在“匹配计算”四个字上怎么把这组坐标变成有意义的比较结果比如两个人的动作是否相似、当前动作和标准动作差多少、两个姿态序列的相似度是多少。这背后涉及坐标系的归一化、特征向量的构造、相似度度量方法的选择以及阈值怎么定才不至于误判。本文会从 Mediapipe Pose 的 landmark 输出结构讲起落到一段可直接使用的 Python 匹配计算代码再给出参数调整方向和调试建议。适合已经跑通过 Mediapipe 基础 demo、想把姿态数据用于动作比对或评分场景的开发者。2. Mediapipe Pose 的关键点输出与特征提取2.1 为什么选 Mediapipe 而不是 OpenPose 或 mmposeMediapipe Pose 的核心优势不在“精度绝对最高”而在“推理速度和工程便利性的平衡”。它基于 BlazePose 架构在 CPU 上也能跑到接近实时的帧率Python 侧通过mediapipe包暴露了极简 API几行代码就能拿到 33 个关键点的归一化坐标。对“姿态识别与匹配计算”这个标题来说选择 Mediapipe 还有一个更实际的考量它的 landmark 坐标统一做了归一化处理x、y 值都在 [0, 1] 区间这给后续的距离和角度计算省掉了图像尺寸换算的麻烦。相比之下OpenPose 的关键点输出质量高但环境配置重、推理慢mmpose 灵活但需要自己处理数据流和模型权重。如果是做实时交互、动作比对原型、或者需要快速验证匹配算法的正确性Mediapipe 是最省事的选择。这里提到的“选择理由”在源码里通常体现为mp.solutions.pose的初始化和 inference 循环你看代码时关注两点即可model_complexity和min_detection_confidence这两个参数直接决定速度和精度。import mediapipe as mp import cv2 mp_pose mp.solutions.pose pose mp_pose.Pose( static_image_modeFalse, model_complexity1, smooth_landmarksTrue, min_detection_confidence0.5, min_tracking_confidence0.5 ) cap cv2.VideoCapture(0) while cap.isOpened(): ret, frame cap.read() if not ret: break rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results pose.process(rgb) if results.pose_landmarks: for idx, lm in enumerate(results.pose_landmarks.landmark): # 这里拿到的是归一化坐标x 和 y 范围均为 [0, 1] print(idx, lm.x, lm.y, lm.z, lm.visibility) cap.release()这段代码展示了 Mediapipe Pose 的最小推理流程。model_complexity1对应模型体积和精度的中间档0是最轻量版2是精度最好但最慢的版本做匹配计算时建议优先用1或2因为关键点抖动会直接影响匹配结果。smooth_landmarksTrue会对时域上的关键点做平滑对视频流匹配很有用但对单帧匹配反而可能引入延迟这个后面会细说。lm.z的数值不是真实深度而是以臀部中心为原点的相对深度估计幅度很小通常不用于匹配计算lm.visibility表示该关键点在被遮挡情况下的置信度低于 0.5 的关键点建议在匹配时直接丢弃或降权。2.2 33 个关键点的分组与选择策略Mediapipe 输出的 33 个关键点覆盖全身但从匹配计算的角度看不是所有点都值得进特征向量。手部关键点和面部关键点在很多姿态识别任务里是干扰项——它们抖动大、视觉显著性低而且对不同体型的人差异明显。我一般会按身体部位把关键点分成三组躯干核心肩膀、髋部、上肢肘、腕、下肢膝、踝。匹配时优先使用躯干和四肢的关键点因为它们的几何关系稳定受服装和体型影响小。比如做“深蹲动作比对”真正需要的是髋、膝、踝三个点的夹角变化做“瑜伽动作匹配”肩、髋、腕的相对位置更关键。这里给出一个关键点取舍的参考表组别关键点索引Mediapipe 官方编号适用场景说明躯干核心11(左肩), 12(右肩), 23(左髋), 24(右髋)全身动作匹配的公共基底这四个点构成躯干平面用于归一化参考上肢13(左肘), 14(右肘), 15(左腕), 16(右腕)手臂动作、太极、健身动作肘关节角度和腕部位置是主要特征下肢25(左膝), 26(右膝), 27(左踝), 28(右踝)深蹲、踢腿、步态分析膝踝夹角对动作质量判断极重要可选增强0(鼻), 1-6(面部)需要头部朝向的场景抖动大建议只在头部动作比对时使用实际写代码时你不需要手动记住每个索引的含义。更稳妥的做法是定义一组具名常量把索引和名称绑定起来源码里通常也会有一段类似的映射表比如LEFT_SHOULDER 11。从匹配的角度讲特征向量不需要包含全部 33 个点选出 15 到 20 个稳定点就足够了——这能显著减少计算量还能降低噪声对匹配结果的干扰。3. 姿态匹配的核心特征构造与相似度计算3.1 用角度特征而非坐标特征做匹配到此为止代码已经能拿到一帧图像的全部姿态关键点。匹配计算的第一个关键决策是到底用什么作为比较的对象最直观的做法是直接比较两组坐标向量的欧氏距离但这里有个陷阱——不同人的身高、臂长、摄像头距离不一样导致同样的动作在不同人身上关键点的绝对坐标差异很大。更稳的做法是构造角度特征。人体关节的夹角是平移不变、尺度不变的——不管人站在画面左边还是右边不管人离镜头远还是近肘关节弯曲 90 度就是 90 度。角度特征的计算逻辑是对每个需要关心的关节点取其前后两个相邻关键点构造两条向量然后计算这两条向量的夹角。import numpy as np def calculate_angle(a, b, c): 计算三个关键点构成的角度b 为角点。 a, b, c 分别为三个关键点的 (x, y) 坐标。 ba np.array([a[0] - b[0], a[1] - b[1]]) bc np.array([c[0] - b[0], c[1] - b[1]]) cos_angle np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc) 1e-6) angle np.arccos(np.clip(cos_angle, -1.0, 1.0)) return np.degrees(angle)这段代码的核心是余弦定理的应用。ba和bc分别是角点 b 指向 a 和指向 c 的向量np.dot计算向量内积np.linalg.norm计算向量长度两者相除得到夹角的余弦值。加1e-6是防止除零np.clip把余弦值限制在 [-1, 1] 区间避免浮点误差导致arccos报错。用这个函数你可以提取出任意关节的角度值。做深蹲动作比对重点提取左右膝关节的角度做手臂平举比对重点提取左右肘关节的角度。角度向量的维度一般控制在 6 到 12 个比如“左肘、右肘、左肩、右肩、左膝、右膝”六个角度就足以区分大多数粗粒度动作。3.2 匹配计算的两种路线欧氏距离与余弦相似度角度特征构造完成后匹配计算就变成了一个标准的高维向量距离计算问题。具体用哪种度量方式取决于你的业务场景。度量方式公式适用场景特点欧氏距离√(Σ(ai - bi)²)动作评分、姿势偏差检测对每个维度的偏差都敏感数值越小越相似余弦相似度(A·B) / (A加权欧氏距离√(Σ wi(ai - bi)²)需要强调关键关节通过权重控制哪些关节对匹配结果影响更大对于“自动评分”这个常见需求我推荐加权欧氏距离。它的好处是直观——计算出来的数值带有物理意义比如“整体偏差 12.5 度”可以直接映射到百分制得分。而余弦相似度虽然能给出一个 [0, 1] 之间的相似度分数但很难解释“0.92 到底意味着动作有多标准”。def compute_pose_similarity(angle_vector_1, angle_vector_2, weightsNone): 加权欧氏距离计算姿态相似度。 angle_vector_1, angle_vector_2 为等长的角度特征向量。 weights 为每个角度维度的权重None 时表示等权。 返回 (similarity, distance) —— similarity 为 0~100 的得分distance 为原始距离。 v1 np.array(angle_vector_1, dtypenp.float32) v2 np.array(angle_vector_2, dtypenp.float32) if weights is None: weights np.ones_like(v1) else: weights np.array(weights, dtypenp.float32) diff v1 - v2 weighted_distance np.sqrt(np.sum(weights * (diff ** 2)) / np.sum(weights)) # 将距离映射为 0~100 的得分max_angle_diff 表示“差多少度算完全不一致” max_angle_diff 60.0 similarity max(0.0, 1.0 - weighted_distance / max_angle_diff) * 100.0 return similarity, weighted_distance这段代码的逻辑是先算出加权后的平均距离然后把距离值映射到得分区间。max_angle_diff 60表示某维角度偏差达到 60 度时相似度直接归零。这个阈值不是拍脑袋定的——人体关节最大活动范围大多在 120 度左右60 度已经意味着一方动作完全变形。实际使用中你可以根据动作的严格程度调整舞蹈动作比对用 30康复训练用 45健身动作用 60 比较合理。需要注意的一个坑是角度是周期性数值0 度和 360 度是同一个角度但直接相减会得到 360 的差值。上述代码没有处理角度环绕问题在实际使用中建议先把角度归一化到 [0, 180) 区间——人体的关节角不会超过 180 度归一化后就不会出现环绕问题。3.3 匹配计算的完整流程与参考代码把上面的概念串起来一个完整的姿态匹配模块包含四个步骤提取关键点坐标 → 计算角度特征 → 与标准姿态比对 → 输出相似度分数。这里给出一段可直接运行的示例假设你已经通过 Mediapipe 得到了两组关键点的坐标列表def build_angle_feature(landmarks): 从 Mediapipe 的关键点列表构造角度特征向量。 landmarks 包含 33 个关键点每个关键点有 x, y。 这里提取六组关键角度左右肘、左右肩、左右膝。 def get_point(idx): return (landmarks[idx].x, landmarks[idx].y) angles [] # 左肘左肩 - 左肘 - 左腕 angles.append(calculate_angle(get_point(11), get_point(13), get_point(15))) # 右肘右肩 - 右肘 - 右腕 angles.append(calculate_angle(get_point(12), get_point(14), get_point(16))) # 左肩左肘 - 左肩 - 左髋 angles.append(calculate_angle(get_point(13), get_point(11), get_point(23))) # 右肩右肘 - 右肩 - 右髋 angles.append(calculate_angle(get_point(14), get_point(12), get_point(24))) # 左膝左髋 - 左膝 - 左踝 angles.append(calculate_angle(get_point(23), get_point(25), get_point(27))) # 右膝右髋 - 右膝 - 右踝 angles.append(calculate_angle(get_point(24), get_point(26), get_point(28))) return anglesbuild_angle_feature函数的输入是 Mediapipe 的results.pose_landmarks.landmark列表输出是一个包含 6 个角度值的列表。你可以看到每个角度都对应一个明确的关节点和两个相邻点这个函数把“原始坐标”转化成了“语义特征”。函数里用到的小技巧是get_point内部只取了 x 和 y忽略了 z 和 visibility。原因在开头提过z 分量不可靠visibility 应该在外面做过滤——如果某个关键点的 visibility 低于 0.5说明该点可能被遮挡此时应该跳过这帧的计算而不是拿一个不准的坐标硬算。在实时场景中我一般会在调用build_angle_feature之前加一个置信度检查低于阈值的帧直接丢弃否则匹配结果会出现严重的跳变。4. 工程化落地从静态图像走到视频匹配4.1 视频流的姿态匹配架构静态姿态匹配跑通后接下来面对的真实问题是视频流里每一帧都在产生姿态数据怎么把“帧级匹配”升级成“动作一致性判断”这里有两种常见做法一种是逐帧比较后取平均值另一种是抽关键帧比较。逐帧比较适合离线分析录好的视频数据量大但稳定抽关键帧适合实时应用计算开销小。如果源码里的项目使用说明提到了“动作得分”或“匹配度百分比”大概率采用的是逐帧比较后取均值或加权平均值的方案。pip install mediapipe opencv-python numpy这是运行源码前的环境准备命令。需要注意 Python 版本兼容性Mediapipe 对 Python 3.8 到 3.10 的支持最稳定如果你用的是 Python 3.11 或 3.12pip install mediapipe可能找不到预编译的 wheel 包解决方法是降级 Python 版本或者使用 conda 创建虚拟环境。这也是标题里的“项目使用说明”大概率会重点强调的部分。4.2 视频序列匹配怎么做才稳视频不是一帧一帧孤立地比而是要处理时间维度上的信息。一个常见的坑是动作执行快慢不一样逐帧匹配会遇到错位问题——“同样做一套广播体操A 做得快B 做得慢第 30 帧对比时两人根本不在同一个动作阶段”。处理这个问题有两个层面如果只是比对“某个瞬间的姿态是否标准”逐帧独立匹配没问题如果要匹配“一段动作序列是否一致”需要在匹配之前做时间归一化——把视频帧重采样到统一长度比如用插值把 80 帧和 120 帧的序列都变成 100 帧然后再逐帧对比。def resample_sequence(score_sequence, target_length100): 将任意长度的帧得分序列重采样到固定长度。 score_sequence 为每一帧和标准姿态的相似度得分列表。 使用线性插值实现时间轴的归一化。 source_length len(score_sequence) source_indices np.linspace(0, source_length - 1, numsource_length) target_indices np.linspace(0, source_length - 1, numtarget_length) resampled np.interp(target_indices, source_indices, score_sequence) return resamplednp.interp做的事情是线性插值给定原始序列的坐标和值计算出目标位置上的值。source_indices是原始帧的序号target_indices是重采样后的帧序号。比如源序列 80 帧目标 100 帧代码会在第 0.79 帧、第 1.58 帧这样的“帧间位置”上插值出新的得分值。时间归一化之后再计算整段动作的匹配度就用重采样后序列的均值或最低分。均值反映整体表现最低分反映最差的那一拍——对于“动作是否标准”的衡量我建议同时输出这两个指标让调用方自己决定用哪个。5. 参数调优与相似度阈值设置的实操建议到了这一步你已经有一整套能跑的姿态匹配代码了但“能跑”和“好用”之间还差一个调参环节。这里给出三个具体技巧。技巧一用“错误样本”来标定阈值。匹配度设置为多少算“动作一致”不要拍脑袋。找一个标准动作的录制视频作为参考姿态然后分别录制 10 次“标准动作”和 10 次“故意做错的动作”计算各自与参考姿态的匹配分数画出分布。两个分布的分界线就是你的阈值——而不是凭感觉选 80 分还是 90 分。这个流程在源码层面其实就是统计similarity在两组样本上的均值和方差然后取中间值作为阈值。技巧二对关键点抖动做平滑。视频流中的关键点在静止状态下也会有 ±2 度的角度抖动这会直接拉低匹配分数。解决办法是设置一个“容忍区间”——角度偏差在 5 度以内视为无差异超出才扣分。在上面compute_pose_similarity的实现上可以给diff增加一个死区判断dead_zone 5.0 diff np.abs(v1 - v2) diff np.where(diff dead_zone, 0.0, diff - dead_zone)这里的逻辑是偏差小于 5 度的维度直接清零超过 5 度的部分减去死区值后再参与距离计算。这个技巧能让匹配得分更稳定尤其是对带有轻微呼吸起伏的站立姿态效果非常明显。技巧三验证匹配结果时把角度特征打印出来。不要只盯着最后的分数要输出每个角度的具体数值和偏差——比如 debug 日志里显示“左膝角度标准 102.3当前 89.7偏差 12.6”。这样当得分异常时你能立刻定位到是哪个关节在拖后腿而不是对着一个分数瞎猜。源码里如果带--debug之类的命令行参数通常就是输出这些中间特征的开关。最后需要记住的一点是Mediapipe 的模型对特定拍摄角度正前方、平视表现最好监控摄像头俯拍视角下关键点抖动会明显增大。如果匹配结果在实景测试中不稳定优先检查摄像头角度和人体在画面中的占比——人体高度占画面高度的 60% 以上时识别最可靠。这属于输入侧的问题调参解决不了。本文还有配套的精品资源点击获取
返回列表