做科研或者跑深度学习模型时,很多人的第一步不是写网络结构,而是卡在怎么把手里的核磁数据变成模型能用的格式。医院拷回来的数据往往是一整个文件夹的 DICOM 文件,几百上千个文件,命名还是乱码;而 PyTorch、FSL、SPM 里常用的数据格式却是单个的 NIfTI 文件。如果你也遇到过这种“文件多到不知从何下手”的情况,这篇文章会很适合你。内容会围绕核磁数据的格式转换展开,讲清楚 DICOM 和 NIfTI 的区别,给出图形界面和命令行两种转换方案,并附上 Python 批量处理和结果校验的完整代码。
1. 背景与核心概念
1.1 核磁数据到底长什么样
核磁共振成像(MRI)扫描完成后,设备并不是直接生成一张我们肉眼看到的大图,而是生成一系列断层切片。每一层切片都包含像素矩阵,以及描述这个切片位置、方向、厚度、扫描参数的大量元数据。为了将这些信息完整保存下来,医疗影像领域制定了 DICOM 标准。
DICOM 的全称是 Digital Imaging and Communications in Medicine,它不仅是文件格式,更是一套包含网络传输、存储、打印在内的完整医疗影像协议。一个典型的 DICOM 文件包含文件头和像素数据两部分,文件头里有患者姓名、检查号、扫描序列、层厚、像素间距、图像位置等字段。这些字段并非全为图像显示服务,很多是给医生写报告、给设备做定位用的。
当我们从医院或扫描设备导出数据时,通常会得到两个层级:一个患者文件夹下包含多个检查,每个检查下又包含多个序列,每个序列里是几十到几百张 DICOM 切片。这种结构对 PACS 系统非常友好,但对科研和深度学习来说却很不方便——算法工程师往往希望输入是一个完整的 3D 体数据,或者一个直接可以送入网络的张量。
1.2 为什么科研领域偏爱 NIfTI
NIfTI 格式(Neuroimaging Informatics Technology Initiative)是神经科学和医学影像研究领域的常用格式。它最早是为了替代 ANALYZE 格式而设计的,解决了 ANALYZE 在方向和坐标信息上不够可靠的问题。NIfTI 文件通常以.nii或.nii.gz结尾,一个文件就能保存完整的 3D 或 4D 体数据,并且自带仿射变换矩阵,可以准确描述体素坐标与真实空间坐标之间的关系。
在 FSL、SPM、FreeSurfer、AFNI 等主流神经影像工具中,NIfTI 是默认的数据格式。PyTorch 生态里的很多医学影像工具包,比如 MONAI、TorchIO,也原生支持 NIfTI。换句话说,如果你的数据要喂给深度学习模型,或者要跑 FSL 的预处理流程,NIfTI 几乎是绕不开的。
1.3 格式转换的本质不是改后缀
很多初学者会问:直接把.dcm改成.nii行不行?
答案是不行。DICOM 是一个文件一张切片,NIfTI 是一个文件一个 3D 体数据,它们的内部结构完全不同。所谓“转换”,本质上是完成三件事:
- 将多张 DICOM 切片按照空间位置重排,堆叠成 3D 体数据。
- 将 DICOM 文件头中的位置、方向、层厚、像素间距等信息换算成 NIfTI 的 affine 矩阵,保证空间坐标不丢失。
- 把需要的元数据(如 TR、TE、翻转角、B 值等)提取出来,写入 NIfTI 扩展头或附属 JSON 文件。
理解了这一点,你就能明白为什么转换工具的选择如此重要。使用错误工具或手动堆叠数据很容易出现方向反转、切片顺序错误、层厚信息丢失等问题,而这些错误在后续配准和建模阶段很难被发现,危害极大。
2. 常见核磁数据格式盘点
2.1 DICOM
DICOM 是临床设备输出的标准格式。它由美国放射学会和北美电气制造协会联合制定,几乎所有厂商的 MRI、CT、PET 设备都支持该标准。优点是与设备无关、信息完整、可追溯;缺点是文件数量大、结构复杂,一个 3D 序列被拆分成很多个小文件,直接读取效率不高。
2.2 NIfTI
NIfTI 是科研社区的事实标准。几乎所有开源神经影像工具都支持.nii和.nii.gz。它把体数据与空间信息整合在一个文件里,并且支持 4D 数据,例如功能磁共振成像(fMRI)的时间序列。与 DICOM 相比,NIfTI 结构简单、读写方便、文件体积小(尤其经过 gzip 压缩之后)。
2.3 ANALYZE
ANALYZE 是 NIfTI 的前身,由一对.hdr和.img文件组成。早期工具中使用广泛,现在基本被 NIfTI 取代。如果你在网上下载到旧数据集,偶尔会碰到这种格式,建议转换时尽快迁移到 NIfTI。
2.4 其他格式
除了上述常见格式,还有 MINC、MGH/MGZ、NRRD 等。MGH 是 FreeSurfer 内部使用的格式,NRRD 和 MHD 常见于 3D 图像分割与计算机视觉领域。具体使用哪种格式,取决于你后续要用哪些工具。下表做一个简单对比:
| 格式 | 扩展名 | 单文件承载 | 主要使用领域 | 压缩方式 |
|---|---|---|---|---|
| DICOM | .dcm | 单层切片 | 医院、PACS、临床诊断 | 可内嵌压缩 |
| NIfTI | .nii / .nii.gz | 3D/4D体数据 | FSL、SPM、深度学习 | gzip |
| ANALYZE | .hdr/.img | 3D体数据 | 旧工具、遗留数据集 | 无内建压缩 |
| MGH | .mgh/.mgz | 3D/4D | FreeSurfer | gzip |
| NRRD | .nrrd | 3D体数据 | 可视化、图像分割 | gzip |
3. 环境准备与工具选择
3.1 操作系统与运行环境
本文示例在 Windows 10/11 和 Ubuntu 20.04/22.04 上均可运行。命令行工具 dcm2niix 是跨平台的,Python 代码依赖标准库和少量第三方库。如果你的电脑上已经安装过 Anaconda,可以直接使用;如果没有,建议先安装一个干净的 Python 3.9 以上的环境,便于管理依赖。
3.2 核心工具一:dcm2niix
dcm2niix 是目前最主流的 DICOM 转 NIfTI 工具,由 Chris Rorden 开发维护。它用于替换早期的 dcm2nii,支持现代 MRI 序列,能处理增强型 DICOM、4D 功能像、弥散加权成像等多种数据类型。其主要优势包括:
- 支持多厂商设备:GE、Siemens、Philips 等。
- 自动识别方向信息,生成准确的 affine 矩阵。
- 可选生成 BIDS 风格命名和 JSON 文件。
- 转换速度快,支持多线程。
- 命令行和图形界面均可使用。
由于 dcm2niix 迭代较快,本文不写死具体版本号,建议从 GitHub 或官方发布页下载最新 release。安装完成后,在终端输入dcm2niix -h能正常输出版本信息,即表示安装成功。
3.3 核心工具二:MRIcroGL
MRIcroGL 是同一作者推出的图形界面查看与转换工具。它适合不想敲命令行的用户,也可以用来快速预览 NIfTI 文件的三维渲染效果。它的转换界面虽然是菜单式操作,但底层调用与 dcm2niix 相同的算法,结果完全一致。
3.4 核心工具三:Python 数据处理环境
如果你在批量处理大量被试数据,或者需要把转换流程嵌入到自动化脚本中,Python 是更好的选择。需要用到以下库:
nibabel:读取和写入 NIfTI、ANALYZE 等神经影像格式。pydicom:读取 DICOM 文件头与像素数据。numpy:数组运算与维度堆叠。
安装命令:
pip install nibabel pydicom numpy这三个工具的关系可以这样理解:dcm2niix 负责“一键转换”,Python 负责“自动化与精细控制”,MRIcroGL 负责“预览和手动检查”。实际项目中,通常用命令行或 Python 批量完成转换,再用 MRIcroGL 抽查结果。
4. 核心转换方法拆解
4.1 使用 dcm2niix 命令行转换
dcm2niix 的基本用法非常简单。假设你的 DICOM 数据放在/data/raw/dicom目录下,希望把转换结果输出到/data/raw/nifti,可以在终端执行:
dcm2niix -o /data/raw/nifti /data/raw/dicom这个命令会把输入目录下所有的 DICOM 序列逐一转换,并按默认规则命名输出文件。但实际场景中,我们往往需要更精细地控制命名和格式。下面介绍几个常用参数:
| 参数 | 含义 | 推荐值 |
|---|---|---|
-z y | 输出 gzip 压缩的 .nii.gz | -z y |
-f %p_%s | 命名规则,%p为序列名,%s为序列号 | 按需调整 |
-o | 输出目录 | 必须指定 |
-b y | 生成附带 JSON 文件 | -b y |
-x n | 不裁剪图像边缘 | 默认即可 |
-m y | 合并 2D 切片为 3D(不推荐对常规序列使用) | 默认即可 |
一个更完整的命令示例:
dcm2niix -z y -b y -f "%p_%s" -o /data/raw/nifti /data/raw/dicom执行后,/data/raw/nifti会生成.nii.gz和对应的.json文件。JSON 文件里包含扫描参数,这为后续质量控制提供了很大帮助。
4.2 使用 MRIcroGL 图形界面转换
不熟悉命令行的用户,可以下载 MRIcroGL。打开软件后,在菜单栏选择Import或直接拖拽 DICOM 文件夹到窗口中,软件会自动识别序列。确认列表中的序列无误后,点击转换按钮,设置输出目录,即可生成 NIfTI 文件。
图形界面的优势是直观,可以看到每个序列的类型、层数、大小,适合用于检查数据质量。劣势是不适合批量处理大量被试数据。如果你的项目有几十个被试,每个被试又有多个序列,强烈建议用命令行或 Python 脚本,而不是手动点击。
4.3 使用 Python 实现 DICOM 转 NIfTI
有些时候,你可能需要在读取 DICOM 后做自定义处理,比如去除运动伪影、重采样、裁剪,这时可以直接用pydicom读取数据并手动构建 NIfTI 文件。下面的代码演示了一个最小实现流程。
# 文件路径:dicom_to_nifti_manual.py import os import numpy as np import pydicom import nibabel as nib def load_dicom_volume(dicom_dir): """ 读取一个 DICOM 序列目录,按空间位置排序并堆叠为 3D 数组。 注意:此函数仅用于演示核心思路,生产环境建议使用 dcm2niix。 """ files = [] for fname in os.listdir(dicom_dir): if fname.lower().endswith(".dcm"): files.append(os.path.join(dicom_dir, fname)) slices = [] for fpath in files: ds = pydicom.dcmread(fpath) slices.append(ds) slices.sort(key=lambda s: float(s.ImagePositionPatient[2])) pixel_array = np.stack([s.pixel_array for s in slices]) # 根据 DICOM 头信息计算 affine # 这里采用简化实现,仅保留方向正确性 first = slices[0] pixel_spacing = first.PixelSpacing # [row_spacing, col_spacing] slice_thickness = float(getattr(first, "SliceThickness", 1.0)) affine = np.eye(4) affine[0, 0] = float(pixel_spacing[1]) affine[1, 1] = float(pixel_spacing[0]) affine[2, 2] = slice_thickness return pixel_array, affine, slices if __name__ == "__main__": data, affine, slice_objs = load_dicom_volume("/data/raw/dicom/T1") img = nib.Nifti1Image(data, affine) nib.save(img, "/data/raw/nifti/manual_t1.nii.gz") print("转换完成,体数据形状:", data.shape)需要特别说明:上面这段代码简化了方向矩阵和患者坐标的换算,只适合处理轴向采集、方向没有旋转的序列。真实数据可能存在倾斜采集、不同扫描方向等情况,因此这里只是展示原理,实际项目中更推荐直接用 dcm2niix。如果你确实需要自己写转换逻辑,一定要逐字段核实ImageOrientationPatient和ImagePositionPatient,并计算正确的方向余弦矩阵。
4.4 使用 Python 调用 dcm2niix 构建批量脚本
与其自己造轮子,更稳妥的方式是用 Python 调用 dcm2niix。这样既保持了自动化能力,又利用了成熟的转换算法。参考代码如下:
# 文件路径:batch_dicom_to_nifti.py import subprocess from pathlib import Path INPUT_ROOT = Path("/data/raw/dicom") OUTPUT_ROOT = Path("/data/raw/nifti") OUTPUT_ROOT.mkdir(parents=True, exist_ok=True) # DICOM 目录结构假设: # /data/raw/dicom/sub-001/T1 # /data/raw/dicom/sub-001/fMRI # /data/raw/dicom/sub-002/T1 # ... for subject_dir in sorted(INPUT_ROOT.iterdir()): if not subject_dir.is_dir(): continue for series_dir in sorted(subject_dir.iterdir()): if not series_dir.is_dir(): continue output_name = f"{subject_dir.name}_{series_dir.name}" cmd = [ "dcm2niix", "-z", "y", "-b", "y", "-f", f"{output_name}_%p", "-o", str(OUTPUT_ROOT), str(series_dir) ] print(f"正在转换: {series_dir}") try: subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"转换失败: {series_dir},错误: {e}")这段代码会自动遍历每个被试、每个序列的 DICOM 目录,并生成带被试 ID 和序列名的 NIfTI 文件。与手动点击相比,脚本处理几十个被试也就几分钟的事,而且不易漏掉序列。
4.5 命名规范与输出组织
转换后的文件命名对后续数据分析影响很大。如果项目最终要使用 BIDS 标准,建议直接把文件名整理成 BIDS 格式。BIDS 命名示例:
sub-001_T1w.nii.gzsub-001_task-rest_bold.nii.gzsub-001_dwi.nii.gz
dcm2niix 的-f参数支持多种占位符,常见的有:
| 占位符 | 含义 |
|---|---|
%p | 序列名称 |
%s | 序列编号 |
%d | 扫描日期 |
%n | 患者姓名 |
%t | 时间 |
可以组合成你需要的格式。例如:
dcm2niix -z y -b y -f "sub-%n_%p" -o /data/raw/nifti /data/raw/dicom/sub-0015. 完整实战案例:从 DICOM 到 NIfTI 的批量处理
5.1 准备测试数据
为了验证整个流程,我们可以手动构造一个包含两个被试的小型测试目录:
/data/raw/dicom/ ├── sub-001/ │ ├── T1/ │ │ ├── 1.dcm │ │ ├── 2.dcm │ │ └── 3.dcm │ └── fMRI/ │ ├── 1.dcm │ └── 2.dcm └── sub-002/ └── T1/ ├── 1.dcm └── 2.dcm真实数据里每个 DICOM 目录的文件数量会远多于这里,但目录层级关系是类似的。
5.2 执行转换脚本
将上一节的batch_dicom_to_nifti.py保存到本地,然后将INPUT_ROOT和OUTPUT_ROOT改为你的实际路径,执行:
python batch_dicom_to_nifti.py预期输出效果:
正在转换: /data/raw/dicom/sub-001/T1 正在转换: /data/raw/dicom/sub-001/fMRI 正在转换: /data/raw/dicom/sub-002/T1转换完成后,/data/raw/nifti目录下会看到生成的.nii.gz文件和对应的.json文件。
5.3 验证转换结果
转换是否成功,不能只看文件有没有生成,还要检查维度、方向和像素间距是否与原数据一致。下面用 nibabel 读取转换后的文件进行校验:
# 文件路径:check_nifti.py import nibabel as nib import json from pathlib import Path nifti_dir = Path("/data/raw/nifti") for nii_file in sorted(nifti_dir.glob("*.nii.gz")): img = nib.load(nii_file) print(f"文件: {nii_file.name}") print(f" 数据形状: {img.shape}") print(f" 体素大小: {img.header.get_zooms()}") print(f" 仿射矩阵: {img.affine.tolist()[0][0]:.2f}, {img.affine.tolist()[1][1]:.2f}, {img.affine.tolist()[2][2]:.2f}") # 查看是否有配套 JSON json_file = nii_file.with_suffix("").with_suffix(".json") if json_file.exists(): with open(json_file, "r", encoding="utf-8") as f: metadata = json.load(f) print(f" JSON 元数据: TR={metadata.get('RepetitionTime', 'N/A')}, TE={metadata.get('EchoTime', 'N/A')}") print()以 T1 结构像为例,正常输出类似:
文件: sub-001_T1_T1.nii.gz 数据形状: (256, 256, 192) 体素大小: (1.0, 1.0, 1.0) 仿射矩阵: 1.00, 1.00, 1.00 JSON 元数据: TR=2.3, TE=2.03如果体素大小是 0 或负数,说明 DICOM 头信息读取异常;如果 shape 只有一个维度为 1,说明可能只转出了单层切片。这些都需要回到原始 DICOM 数据中检查。
5.4 使用 MRIcroGL 做可视化核对
自动校验只能发现数字层面的异常,方向是否真正符合预期,最好还是用 MRIcroGL 或 FSLeyes 打开文件看一眼。强烈建议在完成批量转换后,随机抽取几个被试的 T1 和 fMRI 数据,人工检查:
- 轴位、冠状位、矢状位是否正常,左右是否有翻转。
- 颅顶方向是否朝上。
- fMRI 时间序列是否有异常跳变。
这一步虽然花时间,但能避免后续所有分析建立在错误数据上。
6. 常见问题与排查思路
实际转换过程中,很多问题是有共性的。下表列出高频问题及处理建议:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 输出文件只有一个切片 | 输入目录实际上只有单张 DICOM,或序列被拆分 | 检查序列完整性,确认该序列是否为 2D 单层 |
| 转换后左右翻转 | DICOM 方向余弦信息与 NIfTI 默认坐标系不一致 | 查看原始 DICOM 的ImageOrientationPatient,与正规数据对比;确认 dcm2niix 是否为最新版 |
| 输出文件不为 .nii.gz | 未添加-z y参数 | 添加压缩参数后重新转换 |
| 生成的 JSON 文件缺失 | 未添加-b y参数,或数据本身缺少元数据 | 添加参数;若 DICOM 本身无相应字段,则无法生成 |
| 拼接后的体数据层数不对 | DICOM 文件排序方式错误 | 用ImagePositionPatient的坐标值排序,而不是文件名字符串 |
| 弥散数据 B 值丢失 | DICOM 私有标签未被识别 | 用 dcm2niix 转换后检查 JSON 中的PhaseEncodingDirection、B_value字段 |
| 转换速度很慢 | 文件数量多,未启用多线程 | dcm2niix 默认已多线程,检查是否被杀毒软件限制 |
| 增强型 DICOM 无法转换 | 设备使用了较新的增强型多帧格式 | 使用最新版 dcm2niix,并确认其支持该厂商格式 |
遇到问题时,建议按以下顺序排查:
- 先用 MRIcroGL 打开原始 DICOM 目录,确认数据本身是否能正常显示。
- 用 pydicom 读取一个 DICOM 文件,检查关键字段是否存在、数值是否合理。
- 尝试 dcm2niix 的默认参数转换,不要加入额外选项。
- 对比不同工具的输出结果,看看问题是否出在特定工具上。
- 查看生成文件对应的 JSON,确认扫描参数是否完整。
7. 最佳实践与工程建议
7.1 数据管理:先复制再转换
无论使用哪种工具,转换过程都应遵守“原始数据只读”的原则。DICOM 文件是医院的原始记录,一旦被错误修改或覆盖,很难恢复。建议在项目开始前把源数据复制到一个独立目录,并用chmod或系统权限设置为只读。转换脚本只负责读取原始 DICOM,向输出目录写入 NIfTI,不在原始目录中生成任何文件。
7.2 元数据完整性与 JSON 保留
转换时建议始终加上-b y生成 JSON 文件。这些 JSON 在后续做质量检查时非常关键,可以用于确认 TR、TE、翻转角、B 值等参数是否与实验设计一致。不要因为觉得文件多就删掉 JSON,后续做动态运动校正或弥散张量成像时,这些参数可能是模型输入的一部分。
7.3 数据匿名化与隐私合规
医院提供的 DICOM 数据中通常包含患者姓名、出生日期、检查号等敏感信息。转换到 NIfTI 时,dcm2niix 默认不会把患者姓名写入输出文件,但为了保险起见,建议在转换前用 pydicom 批量匿名化 DICOM 数据,或者使用 DICOM 标准中的匿名化工具。这一步不仅是合规要求,也避免数据在团队间传输时泄露隐私。
7.4 版本记录与脚本保存
不要只用图形界面手动转换几十个被试。每次转换的命令、参数、工具版本、日期都应该记录下来。如果后续发现转换结果存在问题,可以回溯是哪一步操作引入的错误。一个简单的方案是为每个项目建立一个conversion_log.txt,记录如下信息:
工具: dcm2niix v1.0.20220720 命令: dcm2niix -z y -b y -f "%p_%s" -o /data/raw/nifti /data/raw/dicom 日期: 2025-01-157.5 数据校验纳入自动化流程
考虑到核磁数据转换可能涉及几十上百个文件,建议把校验写成自动脚本,每次转换完成后自动检查:
- NIfTI 文件是否存在且非空。
- 数据维度是否在合理范围。
- 体素大小是否为正值。
- JSON 是否存在,关键字段是否齐全。
这样可以在人眼查看之前先过滤掉大部分异常。
8. 总结
通过本文,你应该已经掌握了核磁数据从 DICOM 到 NIfTI 的完整转换思路:理解了 DICOM 和 NIfTI 的本质差异,知道了 dcm2niix、MRIcroGL、Python 三种工具各自的适用场景,并拿到了可运行的批量转换和校验脚本。转换并不是一道简单的命令,而是后续数据处理流程的地基。方向错、层厚错、切片顺序错,都会在你做完整个预处理之后才暴露出来,那时候再回头排查成本极高。
下一篇你可以继续学习 NIfTI 数据的可视化与质量控制,或者用 FSL 完成脑区提取与配准,也可以接触 MONAI 和 TorchIO 等深度学习工具,把转换好的数据真正送入网络训练。不论走哪条路,都建议先拿一个被试的数据跑通全流程,再批量处理全部数据。这一步看起来慢,却是最稳妥的做法。