姿态估计与关键点检测

本文系统讲解姿态估计与关键点检测,回答自顶向下与自底向上怎么选、热图回归与坐标回归有何差别、HRNet 与 MediaPipe 各自适合什么场景等实战问题。覆盖人体、手、人脸关键点,PAF 与 Associative Embedding、OKS 与 PCK 指标、2D 与 3D 姿态,并给出 MediaPipe 姿态估计与热图峰值提取两段可运行代码,附模型对比表、权衡与常见坑。

引言

姿态估计要回答「人的关节在哪」。它比检测更精细:检测给一个框,姿态要给出十几到二十几个关键点坐标,并保证左右、上下关节的结构合理。它是动作识别、健身指导、人机交互、动画驱动、体育分析的基础模块。

本文按「任务分类 → 两种范式 → 热图回归 → 关键模型 → 指标 → 落地」的顺序拆解,重点讲清自顶向下与自底向上的取舍、热图回归的优势、HRNet 与 MediaPipe 的适用场景,并给出可直接运行的代码。

目录

  1. 姿态估计的任务分类
  2. 自顶向下与自底向上
  3. 热图回归与坐标回归
  4. 关键模型
  5. 评估指标
  6. 代码:MediaPipe 姿态估计
  7. 代码:热图峰值提取
  8. 模型对比表
  9. 训练要点
  10. 应用落地
  11. 手部与人脸关键点
  12. 时序平滑与动作识别
  13. 端侧与浏览器部署
  14. 权衡取舍
  15. 常见坑清单
  16. 小结

1. 姿态估计的任务分类

姿态估计可以按三个维度切分:

维度取值说明
人数单人 / 多人多人需先分组或先检测框
维度2D / 3D2D 出像素坐标,3D 出空间坐标
目标人体 / 手 / 人脸关键点定义与数量不同

常见的关键点定义:COCO 人体 17 点、MPII 人体 16 点、手部 21 点、人脸 68 点(或 468 点)。关键点的顺序在不同数据集里不同,跨数据集训练时必须做映射。

2. 自顶向下与自底向上

2.1 自顶向下

先用人体检测器框出每个人,再对每个框做单人体姿态估计。代表有 HRNet、SimpleBaseline(ResNet 加反卷积)。

  • 优点:单人姿态精度高,人体尺度归一化后更容易。
  • 缺点:推理次数随人数线性增长,人越多越慢;检测框不准会拖累姿态。

2.2 自底向上

先检测全图所有关键点,再把这些点分组到不同的人。代表有 OpenPose(用 PAF,部位亲和场)与 Associative Embedding(用嵌入向量聚类)。

  • 优点:推理次数与人数无关,人越多越划算,实时性好。
  • 缺点:分组容易出错,尤其在人挨人、遮挡时。

选择原则:人数少、要精度,选自顶向下;人数多、要实时,选自底向上。

3. 热图回归与坐标回归

两种输出方式:

  • 热图回归:对每个关键点输出一张概率热图,峰值位置即坐标。空间信息保留好,精度高,是主流做法。代价是输出分辨率高、显存大。
  • 坐标回归:直接回归关键点坐标。简单、输出小,但对空间信息利用不足,精度通常低于热图。

热图回归的后处理是找峰值:取最大响应位置,或做 soft-argmax 得到亚像素坐标。下面是一个简单的峰值提取:

import numpy as np

def heatmap_peak(heatmap):
    # heatmap: (K, H, W),返回每个关键点的 (x, y) 与置信度
    K, H, W = heatmap.shape
    flat = heatmap.reshape(K, -1)
    idx = flat.argmax(axis=1)
    conf = flat.max(axis=1)
    ys, xs = idx // W, idx % W
    return np.stack([xs, ys], axis=1), conf

hm = np.random.rand(17, 64, 64).astype(np.float32)
coords, conf = heatmap_peak(hm)
print(coords.shape, conf.shape)   # (17, 2) (17,)

