MMPose 3D 手部姿态估计 Demo 实战指南:从 top-down 脚本到 Inferencer 统一推理接口
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
导读
本文是 MMPose 仓库中 demo/docs/en/3d_hand_demo.md 文档的完整实战化展开,围绕"如何在 MMPose 中运行 3D 手部姿态估计 Demo"这一主题,覆盖两条使用路径:基于hand3d_internet_demo.py的 top-down 单图/视频推理,以及基于inferencer_demo.py的统一推理接口。读完本文,你将掌握 3D 手部姿态估计 Demo 的全部命令行参数与运行方式,理解其背后的模型初始化、结果后处理(rebase 高度、左右手深度补偿、关键点旋转)与可视化流程,并能结合仓库源码定位每一步的实现细节。
背景:MMPose 中的 3D 手部姿态估计
MMPose 是 OpenMMLab 出品的姿态估计工具箱,其中hand_3d_keypoint系列任务专注于从单张 RGB 图像回归手部关节的三维坐标。本文关联文档涉及的推理模型是 InterNet(InterHand2.6M 数据集配套基线,论文出处见 configs/hand_3d_keypoint/internet/interhand3d/internet_interhand3d.md 中的 ECCV'2020 引用),其 ResNet-50 版本在 InterHand2.6M test 集上的指标(MPJPE-single 9.47mm、MPJPE-all 11.59mm、MRRPE 29.28mm、APh 0.99)记录在 configs/hand_3d_keypoint/internet/README.md。
3D 手部估计与 2D 姿态估计的关键差异在于:输出不仅是 42 个关键点(左右手各 21 个)的平面坐标,还包含深度(z 轴)、双手相对根节点深度(rel_root_depth)以及左右手分类(hand_type)。这些额外输出直接决定了 Demo 脚本中后处理与可视化逻辑的复杂度,也解释了为什么 3D 手部 Demo 需要专门的推理脚本与专门的 Inferencer。
方式一:使用hand3d_internet_demo.py进行 top-down 推理
脚本入口与基本用法
仓库提供了 demo/hand3d_internet_demo.py 这一 top-down 风格脚本:先由外部检测器(或直接给定 GT 检测框)提供手部包围框,再对每个手部区域裁剪、缩放后送入姿态模型回归 3D 关键点。文档给出的完整命令为:
python demo/hand3d_internet_demo.py \ ${MMPOSE_CONFIG_FILE} ${MMPOSE_CHECKPOINT_FILE} \ --input ${INPUT_FILE} \ --output-root ${OUTPUT_ROOT} \ [--save-predictions] \ [--gt-joints-file ${GT_JOINTS_FILE}]\ [--disable-rebase-keypoint] \ [--show] \ [--device ${GPU_ID or CPU}] \ [--kpt-thr ${KPT_THR}] \ [--show-kpt-idx] \ [--show-interval] \ [--radius ${RADIUS}] \ [--thickness ${THICKNESS}]其中--gt-joints-file用于在给定 GT json 文件的前提下进行单图测试;--input支持图片、视频甚至webcam(摄像头)输入,脚本内部通过mimetypes.guess_type判断输入类型,见 demo/hand3d_internet_demo.py。
全部命令行参数详解
以下参数均以 demo/hand3d_internet_demo.py 中parse_args()的实现为准,包含默认值与作用:
| 参数 | 默认值 | 说明 |
|---|---|---|
config | 必填 | 模型配置文件路径(位置参数) |
checkpoint | 必填 | 模型权重文件路径(位置参数) |
--input | '' | 图片/视频/摄像头(webcam)输入路径,为空时脚本会直接assert报错 |
--output-root | '' | 可视化结果输出根目录;为空时默认不保存可视化图,且main()中会assert args.show or (args.output_root != '') |
--save-predictions | False | 是否将预测结果以 JSON 形式保存到${output_root}/results_${输入名}.json(见 demo/hand3d_internet_demo.py) |
--disable-rebase-keypoint | False | 是否禁用关键点高度"回正"(rebase)。默认开启 rebase,即将预测 3D 姿态的最低点的高度置为 0,使其"落地",便于可视化 |
--show | False | 是否在弹窗中实时显示结果 |
--device | cpu | 推理设备,可传 GPU id(如0)或cpu |
--kpt-thr | 0.3 | 关键点可视化分数阈值,低于该分数的关键点不绘制 |
--show-kpt-idx | False | 是否在可视化图中标注关键点的索引号 |
--show-interval | 0 | 视频/摄像头逐帧显示时的间隔秒数 |
--radius | 3 | 可视化时关键点圆点半径(会写入model.cfg.visualizer.radius) |
--thickness | 1 | 可视化时骨骼连线粗细(会写入model.cfg.visualizer.line_width) |
完整示例:使用预训练 InterNet 模型推理单张图片
文档给出了一个可直接复制的示例命令,使用 InterNet ResNet-50 模型与测试样例图tests/data/interhand2.6m/image69148.jpg:
python demo/hand3d_internet_demo.py \ configs/hand_3d_keypoint/internet/interhand3d/internet_res50_4xb16-20e_interhand3d-256x256.py \ https://download.openmmlab.com/mmpose/hand3d/internet/res50_intehand3dv1.0_all_256x256-42b7f2ac_20210702.pth \ --input tests/data/interhand2.6m/image69148.jpg \ --save-predictions \ --output-root vis_results对应的模型配置文件为 configs/hand_3d_keypoint/internet/interhand3d/internet_res50_4xb16-20e_interhand3d-256x256.py,其核心结构如下:
- codec:使用
Hand3DHeatmap,输入尺寸 256×256,热图尺寸 64×64×64(深度方向 64 层),根节点热图尺寸 64,sigma=2.5,max_bound=255; - 模型:
TopdownPoseEstimator+ ResNet-50 backbone +InternetHead。InternetHead由三个子头构成(见配置中的keypoint_head_cfg、root_head_cfg、hand_type_head_cfg),分别预测 42 关键点的 3D 热图、根节点深度热图与左右手类型; - 优化与调度:Adam(lr=2e-4)、MultiStepLR(milestones=[15, 17],gamma=0.1),20 个 epoch;
- 数据与评测:InterHand3D 数据集、训练时使用 GT 根节点深度(
use_gt_root_depth=True),评测指标为InterHandMetric的 MPJPE / MRRPE / HandednessAcc。
运行成功后,可视化图会保存在vis_results目录,且因为传入了--save-predictions,预测结果 JSON 会被保存到vis_results/results_image69148.json。脚本完成时会通过print_log输出保存路径(见 demo/hand3d_internet_demo.py)。
3D 手部后处理逻辑:rebase、深度补偿与坐标旋转
hand3d_internet_demo.py中process_one_image()(demo/hand3d_internet_demo.py)集中体现了 3D 手部特有的后处理,理解它才能真正读懂输出结果:
- 相对根深度补偿:
keypoints[:, 21:, 2] += rel_root_depth,把模型预测的左右手相对根节点深度叠加到右手(索引 21 之后)关节的 z 坐标上,从而将两只手对齐到同一深度坐标系; - 按手型缩放分数:
scores[:, :21] *= hand_type[:, [0]]、scores[:, 21:] *= hand_type[:, [1]],用模型预测的左右手类型置信度门控两侧关键点的得分;若分数最大值超过 1,再统一除以 255 归一化; - 坐标轴旋转:乘以旋转矩阵
vis_R = [[1,0,0],[0,0,-1],[0,1,0]],把 z 轴对齐到"高度"方向,以便在 3D 视图(Pose3dLocalVisualizer的axis_azimuth=-115、axis_limit=200、axis_elev=15)中自然显示; - rebase 高度回正:默认将 z 坐标整体平移,使置信度大于 0 的关键点中最低点的 z 值为 0("落地")。由于模型预测的是相对深度而非全局位置,这一步骤对可视化至关重要;若模型输出本身包含全局位置,可通过
--disable-rebase-keypoint关闭。
值得说明的是,同样的后处理逻辑在 Inferencer 的
Hand3DInferencer.forward()中几乎逐行复现(mmpose/apis/inferencers/hand3d_inferencer.py),因此两种方式产出的结果语义完全一致。
视频与摄像头输入
当--input为视频路径或webcam时,脚本进入视频分支(demo/hand3d_internet_demo.py):
- 逐帧调用
process_one_image推理并可视化; - 若指定了
--output-root,会以mp4v编码、25 FPS 创建cv2.VideoWriter输出视频(摄像头输入时输出文件自动加.mp4后缀); - 若指定
--save-predictions,每帧的预测结果会以frame_id + instances的形式追加到列表,最终统一写入 JSON; --show模式下按 ESC 键退出,并通过time.sleep(args.show_interval)控制播放节奏。
方式二:使用 Inferencer 统一推理接口
为什么用 Inferencer
相比手写脚本,MMPoseInferencer提供了统一推理界面:无需显式指定配置文件路径与权重路径,直接用模型别名(model alias)即可;输入格式更丰富(图片路径、视频路径、图片文件夹、摄像头);同时封装了预处理、前向、可视化、后处理四个阶段的关键字参数。其底层分发逻辑在 mmpose/apis/inferencers/mmpose_inferencer.py:当pose3d参数包含'hand3d'时,自动实例化Hand3DInferencer,否则实例化Pose3DInferencer。
一条命令完成 3D 手部推理
文档给出的示例:
python demo/inferencer_demo.py tests/data/interhand2.6m/image29590.jpg --pose3d hand3d --vis-out-dir vis_results/hand3d该命令的作用:
tests/data/interhand2.6m/image29590.jpg为输入图片;--pose3d hand3d选择模型别名hand3d,权重会从 model metafile 自动加载;--vis-out-dir vis_results/hand3d指定可视化结果输出目录。
更多可用参数(均定义在 demo/inferencer_demo.py):
| 参数 | 默认值 | 说明 |
|---|---|---|
--pose3d-weights | None | 自定义 3D 模型权重路径,不指定时从 metafile 加载 |
--pose2d/--pose2d-weights | None | 2D 姿态模型(非 hand3d 的 3D 推理链需要) |
--det-model/--det-weights/--det-cat-ids | None/None/0 | 手部检测器配置/权重/类别 id |
--device | None | 推理设备,不指定时自动选择可用设备 |
--show-progress | False | 推理时显示进度条 |
--show | False | 弹窗显示结果 |
--draw-bbox | False | 是否绘制包围框 |
--bbox-thr | 0.3 | 检测框分数阈值 |
--nms-thr | 0.3 | 检测框 NMS 的 IoU 阈值 |
--kpt-thr | 0.3 | 关键点可视化阈值 |
--disable-rebase-keypoint | False | 同前文,禁用高度回正 |
--num-instances | 1 | 每帧可视化的 3D 姿态数量,小于 0 时取第一帧检测到的实例数 |
--radius | 3 | 关键点半径 |
--thickness | 1 | 连线粗细 |
--vis-out-dir | '' | 可视化结果保存目录 |
--pred-out-dir | '' | 预测结果保存目录 |
--show-alias | False | 打印所有可用模型别名 |
在 Python 代码中调用 Inferencer
除了命令行,MMPoseInferencer也可以在 Python 中直接使用,例如:
from mmpose.apis.inferencers import MMPoseInferencer inferencer = MMPoseInferencer(pose3d='hand3d') for result in inferencer( 'tests/data/interhand2.6m/image29590.jpg', vis_out_dir='vis_results/hand3d', return_datasamples=True): # result 中同时包含 'visualization' 与 'predictions' 两个键 print(result['predictions'])这一调用路径对应的实现细节:
MMPoseInferencer.__call__(mmpose/apis/inferencers/mmpose_inferencer.py)会先把vis_out_dir/pred_out_dir之外的 kwargs 按preprocess_kwargs、forward_kwargs、visualize_kwargs、postprocess_kwargs四类分发到对应阶段;- 输入为
webcam时自动打开摄像头并将show置为True;输入为目录/视频时会切换为逐帧批处理; Hand3DInferencer.preprocess_single(mmpose/apis/inferencers/hand3d_inferencer.py)在有检测器时对检测框执行分数过滤(bbox_thr)与 NMS(nms_thr),无检测框时退化为以整张图像为框;- 预测结果的保存由
postprocess_kwargs['pred_out_dir']控制,支持return_datasample=True时返回PoseDataSample对象。
模型别名与权重加载
Hand3DInferencer的构造函数(mmpose/apis/inferencers/hand3d_inferencer.py)说明:model可以是模型别名(如'hand3d')、metafile 中的配置名或配置路径;weights不指定时从 metafile 自动加载。此外初始化时会执行revert_sync_batchnorm,并把数据集的dataset_meta写入 visualizer。运行python demo/inferencer_demo.py --show-alias可查看当前 scope 下所有可用别名。
两种方式的对比与选型建议
| 维度 | hand3d_internet_demo.py | inferencer_demo.py --pose3d hand3d |
|---|---|---|
| 配置/权重指定 | 必须显式给出 config 与 checkpoint 路径 | 使用模型别名,自动从 metafile 加载 |
| 输入类型 | 图片 / 视频 / webcam | 图片 / 视频 / 图片目录 / webcam,且支持 numpy 数组 |
| 检测框来源 | 依赖 GT json 或外部检测结果 | 可选内置检测器(--det-model)或整图回退 |
| 结果导出 | --save-predictions导出 JSON | --pred-out-dir导出 JSON,支持return_datasample |
| 可视化控制 | --radius/--thickness/--kpt-thr/--show-kpt-idx等 | 同名参数,另有--num-instances、--skeleton-style等 |
| 适用场景 | 复现文档示例、深度调试单条推理链 | 日常推理、批量处理、二次开发集成 |
两条路径共享同一套 3D 后处理(rebase、深度补偿、坐标旋转)与同一套Pose3dLocalVisualizer可视化参数(axis_azimuth=-115、axis_limit=200、axis_elev=15)。若只是快速验证效果或接入业务,推荐 Inferencer;若要细粒度控制每个阶段或配合 GT 框评测,推荐hand3d_internet_demo.py。
测试验证:Inferencer 路径的行为保障
仓库测试 tests/test_apis/test_inferencers/test_hand3d_inferencer.py 从四个维度验证了 3D 手部 Inferencer 的行为:
- 初始化:
Hand3DInferencer(model='hand3d')能正确加载出torch.nn.Module模型实例; - 单图调用:输入图片路径或 numpy 数组结果一致,
predictions中包含keypoints,且每只手输出 42 个关键点(左右手各 21); - 目录批处理:输入目录时对目录下所有图片推理,可分别只保存可视化(
vis_out_dir)或同时保存可视化与预测(out_dir,输出到visualizations/与predictions/两个子目录); - 数据格式:
return_datasample=True时返回PoseDataSample。
同时,tests/test_apis/test_inferencers/test_mmpose_inferencer.py 中的test_hand3d_call验证了MMPoseInferencer(pose3d='hand3d')这条完整链路的可用性。这些测试所使用的样例图片tests/data/interhand2.6m/image29590.jpg也正是本文 Inferencer 示例的输入,读者可以直接复现测试环境下的推理行为。
小结
本文完整展开了 demo/docs/en/3d_hand_demo.md 的两条 3D 手部推理路径:hand3d_internet_demo.py适合需要显式控制 config/checkpoint、配合 GT 框做单图验证的场合;inferencer_demo.py --pose3d hand3d则以模型别名 + 自动权重加载的方式,覆盖图片、目录、视频、摄像头等多种输入,适合快速验证与工程集成。无论走哪条路径,输出都经过同样的 3D 后处理(相对根深度补偿、手型分数门控、坐标旋转、高度 rebase),因此可视化与预测结果语义一致。建议读者结合 demo/hand3d_internet_demo.py、mmpose/apis/inferencers/hand3d_inferencer.py 与对应模型配置 configs/hand_3d_keypoint/internet/interhand3d/internet_res50_4xb16-20e_interhand3d-256x256.py 逐层阅读,以掌握从输入到可视化完整链路的底层原理。
【免费下载链接】mmposeOpenMMLab Pose Estimation Toolbox and Benchmark.项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考