简介:这份资源面向计算机视觉与机器学习方向的开发者,尤其是正在入门或实践3D人体姿态估计的研究者与学生,提供用Python获取并处理Human3.6M数据集的完整工具链。压缩包共14个文件,约34KB,以4个Python脚本为核心,覆盖数据下载、解压、预处理等环节,另含txt校验文件、Dockerfile与docker-compose.yml容器配置、yml流程编排、md说明文档及license等辅助文件,便于快速搭建可复现的运行环境。资源围绕Human3.6M的HDF5数据读取、3D关节坐标提取、格式转换与归一化、训练验证测试集划分,以及CNN、RNN、seq2seq等模型构建与评估展开,并涉及matplotlib结果可视化。目前已有3634人学习下载,适合希望打通数据准备到模型训练全流程、减少环境配置与数据预处理踩坑成本的读者参考。
1. 从一次数据加载失败说起:Human3.6M 环境到底该怎么搭
第一次在服务器上跑 Human3.6M 的姿态估计训练脚本,日志里跳出来的不是 loss 曲线,而是一串FileNotFoundError和KeyError: 'S1'。当时以为只是路径写错,改了两小时才发现,问题根本不在代码,而在这套数据集的目录结构、标注格式和 Python 读取方式三者之间没有对齐。Human3.6M 是 3D 人体姿态领域被引用最多的数据集之一,7 个受试者、17 个动作类别、360 万帧图像,配套的 2D/3D 关节标注和相机参数分散在不同文件里。很多开发者拿到压缩包后直接解压就开跑,结果卡在标注解析、帧对齐、内存溢出这些环节。这篇笔记面向正在用 Python 做机器学习、需要把 Human3.6M 跑通的人,从环境准备到读取验证,把每一步拆开讲清楚。
2. 环境准备与版本选择:Python、依赖库和目录规划
2.1 为什么 Python 版本和 NumPy 版本要先锁死
Human3.6M 的官方标注文件是.mat格式,Python 侧读取主要靠scipy.io.loadmat或h5py。这两个库对 NumPy 版本有隐性要求:scipy在 1.10 之前对numpy>=1.23的兼容性较好,而h5py在 3.7 以下版本读取 MATLAB v7.3 格式的.mat文件时会直接报OSError: Unable to open file。我一般会把环境锁在 Python 3.9 或 3.10,NumPy 固定在 1.24.x,SciPy 用 1.10.x,h5py 用 3.8.x。这个组合在 Ubuntu 20.04 和 22.04 上都验证过,不会出现玄学崩溃。
# 创建独立环境,避免和系统 Python 冲突 conda create -n h36m python=3.10 -y conda activate h36m # 锁定核心依赖版本 pip install numpy==1.24.3 scipy==1.10.1 h5py==3.8.0 pip install opencv-python==4.8.0.74 pillow==9.5.0 tqdm这里numpy==1.24.3是分水岭:再往上到 1.25+,部分旧版scipy的loadmat会触发AttributeError。opencv-python用来做图像读取和可视化验证,tqdm用于遍历帧时看进度。如果你用 PyTorch 做训练,建议先装好 CUDA 对应的 torch,再装这些数据读取库,避免 pip 解析器把 NumPy 降级。
2.2 目录结构规划:别把图像和标注混在一起
Human3.6M 原始数据通常包含images/、annotations/、metadata.xml三部分。图像按S1到S11分受试者,每个受试者下再按动作名分文件夹,每个动作文件夹里是*.jpg序列。标注文件常见的有h36m_train.pkl、h36m_test.pkl或原始的h36m_annot.tar。我习惯在项目根目录下建一个data/h36m/,里面再分raw/和processed/:
mkdir -p data/h36m/raw/images mkdir -p data/h36m/raw/annotations mkdir -p data/h36m/processed把解压后的S1、S5、S6、S7、S8、S9、S11放进raw/images/,把.mat或.pkl标注放进raw/annotations/。这样做的好处是后续写 Dataset 类时,路径拼接逻辑清晰,不会出现images/S1/S1/这种重复层级。常见做法是用软链接把原始数据挂进来,而不是复制,节省磁盘。
2.3 验证依赖是否装对:三行代码自检
装完别急着跑训练,先用一个最小脚本确认scipy和h5py都能正常读标注:
import scipy.io as sio import h5py import numpy as np # 尝试读取一个 .mat 标注文件 mat_path = "data/h36m/raw/annotations/h36m_annot.mat" try: data = sio.loadmat(mat_path) print("scipy loadmat OK, keys:", list(data.keys())[:5]) except NotImplementedError: # MATLAB v7.3 格式需要 h5py with h5py.File(mat_path, "r") as f: print("h5py OK, keys:", list(f.keys())[:5])如果scipy报NotImplementedError,说明文件是 v7.3 格式,必须走h5py分支。这一步能提前暴露 80% 的读取问题,比在训练循环里报错再回头查要省时间。
3. 下载与解压:从压缩包到可读目录的完整操作
3.1 获取数据后的第一件事:校验文件完整性
Human3.6M 的压缩包体积不小,图像部分通常几十 GB,标注文件也有几百 MB。下载完成后先核对文件大小和 MD5,别急着解压。我遇到过解压到一半磁盘满,结果标注文件损坏,训练时才发现标签全乱。常见做法是用md5sum和官方提供的校验值对比,如果没有校验值,至少确认压缩包能正常列出内容:
# 列出 tar 包内容,不实际解压 tar -tf h36m_images.tar | head -20 # 检查磁盘剩余空间,图像解压后通常是压缩包的 1.5 倍 df -h /data如果tar -tf就报错,说明压缩包本身不完整,重新下载比修复更快。解压时用-C指定目标目录,避免文件散落在当前路径:
tar -xf h36m_images.tar -C data/h36m/raw/images/ tar -xf h36m_annot.tar -C data/h36m/raw/annotations/3.2 标注文件格式转换:.mat 转 .pkl 的取舍
原始.mat文件用 MATLAB 结构存储,Python 读取后是嵌套的numpy.ndarray,直接索引很别扭。很多开源项目会提供转换脚本,把标注转成pkl,每个受试者一个字典,键是动作名,值包含S(3D 坐标)、2D(2D 坐标)、camera参数。我一般会跑一遍转换,因为后续 DataLoader 用pickle.load比反复loadmat快很多。
import scipy.io as sio import pickle import numpy as np def convert_annot(mat_path, out_path): data = sio.loadmat(mat_path) # 提取关键字段,具体键名以实际文件为准 annot = { "S": data["S"], # 3D 关节坐标 "2D": data["2D"], # 2D 关节坐标 "camera": data["camera"], # 相机参数 } with open(out_path, "wb") as f: pickle.dump(annot, f, protocol=4) print(f"saved to {out_path}, S shape: {annot['S'].shape}") convert_annot( "data/h36m/raw/annotations/h36m_annot.mat", "data/h36m/processed/h36m_annot.pkl" )protocol=4是为了兼容 Python 3.4+,如果只在 3.8+ 环境用,可以上protocol=5。转换后检查S的 shape,通常是(N, 17, 3)或(N, 32, 3),取决于关节定义。如果 shape 不对,说明键名取错了,用data.keys()打印全部键名再核对。
3.3 图像路径与标注对齐:帧号索引的坑
Human3.6M 的图像命名通常是frame_0001.jpg这种格式,标注里的帧号从 1 开始,但有些转换脚本会做成 0-based。如果不对齐,训练时会出现图像和标签错位一帧,loss 降不下去。我一般会写一个检查函数,随机抽几个样本,把图像读出来和 2D 标注画在一起看:
import cv2 import numpy as np def check_alignment(img_path, joints_2d): img = cv2.imread(img_path) for x, y in joints_2d: cv2.circle(img, (int(x), int(y)), 3, (0, 255, 0), -1) cv2.imwrite("check.jpg", img) print("saved check.jpg, please verify joints on body")如果关节点明显偏离人体,先检查标注里的坐标是像素坐标还是归一化坐标。Human3.6M 的 2D 标注通常是像素坐标,但不同转换版本可能除以了图像宽高。这一步花五分钟,能省掉后面几小时的调参。
4. 配置读取逻辑:Dataset 类与参数取舍
4.1 写一个最小 Dataset:只读单帧验证
在正式训练前,先写一个只返回单帧图像和对应 3D 坐标的 Dataset,确认整条链路通。这样出问题时排查范围小,不会和 DataLoader 的多进程、shuffle 搅在一起。
import os import cv2 import pickle import numpy as np from torch.utils.data import Dataset class H36MDataset(Dataset): def __init__(self, root, annot_path, subjects=None): self.root = root with open(annot_path, "rb") as f: self.annot = pickle.load(f) self.subjects = subjects or ["S1", "S5", "S6", "S7", "S8", "S9", "S11"] self.samples = [] # 构建样本索引:受试者 + 动作 + 帧号 for subj in self.subjects: for action in self.annot[subj].keys(): num_frames = self.annot[subj][action]["S"].shape[0] for i in range(num_frames): self.samples.append((subj, action, i)) def __len__(self): return len(self.samples) def __getitem__(self, idx): subj, action, frame_idx = self.samples[idx] img_path = os.path.join( self.root, subj, action, f"frame_{frame_idx+1:04d}.jpg" ) img = cv2.imread(img_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) joints_3d = self.annot[subj][action]["S"][frame_idx] return img, joints_3d.astype(np.float32)frame_idx+1是因为图像文件名从 1 开始,而列表索引从 0 开始。subjects参数留出来,方便做交叉验证时只取部分受试者。如果cv2.imread返回None,说明路径拼错了,打印img_path核对。
4.2 参数取舍:图像尺寸、归一化和内存
Human3.6M 原始图像分辨率是 1000x1000 左右,直接送进网络显存吃不消。常见做法是缩放到 256x256 或 384x384,同时把 2D 标注按比例缩放。3D 坐标通常不做缩放,因为它是世界坐标系下的米制单位。归一化方面,图像用img / 255.0即可,3D 坐标可以减去骨盆关节或做零均值化,取决于你的模型设计。
def preprocess(img, joints_2d, target_size=256): h, w = img.shape[:2] scale_x = target_size / w scale_y = target_size / h img_resized = cv2.resize(img, (target_size, target_size)) joints_2d_scaled = joints_2d * np.array([scale_x, scale_y]) return img_resized, joints_2d_scaled如果显存小于 8GB,建议先用 256x256 跑通,再考虑上 384。scale_x和scale_y分开算是因为原始图像不一定是正方形,直接用一个 scale 会导致关节点偏移。
4.3 DataLoader 的 num_workers 和 pin_memory
在 Linux 上,num_workers设成 4 或 8 能明显加快图像读取,但设太高会导致内存暴涨。pin_memory=True在 GPU 训练时有用,CPU 训练时反而增加开销。我一般先用num_workers=0跑一个 epoch,确认没有报错,再逐步加到 4。
from torch.utils.data import DataLoader dataset = H36MDataset( root="data/h36m/raw/images", annot_path="data/h36m/processed/h36m_annot.pkl" ) loader = DataLoader( dataset, batch_size=32, shuffle=True, num_workers=4, pin_memory=True, drop_last=True ) for imgs, joints in loader: print(imgs.shape, joints.shape) breakdrop_last=True是为了避免最后一个 batch 只有一两个样本,影响 BatchNorm 统计。如果报BrokenPipeError,把num_workers降到 0 或 2,通常是 OpenCV 在多进程下的线程冲突。
5. 避坑与排查:五个真实翻车记录
5.1 现象:KeyError: 'S1',标注里找不到受试者
原因通常是标注文件的键名不是S1这种格式,而是S1对应的数字索引,或者转换脚本把键名改成了subject_1。解决方法是先打印annot.keys(),确认实际键名,再改 Dataset 里的subjects列表。别硬编码,用list(annot.keys())动态取。
5.2 现象:图像读出来全黑或全白
cv2.imread对路径中的中文和空格敏感,如果路径里有这些字符,会返回None。另外 Human3.6M 的图像是 RGB,但 OpenCV 默认读成 BGR,如果可视化时颜色不对,用cv2.cvtColor转一下。全黑还有一种可能是图像本身是 16 位深度,cv2.imread默认转成 8 位会截断,加cv2.IMREAD_ANYDEPTH参数。
5.3 现象:训练 loss 不降,3D 坐标范围异常
先检查 3D 坐标的单位。Human3.6M 的 3D 坐标是米制,数值范围通常在 -1 到 1 之间。如果打印出来是几百几千,说明读的是像素坐标或毫米坐标。另外确认坐标轴顺序,有些版本是(x, y, z),有些是(x, z, y),轴顺序错了 loss 也会卡住。
5.4 现象:MemoryError在加载标注时出现
.mat文件用scipy.loadmat会一次性全部读进内存,如果标注文件包含大量冗余字段,内存占用可能超过 10GB。解决方法是改用h5py按需读取,或者先用 MATLAB 精简字段再导出。另一个办法是转成pkl后只保留S、2D、camera三个键。
5.5 现象:DataLoader 卡死,不报错也不继续
通常是num_workers大于 0 时,子进程里用了不可序列化的对象,比如 OpenCV 的cv2.VideoCapture。Human3.6M 是图像序列,不需要 VideoCapture,但如果你的 Dataset 里引用了外部句柄,就会卡住。把num_workers设为 0 能定位问题,确认后在__getitem__里避免创建全局对象。
6. 装完怎么验证:三个检查点和一个习惯
验证 Human3.6M 环境是否真正跑通,不需要跑完整训练,三个检查点就够。第一个检查点:随机抽 10 个样本,把 2D 关节点画到图像上,肉眼确认关节点落在人体上。第二个检查点:取一个 batch 的 3D 坐标,打印min、max、mean,确认数值在合理范围(通常 -1 到 1 之间)。第三个检查点:用tqdm遍历一遍 Dataset,确认没有None图像和 shape 不匹配的样本。
from tqdm import tqdm import numpy as np # 检查点 2:3D 坐标统计 all_3d = [] for _, joints in tqdm(loader, desc="scan 3d"): all_3d.append(joints.numpy()) all_3d = np.concatenate(all_3d, axis=0) print("3D min:", all_3d.min(axis=(0, 1))) print("3D max:", all_3d.max(axis=(0, 1))) print("3D mean:", all_3d.mean(axis=(0, 1)))如果min和max的绝对值超过 5,大概率是单位或轴顺序有问题。如果某个轴的mean接近 0 而其他轴偏移很大,检查是否做了骨盆中心化。这三个检查点跑完,基本可以确认数据读取链路是通的。
从那以后我每次配新数据集环境,都强制先跑一遍「单帧可视化 + 坐标统计 + 全量遍历」这三步,不跳过。看起来多花十分钟,但比在训练脚本里加一堆print再回头查要省心得多。希望帮到你。
本文还有配套的精品资源,点击获取