MMPose 手部关键点估计实战:HRNetv2-W18 + DarkPose 在 COCO-WholeBody-Hand 上的 2D 手部姿态估计
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
导读
本文围绕 MMPose 官方在 COCO-WholeBody-Hand 手部关键点数据集上提供的经典配置HRNetv2-W18 + DarkPose 自顶向下(top-down)热图模型展开,完整讲解其网络架构、DarkPose 无偏高斯编解码原理、完整配置文件、数据集加载逻辑与训练/测试/推理流程。读完本文,你将掌握如何在 MMPose 中复现该模型的 PCK@0.2 / AUC / EPE 指标,并能够基于配置与源码理解 heatmap 编解码机制,进而迁移到其他手部或身体关键点任务。
一、方案概览:HRNetv2、DarkPose 与 COCO-WholeBody-Hand
本文所述模型在官方模型库中收录于configs/hand_2d_keypoint/topdown_heatmap/coco_wholebody_hand/目录,对应配置名td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256,它由三部分技术组件构成:
- HRNetv2 骨干网络(TPAMI'2019,
Deep High-Resolution Representation Learning for Visual Recognition):与常见的"先下采样再上采样"的单分支骨干不同,HRNet 在整个前向过程中始终保持高分辨率分支,并通过多分辨率分支之间反复交换信息,从而输出空间分辨率与语义丰富度兼备的特征图。 - DarkPose 编解码方法(CVPR'2020,
Distribution-aware coordinate representation for human pose estimation):在训练阶段使用"无偏"高斯热图(unbiased=True),在推理阶段基于热图分布的泰勒展开做二阶坐标修正,缓解经典 argmax 坐标量化带来的系统性偏差。 - COCO-WholeBody-Hand 数据集(ECCV'2020,
Whole-Body Human Pose Estimation in the Wild):从 COCO-WholeBody 133 个全身关键点中抽取的 42 个手部关键点(左右手各 21 个),覆盖腕部与五根手指。
官方验证集结果
在 COCO-WholeBody-Hand 验证集上的官方结果如下(指标分别为 PCK@0.2、AUC、EPE,输入尺寸 256x256):
| Arch | Input Size | PCK@0.2 | AUC | EPE |
|---|---|---|---|---|
| pose_hrnetv2_w18_dark | 256x256 | 0.814 | 0.840 | 4.37 |
权重文件与日志的下载信息收录在 hrnetv2_dark_coco_wholebody_hand.yml 中,其中记录了架构(HRNetv2 + DarkPose)、训练数据(COCO-WholeBody-Hand)以及上述三项指标,可直接作为复现与模型选择依据。
二、配置文件逐段解读
核心训练/测试配置位于 td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256.py,下面按模块拆解。
1. 运行时与优化器
_base_ = ['../../../_base_/default_runtime.py'] # runtime train_cfg = dict(max_epochs=210, val_interval=10) # optimizer optim_wrapper = dict(optimizer=dict(type='Adam', lr=5e-4,)) # learning policy param_scheduler = [ dict(type='LinearLR', begin=0, end=500, start_factor=0.001, by_epoch=False), # warm-up dict(type='MultiStepLR', begin=0, end=210, milestones=[170, 200], gamma=0.1, by_epoch=True) ] # automatically scaling LR based on the actual training batch size auto_scale_lr = dict(base_batch_size=256)- 模型继承 configs/base/default_runtime.py 中的默认运行时设置(日志、钩子、visualizer 等)。
- 训练 210 个 epoch,每 10 个 epoch 在验证集上评测一次。
- 优化器使用 Adam,初始学习率
5e-4;前 500 次迭代用线性 warm-up(起始系数 0.001),随后在第 170 和 200 epoch 处各降学习率 10 倍。 auto_scale_lr声明基准 batch size 为 256,当实际训练 batch size 不同时 MMPose 会自动等比缩放学习率。- 默认运行时中,
default_hooks.checkpoint使用save_best='AUC', rule='greater',即按照验证 AUC 保存最优权重。
2. Codec:DarkPose 无偏热图编码
codec = dict( type='MSRAHeatmap', input_size=(256, 256), heatmap_size=(64, 64), sigma=2, unbiased=True)这是整个方案的"灵魂"配置。MSRAHeatmapcodec 的实现在 mmpose/codecs/msra_heatmap.py,其核心参数:
input_size=(256, 256):输入图像尺寸(宽、高)。heatmap_size=(64, 64):输出热图尺寸,即输入被下采样 4 倍。sigma=2:高斯核标准差,决定标注点在热图上"扩散"的范围;3-sigma 规则下高斯半径约为 6 个像素。unbiased=True:启用 DarkPose 的无偏编码与解码。编码时调用generate_unbiased_gaussian_heatmaps(见 mmpose/codecs/utils/gaussian_heatmap.py),其特点是高斯中心使用连续浮点坐标(mu = keypoints[n, k],不取整),避免传统取整带来的中心偏移误差;解码时调用refine_keypoints_dark做二阶修正。blur_kernel_size(默认 11):解码阶段高斯模糊核大小,代码注释中给出了经验公式sigma = 0.3*((ks-1)*0.5-1)+0.8,即 ks=11 对应 sigma=2,与训练时的sigma=2匹配。
编码输出为(K, H, W)的热图与(N, K)的keypoint_weights;解码时先由get_heatmap_maximum取热图最大响应点(mmpose/codecs/utils/post_processing.py),再乘以scale_factor = input_size / heatmap_size = 4还原到输入图像坐标系。
3. 模型结构:骨干、颈部与检测头
model = dict( type='TopdownPoseEstimator', data_preprocessor=dict( type='PoseDataPreprocessor', mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], bgr_to_rgb=True), backbone=dict(type='HRNet', in_channels=3, extra=dict(...), init_cfg=dict( type='Pretrained', checkpoint='open-mmlab://msra/hrnetv2_w18')), neck=dict(type='FeatureMapProcessor', concat=True,), head=dict( type='HeatmapHead', in_channels=270, out_channels=21, deconv_out_channels=None, loss=dict(type='KeypointMSELoss', use_target_weight=True), conv_out_channels=(270,), conv_kernel_sizes=(1,), decoder=codec), test_cfg=dict(flip_test=True, flip_mode='heatmap', shift_heatmap=True,))- 整体估计器为
TopdownPoseEstimator(mmpose/models/pose_estimators/topdown.py),属于自顶向下范式:先由检测器给出人手框,再对每个框内实例预测关键点。 - 骨干为 HRNet-w18,四阶段多分辨率结构:stage1(1 个模块、1 分支、BOTTLENECK、64 通道)→ stage2(1 模块、2 分支、BASIC、18/36 通道)→ stage3(4 模块、3 分支、18/36/72 通道)→ stage4(3 模块、4 分支、18/36/72/144 通道,
multiscale_output=True输出多尺度特征)。骨干使用 ImageNet 预训练权重初始化(open-mmlab://msra/hrnetv2_w18)。 - 颈部
FeatureMapProcessor(concat=True)将 HRNet 多尺度特征沿通道拼接,因此检测头输入通道数为 18+36+72+144 =270,与in_channels=270对应。 - 检测头为
HeatmapHead(mmpose/models/heads/heatmap_heads/heatmap_head.py):deconv_out_channels=None表示不使用反卷积上采样(HRNet 已提供高分辨率特征),仅用 1x1 卷积(conv_out_channels=(270,),conv_kernel_sizes=(1,))将 270 通道压缩到out_channels=21(21 个手部关键点),损失为带目标权重的KeypointMSELoss。 test_cfg开启水平翻转测试(TTA):对原图与翻转图分别前向,用flip_mode='heatmap'融合两张热图并取平均(该逻辑在HeatmapHead.predict中实现),shift_heatmap=True用于校正翻转带来的热图偏移。
4. 数据流水线(pipeline)
train_pipeline = [ dict(type='LoadImage'), dict(type='GetBBoxCenterScale'), dict(type='RandomBBoxTransform', rotate_factor=180, scale_factor=(0.7, 1.3)), dict(type='RandomFlip', direction='horizontal'), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='GenerateTarget', encoder=codec), dict(type='PackPoseInputs') ] val_pipeline = [ dict(type='LoadImage'), dict(type='GetBBoxCenterScale'), dict(type='TopdownAffine', input_size=codec['input_size']), dict(type='PackPoseInputs') ]- 训练流水线包含随机旋转(±180 度,手部朝向任意)、随机缩放(0.7~1.3 倍)、水平翻转等增强;
GenerateTarget调用MSRAHeatmap.encode生成无偏高斯热图与关键点权重。 - 验证流水线不做增强,仅做仿射对齐(
TopdownAffine)与打包。
5. 数据加载与评估
dataset_type = 'CocoWholeBodyHandDataset' data_mode = 'topdown' data_root = 'data/coco/'- 训练/验证分别使用
annotations/coco_wholebody_train_v1.0.json与annotations/coco_wholebody_val_v1.0.json,图片前缀为train2017/、val2017/,数据根目录为data/coco/(MMPose 数据集准备约定见 docs/zh_cn/user_guides/prepare_datasets.md)。 - 批量大小 32、2 个 worker、
persistent_workers=True。 - 评估器同时挂载三个指标:
PCKAccuracy(thr=0.2)、AUC、EPE。
三、DarkPose 原理与源码级解析
DarkPose 相比普通 heatmap 方案有两个关键改进,均可从源码中直接验证。
编码端(训练标签):普通generate_gaussian_heatmaps把关键点坐标取整到像素中心再生成高斯(mu = (keypoints[n, k] + 0.5).astype(np.int64)),而 DarkPose 的generate_unbiased_gaussian_heatmaps直接以浮点坐标mu = keypoints[n, k]为中心生成连续高斯,见 mmpose/codecs/utils/gaussian_heatmap.py 第 205-260 行。这消除了"标签取整"带来的系统性编码偏差。
解码端(推理坐标):refine_keypoints_dark(mmpose/codecs/utils/refinement.py 第 49-102 行)的流程为:
- 先用
gaussian_blur对预测热图做高斯模糊调制(核大小与训练 sigma 匹配),并取对数,把高斯分布"拉直"为便于泰勒展开的形式; - 在 argmax 峰值点处计算一阶导
dx, dy与 Hessian 矩阵[[dxx, dxy], [dxy, dyy]](通过相邻像素差分近似); - 求解偏移量
offset = -H^{-1} · g,将预测坐标沿二阶曲面极值方向平移得到亚像素精度坐标。
作为对比,非 Dark 的普通解码refine_keypoints只是向次大值方向移动固定 0.25 像素(同文件第 9-46 行),精度提升有限。DarkPose 的泰勒二阶修正对提高 AUC / 降低 EPE 贡献显著——这也是本配置在 hrnetv2_coco_wholebody_hand.md 中无 Dark 版本(PCK@0.2 0.803 / AUC 0.832 / EPE 4.78)之外的又一档模型选择。
四、数据集:COCO-WholeBody-Hand 的加载细节
数据集类CocoWholeBodyHandDataset定义于 mmpose/datasets/datasets/hand/coco_wholebody_hand_dataset.py,元信息(21 个关键点名称、骨架连接、颜色、sigmas 等)来自 configs/base/datasets/coco_wholebody_hand.py。
21 个关键点的顺序为:wrist(腕部)、thumb1~4(拇指)、forefinger1~4(食指)、middle_finger1~4(中指)、ring_finger1~4(无名指)、pinky_finger1~4(小指)。从_load_annotations实现可以看到两个值得注意的细节:
- 每张 COCO 图像中同时存在左手和右手两套标注(
lefthand_valid/righthand_valid),只有当对应手标注有效且关键点存在(max(hand_kpts) > 0)时才会被解析为一个 top-down 实例,因此一个图像最多贡献两个训练样本; - 关键点可见性
keypoints_visible = np.minimum(1, kpts[..., 2]),被用作keypoint_weights参与 MSE 损失的加权(use_target_weight=True)。
该数据集的单元测试 tests/test_datasets/test_datasets/test_hand_datasets/test_coco_wholebody_hand_dataset.py 验证了 top-down 模式下实例数、元信息键、data_mode校验与bbox_file使用约束等行为,可作为二次开发参考。
五、训练、测试与推理实战
1. 训练
在按文档准备好data/coco/数据后,使用单卡或分布式脚本启动训练:
# 单卡 python tools/train.py configs/hand_2d_keypoint/topdown_heatmap/coco_wholebody_hand/td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256.py # 8 卡分布式 bash tools/dist_train.sh configs/hand_2d_keypoint/topdown_heatmap/coco_wholebody_hand/td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256.py 8训练中每 10 个 epoch 在验证集评测,并按 AUC 保存最优 checkpoint。
2. 测试与指标复现
python tools/test.py configs/hand_2d_keypoint/topdown_heatmap/coco_wholebody_hand/td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256.py <checkpoint路径>三个指标的行为分别由 mmpose/evaluation/metrics/keypoint_2d_metrics.py 中的PCKAccuracy、AUC、EPE实现:
- PCK@0.2:预测点与真值点的归一化距离小于阈值 0.2(默认以 bbox 尺寸归一化)的百分比;
- AUC:改变 PCK 阈值生成的曲线下面积(默认
norm_factor=30像素、20 个阈值),衡量整体定位精度; - EPE:所有关键点的端点平均误差(像素),值越小越好。
3. 推理
可以借助 MMPose 的高层 API 完成单张图片的 top-down 推理,核心调用链为init_model→inference_topdown(见 mmpose/apis/inference.py):
import mmcv from mmpose.apis import inference_topdown, init_model config = 'configs/hand_2d_keypoint/topdown_heatmap/coco_wholebody_hand/td-hm_hrnetv2-w18_dark-8xb32-210e_coco-wholebody-hand-256x256.py' checkpoint = 'checkpoints/hrnetv2_w18_coco_wholebody_hand_256x256_dark.pth' model = init_model(config, checkpoint, device='cuda:0') img = mmcv.imread('a_hand_image.jpg') # 以人手检测框(xyxy 格式)为输入 bboxes = [[100, 120, 260, 280]] results = inference_topdown(model, img, bboxes) pred = results[0].pred_instances print(pred.keypoints, pred.keypoint_scores)若不给bboxes,inference_topdown默认将整张图视为一个实例区域。真实应用中建议先用检测器(如 demo/topdown_demo_with_mmdet.py 所示)定位人手框,再送入本模型。需要可视化时也可参考 demo/image_demo.py 与 demo/inferencer_demo.py。
六、总结与扩展
本文以 MMPose 官方td-hm_hrnetv2-w18_dark配置为线索,完整覆盖了 HRNetv2-W18 骨干 + DarkPose 无偏热图编解码 + COCO-WholeBody-Hand 21 点手部关键点估计这一经典组合:既给出了可直接复现的配置细节(输入 256x256、热图 64x64、sigma=2、210 epoch、PCK@0.2=0.814 / AUC=0.840 / EPE=4.37),也从 MSRAHeatmap 与 refinement.py 源码层面解释了 DarkPose 亚像素解码的数学原理。
在 MMPose 中,同一目录下还提供了 resnet、hourglass、litehrnet 等不同骨干配置,以及 RTMPose 手部模型。若希望获得更快的推理速度或更高精度,可以在理解本文编解码机制的基础上,将这些 backbone 替换进同一套 codec 与训练流程中进行对比实验。
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考