实际工程中还会用高斯模糊平滑热图、用二次插值求亚像素峰值,以提升坐标精度。

4. 关键模型

4.1 HRNet

HRNet(High-Resolution Net)的核心是「全程保持高分辨率」:不像传统网络那样一路下采样再上采样,而是并行维护多个分辨率的支路,并在支路间反复交换信息。这让它成为 2D 人体姿态的强基线,HRNet-W32 在 COCO 上约 74.4 AP,W48 约 76.3 AP。代价是显存与计算较高。

4.2 OpenPose

OpenPose 是自底向上的经典,用 PAF(Part Affinity Fields)编码肢体方向,再用贪心匹配把关键点连成骨架。它在多人实时场景表现好,但精度略低于自顶向下的 HRNet。

4.3 MediaPipe

MediaPipe 是 Google 的端侧框架,提供 Pose、Hands、FaceMesh 等开箱即用的模型,能在手机与浏览器上实时运行。它用轻量骨干加回归头,精度不及 HRNet,但速度与易用性极佳,是移动端首选。

4.4 3D 姿态与 SMPL

3D 姿态估计输出关节的三维坐标,代表方法有基于体积热图(Volumetric Heatmap)与基于参数化人体模型(SMPL)。SMPL 用姿态与形状参数描述人体,广泛用于动画与虚拟试穿。3D 姿态的难点是深度歧义与标注稀缺。

5. 评估指标

指标含义适用
PCK预测点落在真值阈值半径内的比例早期常用
OKS关键点相似度,带尺度与可见性权重COCO 主指标
APOKS 阈值下的平均精度COCO
MPJPE3D 关节平均位置误差(毫米)3D 姿态

OKS 与检测的 IoU 类似,但按关键点计算并考虑人体尺度,是 COCO 姿态的主指标。AP 由 OKS 在不同阈值下的精度平均得到。

6. 代码:MediaPipe 姿态估计

MediaPipe 的接口极简,几行代码就能拿到关键点并绘制骨架。

import cv2
import mediapipe as mp

mp_pose = mp.solutions.pose
mp_draw = mp.solutions.drawing_utils

img = cv2.imread("person.jpg")
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

with mp_pose.Pose(static_image_mode=True, model_complexity=1,
                  min_detection_confidence=0.5) as pose:
    result = pose.process(rgb)

if result.pose_landmarks:
    h, w = img.shape[:2]
    for i, lm in enumerate(result.pose_landmarks.landmark):
        x, y = int(lm.x * w), int(lm.y * h)     # 归一化坐标转像素
        print(i, (x, y), round(lm.visibility, 3))
        cv2.circle(img, (x, y), 3, (0, 255, 0), -1)
    mp_draw.draw_landmarks(img, result.pose_landmarks, mp_pose.POSE_CONNECTIONS)

cv2.imwrite("pose_out.jpg", img)

注意 lm.x、lm.y 是相对图像宽高的归一化坐标(0 到 1),要乘以宽高才是像素坐标;lm.visibility 表示该点可见度,低于阈值应视为不可靠。model_complexity 取 0、1、2 对应不同精度与速度。

7. 代码:热图峰值提取与骨架绘制

用自己的后处理替换框架,便于接入自定义模型。

import numpy as np
import cv2

def soft_argmax(heatmap, beta=100.0):
    # 对热图做 soft-argmax,得到亚像素坐标
    K, H, W = heatmap.shape
    hm = heatmap - heatmap.max(axis=(1, 2), keepdims=True)
    exp = np.exp(beta * hm)
    exp /= exp.sum(axis=(1, 2), keepdims=True)
    xs = (exp.sum(axis=1) * np.arange(W)).sum(axis=1)
    ys = (exp.sum(axis=2) * np.arange(H)).sum(axis=1)
    return np.stack([xs, ys], axis=1)

hm = np.random.rand(17, 64, 64).astype(np.float32)
coords = soft_argmax(hm)
print(coords[:3])   # 前三个关键点的亚像素坐标

