1. NIfTI 坐标系与 DICOM 坐标系的差异
DICOM 坐标系:
- 使用 左手系:
- X 轴:从患者左侧到右侧(L → R)。
- Y 轴:从患者背部到前部(P → A)。
- Z 轴:从患者脚部到头部(F → H)。
NIfTI 坐标系:
- 使用 右手系:
- X 轴:从左到右。
- Y 轴:从后到前。
- Z 轴:从下到上。
由于这两个坐标系的手性不同,在从 DICOM 转换到 NIfTI 时,方向可能会发生翻转。这会影响 image_position_patient 的计算和显示。
如果不进行坐标系转换,代码如下
def dicom_to_nifti(dicom_path, output_nii_path):
# 获取所有 DICOM 文件路径
dicom_files = [os.path.join(dicom_path, f) for f in os.listdir(dicom_path) if f.endswith('.dcm')]
# 加载 DICOM 数据并按 InstanceNumber 排序
dicom_data = [pydicom.dcmread(f) for f in dicom_files]
dicom_data.sort(key=lambda x: float(x.ImagePositionPatient[2])) # 按 Z 轴排序
# 提取像素数据并堆叠为 3D 数组
pixel_data = [d.pixel_array for d in dicom_data]
# 提取元信息
rows, cols = dicom_data[0].Rows, dicom_data[0].Columns
pixel_spacing = dicom_data[0].PixelSpacing # (row_spacing, col_spacing)
slice_thickness = float(dicom_data[0].SliceThickness)
image_orientation_patient = np.array(dicom_data[0].ImageOrientationPatient) # 6 个值
image_position_patient = np.array(dicom_data[0].ImagePositionPatient) # 3 个值
# 计算仿射矩阵
affine = np.eye(4)
# X 方向
affine[:3, 0] = image_orientation_patient[:3] * pixel_spacing[1] # 列方向
# Y 方向
affine[:3, 1] = image_orientation_patient[3:] * pixel_spacing[0] # 行方向
# Z 方向
z_direction = np.cross(image_orientation_patient[:3], image_orientation_patient[3:])
affine[:3, 2] = z_direction * slice_thickness # 切片方向
# 源点坐标
affine[:3, 3] = [image_position_patient[0], image_position_patient[1], image_position_patient[2]]
pixel_data = np.transpose(pixel_data, (0, 1, 2)) # 不修改坐标
# 打印仿射矩阵
print("Adjusted Affine Matrix:\n", affine)
# 创建 NIfTI 图像
nifti_image = nib.Nifti1Image(pixel_data, affine)
nib.save(nifti_image, output_nii_path)
图像的原点

结果如下,发现原点坐标是(89.06,-63.56,-1073.60)

修改代码:
pixel_data = np.transpose(pixel_data, (2, 1, 0)) # 假设需要从 ZYX 转换为 XYZ
pixel_data = np.flip(pixel_data, axis=1)
结果发现 原点坐标 (-249.56,-63.56,-887.60)

但是发现右下角坐标是(197.56,383.56,-887.60),是原点的相反数

修改代码:
# 计算仿射矩阵
affine = np.eye(4)
# X 方向的反方向
affine[:3, 0] = -image_orientation_patient[:3] * pixel_spacing[1] # 列方向
# Y 方向的反方向
affine[:3, 1] = -image_orientation_patient[3:] * pixel_spacing[0] # 行方向
# Z 方向
z_direction = np.cross(image_orientation_patient[:3], image_orientation_patient[3:])
affine[:3, 2] = z_direction * slice_thickness # 切片方向
# 源点坐标取相反数
affine[:3, 3] = [-image_position_patient[0], -image_position_patient[1], image_position_patient[2]]
注释上下翻转
#pixel_data = np.flip(pixel_data, axis=1)
最后结果:原点坐标(-197.56,-383.56,-887.6)

最后完整代码
def dicom_to_nifti(dicom_path, output_nii_path):
# 获取所有 DICOM 文件路径
dicom_files = [os.path.join(dicom_path, f) for f in os.listdir(dicom_path) if f.endswith('.dcm')]
# 加载 DICOM 数据并按 InstanceNumber 排序
dicom_data = [pydicom.dcmread(f) for f in dicom_files]
dicom_data.sort(key=lambda x: float(x.ImagePositionPatient[2])) # 按 Z 轴排序
# 提取像素数据并堆叠为 3D 数组
pixel_data = [d.pixel_array for d in dicom_data]
# 提取元信息
rows, cols = dicom_data[0].Rows, dicom_data[0].Columns
pixel_spacing = dicom_data[0].PixelSpacing # (row_spacing, col_spacing)
slice_thickness = float(dicom_data[0].SliceThickness)
image_orientation_patient = np.array(dicom_data[0].ImageOrientationPatient) # 6 个值
image_position_patient = np.array(dicom_data[0].ImagePositionPatient) # 3 个值
# 计算仿射矩阵
affine = np.eye(4)
# X 方向的反方向
affine[:3, 0] = -image_orientation_patient[:3] * pixel_spacing[1] # 列方向
# Y 方向的反方向
affine[:3, 1] = -image_orientation_patient[3:] * pixel_spacing[0] # 行方向
# Z 方向
z_direction = np.cross(image_orientation_patient[:3], image_orientation_patient[3:])
affine[:3, 2] = z_direction * slice_thickness # 切片方向
# 源点坐标取相反数
affine[:3, 3] = [-image_position_patient[0], -image_position_patient[1], image_position_patient[2]]
# 打印仿射矩阵
print("Adjusted Affine Matrix:\n", affine)
pixel_data = np.transpose(pixel_data, (2, 1, 0)) # 假设需要从 ZYX 转换为 XYZ
#pixel_data = np.flip(pixel_data, axis=1)
# 创建 NIfTI 图像
nifti_image = nib.Nifti1Image(pixel_data, affine)
nib.save(nifti_image, output_nii_path)
print(f"NIfTI 文件已保存到: {output_nii_path}")
&spm=1001.2101.3001.5002&articleId=145003350&d=1&t=3&u=5df215c65abd49e1864c8107ac70e1e5)
8374

被折叠的 条评论
为什么被折叠?



