2. 医学影像数据集的标准化组织与元数据管理
在医学影像实验中,数据往往来自不同扫描仪、不同序列、不同被试和不同时间点。如果只把文件按“日期+姓名”堆在硬盘里,后续复现实验时几乎无法回答三个关键问题:这份数据属于谁、它是怎样采集的、它经过了哪些处理。标准化组织与元数据管理要解决的正是这三个问题——用统一的格式承载像素与头信息,用统一的目录结构承载被试与实验条件,用机器可读的元数据文件承载扫描参数与处理历史。
一、三种主流格式的元数据提取
DICOM 是临床采集的原始格式,元数据以“标签(Tag)”形式存在,例如 (0010,0010) 为患者姓名、(0018,0080) 为重复时间 TR、(0018,0081) 为回波时间 TE、(0020,0032) 为图像位置。一个被试的一次扫描通常包含成百上千个 DICOM 文件,每个文件都带一份头信息,因此提取时需要先按 SeriesInstanceUID 分组,再逐组读取代表性标签。Python 中常用 pydicom:
import pydicom, glob
files = glob.glob("sub-01/anat/*.dcm")
ds = pydicom.dcmread(files[0])
meta = {
"PatientID": ds.PatientID,
"StudyDate": ds.StudyDate,
"Manufacturer": ds.Manufacturer,
"TR": float(ds.RepetitionTime),
"TE": float(ds.EchoTime),
"SliceThickness": float(ds.SliceThickness),
"SeriesDescription": ds.SeriesDescription,
}
NIfTI 是分析阶段最常用的格式,像素数据与头信息分离在 .nii 或 .nii.gz 中,头信息包含 pixdim(体素尺寸)、qform/sform(空间变换矩阵)、datatype 等。用 nibabel 可读取:
import nibabel as nib
img = nib.load("sub-01_T1w.nii.gz")
hdr = img.header
print(hdr.get_zooms()) # 体素尺寸 (1.0, 1.0, 1.0)
print(img.affine) # 4x4 仿射矩阵
NRRD 常见于扩散与显微影像,头信息以键值对文本保存,如 space directions、kinds、sizes。三种格式的元数据字段并不一一对应,因此标准化提取的核心是建立一张“字段映射表”,把不同来源的字段统一映射到同一套命名,例如把 DICOM 的 RepetitionTime、NIfTI 的 pixdim[4]、NRRD 的 space directions 分别映射到统一的 RepetitionTime 与 VoxelSize。
二、BIDS 目录结构设计
BIDS(Brain Imaging Data Structure)用一套约定俗成的命名规则把“谁、什么时候、做了什么、采集了什么”编码进路径。一个符合 BIDS 的最小数据集如下:
ds-memory/
├── dataset_description.json
├── participants.tsv
├── participants.json
├── sub-01/
│ ├── anat/
│ │ ├── sub-01_T1w.nii.gz
│ │ └── sub-01_T1w.json
│ └── func/
│ ├── sub-01_task-nback_bold.nii.gz
│ ├── sub-01_task-nback_bold.json
│ └── sub-01_task-nback_events.tsv
└── sub-02/
└── ...
命名中的 sub-、ses-、task-、run-、acq- 等键值对称为实体(entity),顺序固定,不能随意调换。每个影像文件旁必须有一个同名 JSON 侧车文件(sidecar),记录该序列的采集参数;participants.tsv 记录被试级信息,如年龄、性别、组别;dataset_description.json 记录数据集名称、BIDS 版本、许可证。这样,任何工具只要按 BIDS 规则解析路径,就能自动定位数据与参数,无需人工询问。
三、用 JSON/YAML 记录扫描参数与实验条件
侧车 JSON 是 BIDS 的核心。以 N-back 任务为例,sub-01_task-nback_bold.json 可写为:
{
"TaskName": "nback",
"RepetitionTime": 2.0,
"EchoTime": 0.03,
"FlipAngle": 90,
"SliceTiming": [0.0, 0.5, 1.0, 1.5],
"Manufacturer": "Siemens",
"ManufacturersModelName": "Prisma",
"MagneticFieldStrength": 3.0,
"Instructions": "判断当前刺激是否与2个试次前相同"
}
实验条件则记录在 events.tsv 中,每行一个试次,列包括 onset、duration、trial_type、response_time、correct。例如:
onset duration trial_type response_time correct
0.0 1.5 1-back 0.82 1
3.0 1.5 2-back 1.05 0
6.0 1.5 1-back 0.76 1
YAML 更适合人工编辑的配置,例如扫描协议模板:
protocol: T1w_MPRAGE
parameters:
TR: 2300
TE: 2.98
TI: 900
flip_angle: 9
voxel_size: [1.0, 1.0, 1.0]
slices: 176
四、标准化流程与校验
把原始数据整理成 BIDS 通常分四步:第一,用 dcm2niix 把 DICOM 转成 NIfTI 并生成初始 JSON;第二,按 BIDS 实体重命名文件并放入对应目录;第三,补全侧车 JSON 中缺失的字段,如 TaskName、SliceTiming;第四,用 bids-validator 校验。校验命令与典型输出如下:
$ bids-validator ds-memory/
[WARN] sub-01/func/sub-01_task-nback_bold.json
SliceTiming 长度与切片数不一致
[ERROR] sub-02/anat/ 缺少 T1w 侧车 JSON
校验通过后,还应生成一份 dataset_description.json,并计算每个文件的校验和写入 checksums.tsv,以便复现时确认数据未被改动。元数据管理不是一次性工作:每次新增被试、修改参数或重新处理,都应同步更新侧车文件与处理历史,使数据集始终处于“可被他人直接理解并复现”的状态。
dcm2niix -b y 批量转换并保留 BIDS 侧车,再用 pybids 的 BIDSLayout 读取元数据,可避免手工整理路径与字段。