# 用 COCO 骨架连接关系绘制
EDGES = [(5, 7), (7, 9), (6, 8), (8, 10), (5, 6), (5, 11), (6, 12), (11, 12)]
canvas = np.zeros((480, 480, 3), np.uint8)
scale = 4.0   # 热图 64 缩放到 256 显示
pts = [(int(x * scale), int(y * scale)) for x, y in coords]
for a, b in EDGES:
    cv2.line(canvas, pts[a], pts[b], (0, 200, 0), 2)
cv2.imwrite("skeleton.png", canvas)

beta 越大 soft-argmax 越接近硬 argmax,越小越平滑。亚像素精度对体育测量这类需要精确关节角度的场景很重要。

8. 模型对比表

模型范式参数量COCO AP速度场景
HRNet-W32自顶向下约 28.5M74.4慢高精度离线
HRNet-W48自顶向下约 63.6M76.3更慢精度优先
OpenPose自底向上约 52M约 61中多人实时
MediaPipe Pose单人约 6M约 60 级很快移动端
SimpleBaseline R50自顶向下约 34M约 72中通用基线

自顶向下的 AP 通常高于自底向上,但整体系统还要加上人体检测的开销。移动端优先 MediaPipe,服务器高精度选 HRNet。

9. 训练要点

  • 数据:COCO Keypoints、MPII、AIC;手部用 FreiHAND,人脸用 WFLW 或 300W。
  • 损失:热图回归用 MSE,配合高斯目标热图;坐标回归用 L1 或 L2。
  • 增强:随机旋转、缩放、翻转(注意翻转要交换左右关键点索引)、遮挡模拟。
  • 人体检测质量:自顶向下对检测框敏感,检测框抖动会直接传导到姿态。
  • 小目标:远处的人关键点极小,输入分辨率要够高。
  • 蒸馏:把 HRNet 蒸馏到轻量模型,兼顾精度与端侧速度。
  • 预训练:ImageNet 分类权重初始化骨干,姿态数据少时收益明显。
  • 难例挖掘:遮挡、罕见姿态单独补数据,比堆量更有效。

10. 应用落地

应用关键点关注指标常见方案
健身动作计数人体 17 点关节角度稳定MediaPipe + 规则
手语识别手部 21 点时序一致性MediaPipe Hands + 时序模型
虚拟试穿人体 + 3D3D 误差SMPL 拟合
体育分析人体 17 点亚像素精度HRNet
人机交互手/脸低延迟MediaPipe

落地时要处理时序平滑:单帧关键点会抖动,用卡尔曼滤波或指数平滑让结果稳定。

11. 手部与人脸关键点

手部与人脸的关键点更密集,对精度与延迟的要求也不同。

  • 手部 21 点:MediaPipe Hands 先用掌检测器定位手掌,再回归 21 点,适合手势交互。难点是自遮挡与快速运动导致的运动模糊。
  • 人脸 68 点或 468 点:MediaPipe FaceMesh 输出 468 点三维网格,用于表情驱动、虚拟形象。468 点的稠密网格对算力要求更高。
  • 关键点数量越多,标注成本越高,合成数据(如用 3D 模型渲染)成为重要补充。
import cv2
import mediapipe as mp

mp_hands = mp.solutions.hands
img = cv2.cvtColor(cv2.imread("hand.jpg"), cv2.COLOR_BGR2RGB)

with mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.5) as hands:
    res = hands.process(img)
    if res.multi_hand_landmarks:
        for hand in res.multi_hand_landmarks:
            tips = [(lm.x, lm.y) for lm in hand.landmark]
            print("hand points:", len(tips))   # 21

12. 时序平滑与动作识别

