引言
姿态估计要回答「人的关节在哪」。它比检测更精细:检测给一个框,姿态要给出十几到二十几个关键点坐标,并保证左右、上下关节的结构合理。它是动作识别、健身指导、人机交互、动画驱动、体育分析的基础模块。
本文按「任务分类 → 两种范式 → 热图回归 → 关键模型 → 指标 → 落地」的顺序拆解,重点讲清自顶向下与自底向上的取舍、热图回归的优势、HRNet 与 MediaPipe 的适用场景,并给出可直接运行的代码。
目录
- 姿态估计的任务分类
- 自顶向下与自底向上
- 热图回归与坐标回归
- 关键模型
- 评估指标
- 代码:MediaPipe 姿态估计
- 代码:热图峰值提取
- 模型对比表
- 训练要点
- 应用落地
- 手部与人脸关键点
- 时序平滑与动作识别
- 端侧与浏览器部署
- 权衡取舍
- 常见坑清单
- 小结
1. 姿态估计的任务分类
姿态估计可以按三个维度切分:
| 维度 | 取值 | 说明 |
|---|---|---|
| 人数 | 单人 / 多人 | 多人需先分组或先检测框 |
| 维度 | 2D / 3D | 2D 出像素坐标,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 主指标 |
| AP | OKS 阈值下的平均精度 | COCO |
| MPJPE | 3D 关节平均位置误差(毫米) | 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.5M | 74.4 | 慢 | 高精度离线 |
| HRNet-W48 | 自顶向下 | 约 63.6M | 76.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 + 时序模型 |
| 虚拟试穿 | 人体 + 3D | 3D 误差 | 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 → 视频场景做时序平滑。记住三个要点:坐标归一化要转像素、翻转增强要交换左右、遮挡点要看可见度。姿态估计的精度上限很大程度上取决于人体检测与输入分辨率。
延伸阅读
- 目标检测与 YOLO 系列工程落地 — 自顶向下依赖人体检测
- 图像分割与实例分割 — 关键点与掩码的联合任务
- 视觉 Transformer 与多模态模型 — 姿态中的 Transformer 方案
- Python 数据科学与 AI 实战 — 张量与数值计算基础
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。