MMPose 2D手部关键点推理实战:从MMDetection手部检测到Top-Down姿态估计的Demo全解
MMPose 2D手部关键点推理实战:从MMDetection手部检测到Top-Down姿态估计的Demo全解
本指南面向需要在真实图像或视频上完成 2D 手部关键点(Hand Keypoint)检测的开发者,完整讲解 MMPose 仓库中手部姿态推理的三种主流方式:基于 MMDetection 检测器的 Top-Down 流程(topdown_demo_with_mmdet.py)、统一推理接口(inferencer_demo.py)以及推理加速技巧。读完本文,你将能够基于仓库自带的手部检测器与预训练模型,一行命令跑通"先检测手部框、再估计 21 个关键点"的完整推理链路,并理解其底层参数与源码实现。
前置条件与核心思路
2D 手部关键点检测采用经典的 Top-Down(自上而下)两阶段流程:
- 手部框检测:先用目标检测器(如 MMDetection 中的 RTMDet)从图像中定位每一只手的位置;
- 关键点估计:将裁剪出的手部区域送入 MMPose 的 Top-Down 姿态估计模型(如 RTMPose、HRNet),输出腕部(wrist)与五根手指共 21 个关键点坐标。
运行本指南中的 Demo 前,需要满足以下条件:
- 已安装 MMPose 及其依赖;
- 已安装 MMDetection)。
手部框检测模型准备
Top-Down 流程首先需要一只手部检测器。MMPose 仓库为手部场景准备了专用检测模型,其配置与权重索引在 手部检测模型文档 中列出:
| 网络结构 | 数据集 | Box AP | 配置文件 |
|---|---|---|---|
| Cascade R-CNN X-101-64x4d-FPN-1class | OneHand10K test | 0.817 | demo/mmdetection_cfg/cascade_rcnn_x101_64x4d_fpn_1class.py |
| RTMDet-nano | OneHand10K test | 0.760 | demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py |
这两个检测器都在 OneHand10K 数据集上训练,其中 RTMDet-nano 在精度与速度之间取得了较好平衡,是官方 Demo 默认采用的手部检测器。其配置文件 demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py 值得关注:
- 输入尺寸固定为
320×320(input_shape = 320); - 采用 RTMDet 的 nano 轻量化配置(
deepen_factor=0.33、widen_factor=0.25、use_depthwise=True); - 检测头将类别数改为
num_classes=1,即只预测"手"这一类; - 训练数据由 OneHand10K、FreiHand、RHD、HalpeHand 等多个手部数据集拼接而成(
ConcatDataset),测试则使用 OneHand10K 的 test 划分。
2D手部图像Demo:MMDetection检测 + MMPose姿态估计
基础命令
官方提供的核心脚本是 topdown_demo_with_mmdet.py,其完整用法如下:
python demo/topdown_demo_with_mmdet.py \
${MMDET_CONFIG_FILE} ${MMDET_CHECKPOINT_FILE} \
${MMPOSE_CONFIG_FILE} ${MMPOSE_CHECKPOINT_FILE} \
--input ${INPUT_PATH} [--output-root ${OUTPUT_DIR}] \
[--show] [--device ${GPU_ID or CPU}] [--save-predictions] \
[--draw-heatmap ${DRAW_HEATMAP}] [--radius ${KPT_RADIUS}] \
[--kpt-thr ${KPT_SCORE_THR}] [--bbox-thr ${BBOX_SCORE_THR}]
以 RTMPose-m 手部模型(在 5 个公开手部数据集上训练,即 Hand5 模型)为例,官方给出可直接运行的完整命令:
python demo/topdown_demo_with_mmdet.py \
demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmdet_nano_8xb32-300e_hand-267f9c8f.pth \
configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-hand5_pt-aic-coco_210e-256x256-74fb594_20230320.pth \
--input tests/data/onehand10k/9.jpg \
--show --draw-heatmap
该命令的四组必选参数含义如下:
| 参数 | 本例取值 | 说明 |
|---|---|---|
MMDET_CONFIG_FILE |
demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py |
手部检测器配置 |
MMDET_CHECKPOINT_FILE |
RTMDet-nano 手部检测权重 | 检测器权重(URL 或本地路径) |
MMPOSE_CONFIG_FILE |
configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py |
手部姿态估计模型配置 |
MMPOSE_CHECKPOINT_FILE |
RTMPose-m Hand5 权重 | 姿态模型权重(URL 或本地路径) |
若使用的姿态模型为热力图类(Heatmap-based),加上 --draw-heatmap 参数后,预测热力图会与关键点一起被可视化叠加在图像上。
将可视化结果保存到磁盘
不带任何输出参数时结果只会在弹窗中显示。指定 --output-root 即可将可视化图片/视频写入指定目录,同时保留 --show 弹窗:
python demo/topdown_demo_with_mmdet.py \
demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmdet_nano_8xb32-300e_hand-267f9c8f.pth \
configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-hand5_pt-aic-coco_210e-256x256-74fb594_20230320.pth \
--input tests/data/onehand10k/9.jpg \
--output-root vis_results --show --draw-heatmap
如果需要把预测的关键点坐标结果(而非可视化图像)保存为 JSON 文件,请追加 --save-predictions。从源码看,该参数要求同时指定 --output-root,预测结果会写入 {output_root}/results_{输入文件名}.json,其中包含检测器输出与姿态估计结果(见 demo/topdown_demo_with_mmdet.py 与 demo/topdown_demo_with_mmdet.py)。
在CPU上运行
没有 GPU 时,只需将 --device 指定为 cpu:
python demo/topdown_demo_with_mmdet.py \
demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmdet_nano_8xb32-300e_hand-267f9c8f.pth \
configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-hand5_pt-aic-coco_210e-256x256-74fb594_20230320.pth \
--input tests/data/onehand10k/9.jpg \
--show --draw-heatmap --device cpu
2D手部视频Demo
视频与图像共享完全相同的接口与脚本。唯一的区别是 --input 既可以是本地视频文件路径,也可以是指向视频文件的 URL 链接(脚本会按输入类型自动分流处理,见 demo/topdown_demo_with_mmdet.py)。处理视频时还会自动写回一个 25 FPS 的 mp4 输出文件(demo/topdown_demo_with_mmdet.py)。
python demo/topdown_demo_with_mmdet.py \
demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmdet_nano_8xb32-300e_hand-267f9c8f.pth \
configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py \
https://download.openmmlab.com/mmpose/v1/projects/rtmposev1/rtmpose-m_simcc-hand5_pt-aic-coco_210e-256x256-74fb594_20230320.pth \
--input data/tests_data_nvgesture_sk_color.avi \
--output-root vis_results --kpt-thr 0.1
视频示例中把 --kpt-thr 调低到 0.1,以便在低置信度场景下保留更多关键点用于可视化(默认值为 0.3)。播放窗口按 ESC 键即可提前退出(demo/topdown_demo_with_mmdet.py)。
2D手部Demo的Inferencer统一推理接口
除了拼接检测器+姿态模型的传统方式,MMPose 还提供更简洁的 Inferencer(统一推理接口),通过模型别名(Model Alias)代替繁琐的配置文件和权重路径,并支持图像路径、视频路径、图像文件夹、摄像头等多种输入。其入口为 demo/inferencer_demo.py,底层由 MMPoseInferencer 实现。
python demo/inferencer_demo.py tests/data/onehand10k \
--pose2d hand --vis-out-dir vis_results/onehand10k \
--bbox-thr 0.5 --kpt-thr 0.05
该命令会:
- 对
tests/data/onehand10k目录下的全部图片逐张推理(--pose2d hand自动选用手部姿态模型别名); - 将可视化结果保存到
vis_results/onehand10k目录(--vis-out-dir); - 用
--bbox-thr 0.5过滤掉置信度低于 0.5 的检测框,用--kpt-thr 0.05过滤低分关键点。
Inferencer 的常用参数(见 demo/inferencer_demo.py):
| 参数 | 作用 |
|---|---|
--pose2d |
2D 姿态模型:支持模型别名或配置文件路径 |
--pose2d-weights |
自定义姿态模型权重路径,缺省时从 metafile 自动加载 |
--det-model / --det-weights |
自定义检测模型(配置路径/别名 + 权重) |
--device |
推理设备,缺省时自动选择可用设备 |
--bbox-thr / --nms-thr / --kpt-thr |
检测框分数阈值、NMS IoU 阈值、关键点分数阈值 |
--draw-heatmap |
是否绘制热力图 |
--show-alias |
打印当前 scope 下所有可用的模型别名列表 |
此外,Inferencer 也支持将预测结果(prediction)保存到磁盘(对应 --pred-out-dir 参数),便于下游做数据分析或二次开发。--show-alias 可以快速列出可用别名,例如执行 python demo/inferencer_demo.py --show-alias 即可查看包括 hand 在内的全部模型别名与其对应模型名(demo/inferencer_demo.py)。
关键参数与底层调用链详解
topdown_demo_with_mmdet.py 的完整参数集(默认值与含义,来自 demo/topdown_demo_with_mmdet.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--input |
'' |
输入图像/视频路径,也可为 URL |
--show |
False |
是否弹出窗口实时显示 |
--output-root |
'' |
输出文件根目录,缺省不保存可视化结果 |
--save-predictions |
False |
是否将预测结果保存为 JSON |
--device |
cuda:0 |
推理设备(GPU ID 或 cpu) |
--det-cat-id |
0 |
检测模型的目标类别 ID(手部检测器为 0) |
--bbox-thr |
0.3 |
检测框分数阈值 |
--nms-thr |
0.3 |
检测框 NMS 的 IoU 阈值 |
--kpt-thr |
0.3 |
关键点可视化分数阈值 |
--draw-heatmap |
False |
绘制模型预测的热力图 |
--show-kpt-idx |
False |
是否显示关键点索引编号 |
--skeleton-style |
mmpose |
骨架绘制风格:mmpose 或 openpose |
--radius |
3 |
关键点绘制半径 |
--thickness |
1 |
骨架连线粗细 |
--show-interval |
0 |
视频逐帧显示的间隔秒数 |
--alpha |
0.8 |
检测框透明度 |
--draw-bbox |
False |
是否绘制实例检测框 |
从源码调用链看,单帧推理的核心流程集中在 process_one_image(demo/topdown_demo_with_mmdet.py):
inference_detector(detector, img)用 MMDetection 检测手部框;- 按
det_cat_id与bbox_thr过滤检测结果,再用nms抑制重叠框; inference_topdown(pose_estimator, img, bboxes)对每个框执行 Top-Down 关键点估计;merge_data_samples合并多实例结果,交由Visualizer渲染;- 若
--draw-heatmap为真,会在初始化姿态模型时注入test_cfg.output_heatmaps=True(demo/topdown_demo_with_mmdet.py),从而让模型在推理时额外输出热力图。
推理加速:关闭测试时翻转
对于手部关键点模型,最直接的加速手段是修改配置文件中的 flip_test 开关。以 OneHand10K 上的 HRNet 模型为例:
- 配置文件:configs/hand_2d_keypoint/topdown_heatmap/onehand10k/td-hm_hrnetv2-w18_8xb64-210e_onehand10k-256x256.py;
- 默认配置中
model.test_cfg为flip_test=True,即测试时对图像做水平翻转并融合两次推理结果(flip_mode='heatmap'、shift_heatmap=True,见该配置第 91-95 行)。
将 model.test_cfg.flip_test 改为 False,可跳过翻转推理分支,显著减少推理耗时(约接近减半的前向计算量),代价是精度略有下降,适合对速度敏感、或翻转对称性不敏感的手部场景。
这一机制的底层实现可在模型头部源码中确认:RTMCCHead、SimCCHead、RTMWHead 等头部在 predict 时都会读取 test_cfg.get('flip_test', False),一旦开启,则从数据样本的 metainfo 中取出 flip_indices(关键点镜像索引),对翻转分支的输出进行还原与融合。例如 mmpose/models/heads/coord_cls_heads/rtmcc_head.py 中展示了 SimCC 坐标分类头的翻转融合实现,热力图头则在 mmpose/models/heads/heatmap_heads/ae_head.py 中按 flip_mode='heatmap' 处理。
若希望进一步提速,还可以换用更轻量的手部检测器(例如将 RTMDet-nano 替换为其他轻量检测模型),检测阶段本身也是 Top-Down 流程的主要开销之一。
模型与数据集配置深度解析
RTMPose-m Hand5:多数据集训练的实时手部模型
官方 Demo 使用的姿态模型 configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py 具备以下特征:
- Codec:采用
SimCCLabel(SimCC 坐标分类),输入 256×256,simcc_split_ratio=2.0,即把坐标预测转化为一维坐标分类问题; - Backbone:复用 MMDetection 的
CSPNeXt(_scope_='mmdet'),arch 为 P5,宽深因子对应 RTMPose-m; - Head:
RTMCCHead,输出通道out_channels=21(21 个手部关键点),配合KLDiscretLoss与GAU(门控注意力单元); - 数据集:名为 "Hand5",即在 COCO-Wholebody-Hand、OneHand10K、FreiHand2d、RHD2d、Halpe 五个公开数据集上联合训练(
CombinedDataset),因此泛化能力更强; - 评估指标:
PCKAccuracy(thr=0.2)、AUC、EPE三个手部关键点标准指标(configs/hand_2d_keypoint/rtmpose/hand5/rtmpose-m_8xb256-210e_hand5-256x256.py)。
该模型的详细指标(包括在 COCO-Wholebody-Hand 与 Hand5 上的 PCK@0.2、AUC、EPE 以及 FLOPS)可查阅 configs/hand_2d_keypoint/rtmpose/hand5/rtmpose_hand5.md。
HRNet OneHand10K:经典热力图方案
若需要对比热力图方案,可参考 OneHand10K 上的 HRNetv2-w18 配置 configs/hand_2d_keypoint/topdown_heatmap/onehand10k/td-hm_hrnetv2-w18_8xb64-210e_onehand10k-256x256.py:
- Codec 为
MSRAHeatmap,输入 256×256,输出 64×64 热力图,sigma=2; - Backbone 为
HRNet(HRNetv2-w18),Head 为HeatmapHead(out_channels=21),Loss 为KeypointMSELoss; - 训练 210 个 epoch,optimizer 为 Adam(lr=5e-4),评价指标同样为 PCK/AUC/EPE。
21个手部关键点的定义
手部关键点采用业界通用的 21 点定义:1 个腕部(wrist)+ 每根手指 4 个关节点(共 20 个),顺序为拇指(thumb)、食指(forefinger)、中指(middle_finger)、无名指(ring_finger)、小指(pinky_finger)。每个关键点的名称、序号、颜色与骨架连接关系(含各手指的颜色区分)定义在 configs/base/datasets/coco_wholebody_hand.py 中,可视化时会依据该文件中的 keypoint_info、skeleton_info 与 keypoint_colors 进行绘制。
总结与下一步
本文围绕 MMPose 的 2D 手部关键点推理,给出了三种可立即落地的方案:传统 MMDetection+MMPose 两阶段 Demo(图像/视频/CPU/结果保存全覆盖)、Inferencer 统一推理接口(模型别名+目录/摄像头输入),以及通过关闭 flip_test 实现的推理加速。同时从源码与配置层面解释了检测-估计的完整调用链、关键参数默认值以及 21 点手部骨架的数据定义。
如果希望深入底层,建议继续阅读仓库内的以下资源:
- 推理脚本本体:demo/topdown_demo_with_mmdet.py 与 demo/inferencer_demo.py;
- 手部检测器配置:demo/mmdetection_cfg/rtmdet_nano_320-8xb32_hand.py 及 检测模型索引文档;
- 手部姿态模型配置:configs/hand_2d_keypoint/rtmpose/hand5/ 与 configs/hand_2d_keypoint/topdown_heatmap/onehand10k/;
- 头部翻转融合实现:mmpose/models/heads/coord_cls_heads/rtmcc_head.py 与 mmpose/models/heads/coord_cls_heads/simcc_head.py;
- 21 点手部骨架元信息:configs/base/datasets/coco_wholebody_hand.py。
以此为起点,你可以进一步将该推理链路集成到手势识别、人机交互、AR/VR 手部追踪等实际业务中。