视频姿态的关键问题是抖动。逐帧独立推理会让骨架跳动,影响下游动作判断。

  • 指数平滑:s_t = alpha * x_t + (1 - alpha) * s_{t-1},实现简单,延迟小。
  • 卡尔曼滤波:对位置与速度建模,平滑效果更好,适合追踪。
  • One Euro Filter:自适应截止频率,静止时平滑、运动时低延迟,交互场景首选。
def ema_smooth(series, alpha=0.5):
    out = []
    prev = None
    for x in series:
        prev = x if prev is None else alpha * x + (1 - alpha) * prev
        out.append(prev)
    return out

import numpy as np
coords = np.random.rand(30).tolist()
smoothed = ema_smooth(coords, alpha=0.4)
print("raw std:", round(float(np.std(coords)), 4),
      "smoothed std:", round(float(np.std(smoothed)), 4))

有了稳定的关键点序列,动作识别可以接时序模型:早期用 LSTM 或 ST-GCN(时空图卷积),现在也有基于 Transformer 的方案。关键点序列比原始视频轻量得多,适合端侧动作识别。

13. 端侧与浏览器部署

姿态估计常见于手机与网页,部署要点:

  • 手机:MediaPipe 提供 Android 与 iOS 的 SDK,模型可量化到 int8 提速。
  • 浏览器:MediaPipe 与 TensorFlow.js 支持在浏览器用 WebGL 或 WebAssembly 推理,无需上传视频,隐私友好。
  • 边缘盒子:把 HRNet 蒸馏或量化为轻量模型,配合 TensorRT 部署。
  • 算力预算:手机端单帧姿态通常要控制在 10 到 30 毫秒内,否则交互卡顿。

端侧部署的通用手段(量化、剪枝、蒸馏)与检测分割一致,详见模型压缩与端侧部署。

14. 权衡取舍

  • 精度与速度:HRNet 精度高但慢,MediaPipe 快但精度一般,按设备与延迟预算选。
  • 自顶向下与自底向上:人多选自底向上省时间,人少选自顶向下提精度。
  • 2D 与 3D:2D 数据多、易标注,3D 信息全但标注贵且深度歧义大。
  • 单人与多人:单人模型无法处理多人,多人模型在单人图上略慢。
  • 热图与回归:热图精度高但显存大,回归轻量但精度略低。

15. 常见坑清单

  • 坐标归一化混淆:MediaPipe 输出归一化坐标,直接用像素坐标会导致点全挤在左上角。
  • 翻转增强不交换左右点:水平翻转后左肩变右肩,索引不交换会污染标签。
  • 忽略 visibility:遮挡点的低置信度坐标参与计算会拉低指标。
  • 检测框太紧:自顶向下裁剪留白不足,关键点被裁掉,精度下降。
  • 热图分辨率过低:下采样过度导致小目标关键点丢失。
  • 多人分组错误:自底向上在人挨人时把人 A 的点连到人 B。
  • 时序抖动:视频逐帧独立推理,骨架剧烈跳动,需平滑。
  • 关键点定义不一致:COCO 17 点与 MPII 16 点顺序不同,跨集训练必须映射。
  • 忽略对称性约束:左右对称关键点可互相校验,能过滤明显错误。
  • 忘记处理空检测:无人帧时姿态输出为空,下游代码需判空。
  • 输入长宽比被拉伸:直接 resize 到方形会让关节角度失真。
  • 3D 深度歧义:单目 3D 姿态存在尺度与深度不确定,需标定或先验约束。

16. 小结

姿态估计的工程主线是:判断人数与设备 → 人少要精度选自顶向下(HRNet),人多要实时选自底向上(OpenPose)或 MediaPipe → 用热图回归加亚像素峰值 → 指标用 OKS 与 AP → 视频场景做时序平滑。记住三个要点:坐标归一化要转像素、翻转增强要交换左右、遮挡点要看可见度。姿态估计的精度上限很大程度上取决于人体检测与输入分辨率。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「计算机视觉」更多文章

  1. 检测与分割的评估指标
  2. 多模态视觉语言模型
  3. 3D 视觉:点云与深度估计