如何将nibabel.Nifti1Image元数据迁移至SimpleITK.Image对象?
解决Nibabel 5D MRI转SimpleITK 3D图像时保留元数据的问题
核心逻辑
Nibabel和SimpleITK对空间信息的存储逻辑不同:
- Nibabel用4x4仿射矩阵统一描述原点、轴间距和方向
- SimpleITK将空间信息拆分为
Origin(原点坐标)、Spacing(轴间距)、Direction(3x3方向矩阵)三个独立参数
我们需要手动从Nibabel的仿射矩阵中拆分出这三个参数,再迁移头文件中的元数据。
具体实现步骤
1. 加载5D Nifti图像并切片
先用Nibabel加载图像,按需求切片得到目标3D numpy数组。假设5D维度为(x, y, z, t, c),示例中按时间维度t和组分维度c切片:
import nibabel as nib import numpy as np import SimpleITK as sitk # 加载5D Nifti图像 nii_img = nib.load("5d_mri.nii.gz") nii_data = nii_img.get_fdata() # 维度:(x, y, z, t, c) # 切片提取3D数据(示例:取t=0、c=0的切片) slice_3d_data = nii_data[:, :, :, 0, 0]
2. 从Nibabel仿射矩阵提取SimpleITK空间参数
Nibabel的仿射矩阵结构为:
[ [a11, a12, a13, ox], [a21, a22, a23, oy], [a31, a32, a33, oz], [0, 0, 0, 1] ]
其中前3×3子矩阵是方向向量×轴间距,最后一列是原点坐标(ox, oy, oz)。据此拆分参数:
affine = nii_img.affine # 提取原点坐标 origin = affine[:3, 3].tolist() # 计算各轴间距:方向向量的L2范数 spacing = np.linalg.norm(affine[:3, :3], axis=0).tolist() # 计算方向矩阵:方向向量除以对应间距后展平 direction = (affine[:3, :3] / spacing).flatten().tolist()
3. 创建SimpleITK 3D图像并设置空间信息
注意Nibabel与SimpleITK的维度顺序差异(Nibabel为x,y,z,SimpleITK为z,y,x),需先转置数组再创建图像:
# 转置数组以匹配SimpleITK的维度顺序 sitk_data = sitk.GetImageFromArray(np.transpose(slice_3d_data, (2, 1, 0))) # 设置空间信息 sitk_data.SetOrigin(origin) sitk_data.SetSpacing(spacing) sitk_data.SetDirection(direction)
4. 迁移头文件元数据
遍历Nibabel头文件字段,将可兼容的元数据迁移到SimpleITK的元数据字典中:
header = nii_img.header # 遍历并迁移元数据,跳过无法序列化的复杂类型 for key in header: try: value = header[key] if isinstance(value, (str, int, float, bool)) or np.isscalar(value): sitk_data.SetMetaData(key, str(value)) except Exception: # 忽略无法设置的专属字段 pass # 可选:手动匹配像素类型(示例:转为float64) # sitk_data = sitk.Cast(sitk_data, sitk.sitkFloat64)
关键注意点
- 维度顺序必须转置,否则空间位置会完全错乱;
- 若原图像仿射包含剪切变换(非正交),上述拆分方法依然适用,SimpleITK支持非正交方向矩阵;
- 部分Nibabel专属头字段无法在SimpleITK中设置,直接忽略即可。
内容的提问来源于stack exchange,提问作者Jeremy Joe
相关产品推荐
相关产品推荐

