You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于OpenPose骨骼关键点坐标用向量法计算人体关节角度

人体关节角度计算最优方案及向量法实现

原atan2方法误差原因

你之前用atan2计算得到的是平面有向角,返回范围是0360度,不符合人体关节角度的常规定义(我们需要的是两条骨骼段之间的最小无向夹角,范围0180度),同时你没有对低置信度的关键点做过滤,当OpenPose识别的关键点置信度不足时(比如坐标为(0,0)、置信度为0的无效点),计算结果必然偏离实际值。

向量法计算逻辑

关节角度的核心是计算以关节点为顶点、连接两端骨骼的两条线段的夹角,向量点积法是这类场景的最优方案,计算逻辑如下:

  • 已知组成关节的三个点,编号为p1(远端骨骼端点)、p2(关节顶点)、p3(近端骨骼端点)
  • 构造两个从关节顶点出发的向量:v1 = (x1 - x2, y1 - y2),v2 = (x3 - x2, y3 - y2)
  • 通过点积公式计算夹角余弦值:cosθ = (v1·v2) / (|v1| * |v2|)
  • 对cosθ做数值钳制,避免浮点计算误差导致超出arccos的定义域[-1, 1]
  • 转换为角度值即可得到0~180度的关节夹角

完整代码实现

import os
import json
import math

# 配置项
JSON_DIR = "你的JSON文件夹路径"
CONFIDENCE_THRESHOLD = 0.5 # 关键点置信度阈值,低于该值的帧跳过计算
OPENPOSE_ANGLES = [(9, 10, 11, "right_knee"),
                   (2, 3, 4, "right_arm")]

def calculate_joint_angle(p1, p2, p3):
    """
    输入三个点的(x,y,c),返回关节角度,无效返回None
    """
    x1, y1, c1 = p1
    x2, y2, c2 = p2
    x3, y3, c3 = p3
    
    # 过滤低置信度点
    if c1 < CONFIDENCE_THRESHOLD or c2 < CONFIDENCE_THRESHOLD or c3 < CONFIDENCE_THRESHOLD:
        return None
    # 过滤识别失败的零值点
    if (x1 == 0 and y1 ==0) or (x2 ==0 and y2 ==0) or (x3 ==0 and y3 ==0):
        return None
    
    # 构造向量
    v1 = (x1 - x2, y1 - y2)
    v2 = (x3 - x2, y3 - y2)
    
    # 计算点积
    dot_product = v1[0] * v2[0] + v1[1] * v2[1]
    # 计算向量模长
    norm_v1 = math.hypot(v1[0], v1[1])
    norm_v2 = math.hypot(v2[0], v2[1])
    
    # 避免除零错误
    if norm_v1 == 0 or norm_v2 == 0:
        return None
    
    # 计算cosθ并钳制数值范围
    cos_theta = dot_product / (norm_v1 * norm_v2)
    cos_theta = max(min(cos_theta, 1.0), -1.0)
    
    # 转换为角度
    angle = math.degrees(math.acos(cos_theta))
    return angle

# 批量处理JSON文件
for file_name in os.listdir(JSON_DIR):
    if not file_name.endswith(".json"):
        continue
    file_path = os.path.join(JSON_DIR, file_name)
    with open(file_path, 'r') as f:
        data = json.load(f)
    if not data['people']:
        continue # 无识别结果跳过
    # 提取单个人的关键点(你的场景只有一个骑行者,取索引0即可)
    pose_kps = data['people'][0]['pose_keypoints_2d']
    # 转换为索引到(x,y,c)的映射
    kp_map = {}
    for idx in range(0, len(pose_kps), 3):
        kp_idx = idx // 3
        x = pose_kps[idx]
        y = pose_kps[idx+1]
        c = pose_kps[idx+2]
        kp_map[kp_idx] = (x, y, c)
    
    # 计算所有配置的关节角度
    frame_angles = {}
    for p1_idx, p2_idx, p3_idx, angle_name in OPENPOSE_ANGLES:
        p1 = kp_map[p1_idx]
        p2 = kp_map[p2_idx]
        p3 = kp_map[p3_idx]
        angle = calculate_joint_angle(p1, p2, p3)
        frame_angles[angle_name] = angle
    
    # 这里可以加你自己的结果存储逻辑
    print(f"文件名:{file_name},角度结果:{frame_angles}")

侧面骑行场景优化建议

  • 你的场景是固定拍摄右侧轮廓,不存在左右翻转的问题,无需额外做坐标镜像处理
  • 可以对连续5~10帧的角度结果做滑动平均滤波,消除OpenPose关键点识别的瞬时抖动,得到更平滑的角度序列
  • 如果需要计算关节的屈伸方向,可以在向量法基础上结合角度正负判断,常规骑行场景下0~180度的无向夹角已经满足需求。

内容的提问来源于stack exchange,提问作者vesii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 10:45:02