简介:面向计算机视觉与人体姿态估计学习者的Human3.6M 3D人体姿态数据集获取资源,提供基于Python的完整下载、解压、预处理工具链,适用于需要快速获取并解析该数据集的科研人员与开发者。资源共14个文件,包含4个Python脚本(负责下载、解压、元数据处理等),以及配置文件、Dockerfile、依赖清单、说明文档等辅助文件,压缩包仅34KB,便于下载和部署。已有3624人学习浏览。通过脚本可自动获取Human3.6M数据集,并支持后续的3D关节位置提取与格式转换,帮助用户跳过繁琐的手动下载与前期处理,直接聚焦于姿态估计模型构建与训练,是入门和研究中实用的效率工具。 如果你准备用Python做3D人体姿态估计,那你迟早会被同一个数据集拦住:Human36M。这个数据集在论文里出现得实在太频繁,几乎所有姿势估计模型的评测都绕不开它;但真要把它下载下来、用Python读进内存,新手的体验往往是:官网申请要好几天,下完的MAT文件打不开,好不容易读出来坐标又对不上。这篇文章我就把申请、下载、解析、投影、可视化整条链路完整讲一遍,刚起步的同学可以直接照着做,踩过的坑也都给你标好了。
1. Human3.6M的家底:为什么它在一众数据集里地位这么高
3D人体姿态估计的任务,是从一张图或者一段视频里恢复人体各关节的三维坐标。这个任务看起来简单,其实对数据集的要求很高,因为你需要“真值”是精确的3D坐标,而不是从2D逆推出来的近似结果。Human3.6M就是在这种需求下诞生的经典基准数据集。
它由Max Planck智能系统研究所于2014年随CVPR论文《Reconstructing 3D Human Pose from 2D Detections》发布。名字里的“3.6M”指的是动作捕捉数据总共提供约360万个3D姿态标注。采集方式是让受试者在室内完成一系列日常动作,同时用Vicon动作捕捉系统记录高精度的3D关节坐标,再用4个数字摄像机从不同视角同步拍摄画面。视频帧率是50Hz,动作捕捉帧率是100Hz,也就是说每个动作序列不仅有多视角的RGB视频,还有对应的、一帧不差的3D骨架真值。
不过有个地方第一次接触的人很容易懵:数据里一共采集了多位受试者,对外公开的只有7位,分别是S1、S5、S6、S7、S8、S9、S11。编号不是从S1到S11连续排列的,没有S2、S3、S4、S10,这是正常现象,不是你下载漏了。动作类型一般是15类,像Directions、Discussion、Eating、Greeting、Jumping、Phoning、Posing、Purchases、Sitting、SittingDown、Smoking、Waiting、WalkDog、Walking、WalkTogether,每位受试者的每个动作还会录制多个重复序列。
骨架标注方面,官方原始标注的关节数量比较密,大家实际用的时候多整理成17个关键关节点这种形式,也有工程实现会重排成COCO或MPII的骨骼顺序。评测指标里见得最多的是两个:一个是MPJPE,直接计算预测关节和真值关节的平均欧氏距离,单位是毫米;另一个是PA-MPJPE,先把预测结果做一次刚性对齐再计算误差。
还有一个绕不开的概念叫Protocol。Protocol 1输入用的2D关键点来自真实标注,只评测从2D升到3D的部分;Protocol 2输入用的2D关键点来自检测器,比如CPM或者Mask R-CNN先检测出2D关键点,再交给3D模型,更贴近真实应用,但整体误差也会更大。论文里常见的训练测试划分是用S1、S5、S6、S7、S8训练,S9、S11测试。你看到“Human3.6M under Protocol 2”这类表述,指的就是这套设置。
2. 数据获取的三条路:官方申请、开源脚本、NPZ预处理版,各有各的门道
数据集这么经典,拿到手却没那么容易。我实际走过三条路,分别说下体验和适用场景。
2.1 官网申请流程与注意事项
最正统的途径是去Human3.6M官网提交申请。流程大概是:
- 打开官网,找到数据申请入口。
- 填写一张表单,内容包括姓名、所属单位、邮箱、研究用途。
- 提交后等待人工审核,快的话一两天,慢的话可能要一周。
- 审核通过后会收到一封激活邮件,里面包含下载链接和简单的使用说明。
- 进入下载页面,按subject、action组织好的文件夹就在那,可以用浏览器直接点,也可以用支持断点续传的工具批量拉取。
有几个坑一定要提前知道。第一,申请邮箱尽量用机构邮箱,个人邮箱被拒的概率很大;第二,下载链接通常有时效性,我遇到过48小时内必须下载完的情况,文件还没下完链接就先失效了,最后只好重新申请;第三,完整数据集非常大,视频部分占大头,动辄几十GB到上百GB,下载前千万先检查磁盘空间和网络稳定性。
2.2 开源项目里附带的下载脚本
第二种途径是直接找开源项目里已经写好的下载脚本。GitHub上做3D姿态估计的项目不少,VideoPose3D、ST-GCN这类经典仓库里都带了数据处理相关的脚本,有些项目甚至会直接在release里挂好预处理后的数据文件。走这条路的好处是省心,不用自己写申请理由,也不用去跟原始目录结构较劲。坏处是版本比较杂,有的脚本是针对老版文件命名的,有的预处理环节已经做了自己项目的自定义修改,用之前得把处理流程看清楚。
2.3 NPZ预处理版本才是最省心的选择
如果只是想快速跑通一个baseline,或者验证一个想法,我强烈建议直接找NPZ预处理版本。最典型的是Martinez等人在ICCV 2017工作里发布的data_3d_h36a.npz,这个文件把官方MAT和XML里的核心信息抽出来,统一整理成一个numpy压缩包:2D关键点、3D关键点、相机参数、训练测试划分、动作名称全都排好,np.load一下就能用。很多后来的开源项目都基于这个文件二次fork。
但用预处理版也有前提,你得搞清楚它的关节顺序是什么、坐标单位是什么、2D坐标到底是GT投影还是检测器输出。我见过有人拿到的数据姿态画出来是倒的,就是因为关节索引没对齐。
三条路径对比下来,大致是这样:
| 获取途径 | 适合场景 | 主要风险 |
|---|---|---|
| 官网申请 | 做数据pipeline、多视角任务、视频输入 | 审核周期长、下载链接会过期、总量大 |
| 开源项目脚本 | 快速复现某个论文 | 版本混杂、可能被二次裁剪 |
| NPZ预处理版 | 快速实验、对比SOTA | 缺少原始视频、动作序列可能被精简 |
3. 从文件到Python数组:MAT/XML/NPZ格式与加载代码
数据下载完,真正的考验才开始。不同途径拿到的文件格式差别很大,你得先搞清楚里面装的是什么。
3.1 官网完整包的目录结构
以官网下载并解压后的常见结构为例,大致是这样:
human3.6m/ ├── S1/ │ ├── Directions_1/ │ │ ├── video.mp4 │ │ └── ... │ ├── Discussion_1/ │ └── ... ├── S5/ ├── metadata.xml └── ...每个subject下面按“动作名_重复序号”建文件夹,里面放对应动作的视频和动作捕捉原始数据。如果是做视频输入的模型,这套目录就是你的数据源。
3.2 MAT文件命名:新老版本差别很大
动作捕捉数据通常会整理成MAT文件保存,但文件命名有两种常见风格,老版本长这样:
S_01_act_01_sub_01_ca_01.mat新版本长这样:
subject_01_act_01_rr_01_ca_01.mat含义都是:subject编号、动作编号、重复序号、相机编号。很多网上流传的解析脚本只认老版本命名,换到新下载的文件上直接报错。如果你下载的是新命名,务必先看脚本里怎么拼文件名,或者统一用glob匹配。
3.3 用scipy.io.loadmat读原始MAT
MAT文件用scipy.io.loadmat读取。但H36M的MAT文件不是简单一个矩阵,通常是多层struct嵌套,字段名也因为来源不同而千差万别。我的习惯是先打印keys,再逐层往下看,绝不猜字段名:
import scipy.io as sio mat = sio.loadmat("subject_01_act_01_rr_01_ca_01.mat", squeeze_me=True) print(mat.keys()) # 常见字段名有 pose_3d、points3d、pose_xyz 之类 # 如果遇到 1x1 的 struct array,先调用 .item() 解包 data = mat["pose_3d"] # 具体字段名以实际打印为准 print(data.shape)3.4 NPZ预处理版怎么读
如果你用的是data_3d_h36a.npz这种预处理文件,读起来就痛快多了:
import numpy as np data = np.load("data_3d_h36a.npz", allow_pickle=True) train = data["train"].item() test = data["test"].item() print(train.keys()) # 受试者列表 print(train["S1"].keys()) # 动作序列列表 seq = train["S1"]["Walking 1"] print(seq.keys()) # 输出里会有 positions_3d、positions_2d、cameras 等 print(seq["positions_3d"].shape) # (帧数, 17, 3) print(seq["positions_2d"].shape) # (帧数, 17, 2)第一次读的时候容易漏掉allow_pickle=True,不写的话numpy会直接报错。这个参数就是为了解包里面的Python dict,属于这个数据集的标准操作。
3.5 动作名字符串里的隐形地雷
NPZ里动作名的键通常是“动作名+空格+重复序号”的形式,比如Walking 1和Walking 2,中间是有空格的。很多人写成Walking_1,一查就报KeyError。先print(train["S1"].keys())看清楚原始字符串,再写索引。
另外一个容易忽略的点是帧率对齐。动作捕捉本身是100Hz,视频是50Hz,所以官方原始数据里每秒有100个骨架帧,但视频每秒只有50帧。直接用原始MAT做训练时,一定要明确自己到底取哪个基准;NPZ预处理版在制作时已经把2D和3D统一对齐成了50Hz,这也是我推荐你从NPZ入门的重要原因。
4. 坐标投影与可视化验证:确认数据真的读对了
数据读进来了,不代表它是对的。H36M的坐标系、单位、相机参数这堆东西,初学者稍不注意就全乱了。
4.1 单位:毫米还是米
先记住结论:H36M的3D坐标单位是毫米。有些库训练时习惯把坐标除以1000转成米,有些直接拿毫米算MPJPE,这都行,但你别混着用。我见过有人直接从某个开源项目里拉数据,后来又换了另一个预处理版本,两个版本单位不一致,训练半天loss下不去,查了两天才发现是这问题。
4.2 相机参数长什么样
在NPZ里,每个动作序列的cameras字段是一个长度为4的数组,对应4个摄像机视角。每个相机的dict包含:
f:焦距,2维向量,对应(fx, fy)c:主点坐标,2维向量,对应(cx, cy)R:3x3旋转矩阵t:3x1平移向量dist:畸变系数
4.3 3D到2D的投影公式
世界坐标系下的3D点,先经旋转和平移变换到相机坐标系,再做透视投影,公式如下:
x_cam = R @ X_world + t x_pixel = f[0] * x_cam[0] / x_cam[2] + c[0] y_pixel = f[1] * x_cam[1] / x_cam[2] + c[1]写成Python函数:
import numpy as np def project_3d_to_2d(points_3d, camera): R = camera["R"] t = camera["t"] f = camera["f"] c = camera["c"] points_cam = (R @ points_3d.T).T + t.reshape(1, 3) points_2d = np.zeros((points_cam.shape[0], 2)) points_2d[:, 0] = f[0] * points_cam[:, 0] / points_cam[:, 2] + c[0] points_2d[:, 1] = f[1] * points_cam[:, 1] / points_cam[:, 2] + c[1] return points_2d这里特别提醒:不同解析包对R和t的定义可能反着写,有的是R @ X_world + t,有的是R @ (X_world - t)。你自己推断公式的时候,最有效的验证方法就是拿已知的3D点和2D标注做对比,如果投影结果和官方给的2D坐标位置对不上,第一怀疑R和t的符号形式,第二怀疑单位。
4.4 把骨架画出来,一眼看出对错
判断数据是否正确的黄金标准是可视化。画3D骨架其实不复杂:
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D edges = [ (0, 1), (1, 2), (2, 3), (0, 4), (4, 5), (5, 6), (0, 7), (7, 8), (8, 9), (8, 10), (10, 11), (11, 12), (8, 13), (13, 14), (14, 15), ] def draw_skeleton(ax, pts3d, edges, color="b"): ax.scatter(pts3d[:, 0], pts3d[:, 1], pts3d[:, 2], s=20, c=color) for a, b in edges: ax.plot(pts3d[[a, b], 0], pts3d[[a, b], 1], pts3d[[a, b], 2], c=color, lw=2) seq = train["S1"]["Walking 1"] points_3d = seq["positions_3d"][0] fig = plt.figure(figsize=(8, 8)) ax = fig.add_subplot(111, projection="3d") draw_skeleton(ax, points_3d, edges) ax.set_box_aspect((1, 1, 1)) plt.show()画出来的姿态如果站立方向合理、四肢位置符合人体结构,说明数据基本没问题。我每次换一个新数据集,第一件事永远是画骨架,不画到正常绝不往下走。这一步看着简单,实际能省下大量的debug时间。
5. 我踩过的坑,和你可能也会遇到的坑
最后把这些年实际踩过的坑集中写出来,基本都是血泪教训。
| 现象 | 原因 | 对策 |
|---|---|---|
| 申请邮件一直不回复 | 用了个人邮箱 | 换机构邮箱重新提交 |
| 下载到一半链接失效 | 下载链接有时效 | 失效后重新走申请流程,用支持断点续传的工具 |
| loadmat后print出来全是1x1 struct | MAT内部存在多层嵌套 | 逐层.item()展开,先看清楚结构 |
| np.load报Object arrays错误 | 没开allow_pickle | 加allow_pickle=True |
| 动作名查不到 | 字符串里多了空格或下划线 | 先打印keys再复制原始字符串 |
| 训练时MPJPE数值离谱 | 单位、关节顺序或坐标定义不统一 | 先画骨架,再做投影对比 |
| Protocol 2实验的2D输入对不上 | 把GT 2D当成了检测器2D | 确认输入来自检测器输出,不是GT投影 |
还有一个细节值得单独拎出来说。H36M数据里,同一个动作序列在不同相机视角下,视频帧和动作捕捉帧的对应关系是固定的,但你如果从原始MAT直接解析,必须自己处理“100Hz动捕帧到50Hz视频帧”的抽取。NPZ版本已经帮你做了这层对齐,所以很多人推荐新手先用NPZ。我自己在踩过一次“2D视频帧和3D骨架错位”的坑之后,就养成了一个习惯:无论是自己做pipeline还是用开源代码,第一件事永远是随机抽一帧,把2D关键点画到原始视频画面上,确认3D骨架投影后和2D标注完全重合,再放心进入训练环节。
如果让我重新入坑一次,我的顺序会是这样:先下载NPZ预处理版,把整个读取、可视化、训练流程跑通,等真要做视频输入或者多视角任务,再回官方源申请完整数据。数据集获取只是万里长征第一步,但这一步走稳了,后面能省掉大量查错时间。希望这份H36M获取与解析的实战记录,能帮你在入门3D人体姿态估计时少走些弯路。
本文还有配套的精品资源,点击获取