使用SimpleITK对CT图像进行重采样和下采样

CT 重采样是很常见的一种前处理技术。

重采样

基于ITK库进行CT图像重采样,通常有两种主流方式:一是直接使用底层的 ITK 库(C++或Python),二是使用其更友好的高层封装 SimpleITK(主要支持Python)。SimpleITK 接口更简洁,是实现这类任务的更常见选择。

其核心思路是:计算新的图像尺寸,使新旧图像覆盖相同的物理空间。下面以 SimpleITK 为例,详细介绍实现步骤。

🛠️ 准备工作:安装与导入

首先,确保已安装 SimpleITK 库。

1
pip install SimpleITK

在Python脚本中导入:

1
2
import SimpleITK as sitk
import numpy as np

📝 核心步骤:用 SimpleITK 实现重采样

实现重采样的核心是配置 sitk.ResampleImageFilter()。主要步骤如下:

  1. 读取原始图像:使用 sitk.ReadImage() 读取CT图像。
  2. 获取原始参数:通过 GetSize() 和 GetSpacing() 获取原始图像的尺寸和体素间距。
  3. 设定目标参数:定义你期望的目标体素间距 (new_spacing)。
  4. 计算新图像尺寸:根据“物理空间不变”的原则计算新尺寸。
    1
    2
    3
    4
    new_size = [
    int(orig_size[i] * (orig_spacing[i] / new_spacing[i]) + 0.5)
    for i in range(3)
    ]
    > +0.5 是为了四舍五入取整。
  5. 配置并执行重采样:创建 ResampleImageFilter,设置好所有参数后执行。

💻 完整代码示例

方法一:封装为通用函数 (推荐)

这是一个功能完整的重采样函数,能自动处理CT图像和分割标签的不同插值方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import SimpleITK as sitk
import numpy as np

def resample_image(itk_image, out_spacing=[1.0, 1.0, 1.0], is_label=False):
"""
将ITK图像重采样到指定的体素间距。
"""
# 1. 获取原始信息
original_spacing = itk_image.GetSpacing()
original_size = itk_image.GetSize()

# 2. 计算新的图像尺寸
out_size = [
int(np.round(original_size[0] * (original_spacing[0] / out_spacing[0]))),
int(np.round(original_size[1] * (original_spacing[1] / out_spacing[1]))),
int(np.round(original_size[2] * (original_spacing[2] / out_spacing[2])))
]

# 3. 配置重采样器
resampler = sitk.ResampleImageFilter()
resampler.SetSize(out_size)
resampler.SetOutputSpacing(out_spacing)
resampler.SetOutputOrigin(itk_image.GetOrigin())
resampler.SetOutputDirection(itk_image.GetDirection())

# 4. 根据图像类型选择插值方式
if is_label:
# 分割标签使用最近邻插值,以保留标签值
resampler.SetInterpolator(sitk.sitkNearestNeighbor)
else:
# 医学图像(如CT)使用线性或B-Spline插值
resampler.SetInterpolator(sitk.sitkLinear)

# 5. 执行重采样
return resampler.Execute(itk_image)

# --- 使用示例 ---
# 读取图像
input_image = sitk.ReadImage("your_ct_image.nii.gz")
# 重采样到各向同性1mm体素
resampled_image = resample_image(input_image, out_spacing=[1.0, 1.0, 1.0])
# 保存结果
sitk.WriteImage(resampled_image, "resampled_ct_image.nii.gz")

方法二:直接、简洁的实现

如果不需要封装,也可以直接编写核心逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import SimpleITK as sitk

def resample_image_itk(ori_img, new_spacing=[1.0, 1.0, 1.0]):
# 1. 获取原始信息
ori_size = ori_img.GetSize()
ori_spacing = ori_img.GetSpacing()

# 2. 计算新尺寸
new_size = [
int(ori_size[0] * ori_spacing[0] / new_spacing[0] + 0.5),
int(ori_size[1] * ori_spacing[1] / new_spacing[1] + 0.5),
int(ori_size[2] * ori_spacing[2] / new_spacing[2] + 0.5)
]

# 3. 配置并执行重采样
resampler = sitk.ResampleImageFilter()
resampler.SetSize(new_size)
resampler.SetOutputSpacing(new_spacing)
resampler.SetOutputOrigin(ori_img.GetOrigin())
resampler.SetOutputDirection(ori_img.GetDirection())
resampler.SetInterpolator(sitk.sitkLinear) # 对CT图像使用线性插值

return resampler.Execute(ori_img)

🌐 备选方案:使用 C++ 和 ITK

如果需要在C++环境中开发,可直接使用 itk::ResampleImageFilter。其核心逻辑与Python版本一致:创建滤波器,设置变换、插值器、输出参数(Size, Spacing, Origin)等。

💎 关键点总结

  • 物理空间不变:重采样的核心是新尺寸 = 旧尺寸 * (旧间距 / 新间距),确保图像覆盖的物理范围不变。
  • 插值方法的选择:对CT等灰度图像,使用线性插值(sitkLinear)或B-Spline插值;对分割标签,务必使用最近邻插值(sitkNearestNeighbor)以保护标签值。
  • 保留空间信息:重采样时,除了设置Size和Spacing,还应将原始的Origin(原点)和Direction(方向)复制到新图像,确保其空间位置准确。

下采样

你的理解完全正确!重采样的核心逻辑正是:给定新的体素间距(Spacing) → 保持物理空间不变 → 自动换算出新的图像尺寸(Size)。

那么,下采样(Downsampling) 其实就是 重采样(Resampling)的一个特例。

简单来说:当“新间距”大于“旧间距”时,计算出的“新尺寸”就会小于“旧尺寸”,这个过程就是下采样。


1. 下采样在公式中的体现

论文里,他们将原始图像从 1024×1024 (假设对应某个体素间距)降为 256×256×128,这就是典型的4倍下采样。

在代码层面,有两种等效的实现思路:

  • 思路A(通过间距驱动):如果你想让图像缩小4倍,就把新间距设置为旧间距的4倍。

\[ \text{新尺寸} = \frac{\text{旧尺寸} \times \text{旧间距}}{\text{新间距}} = \frac{1024 \times S}{4S} = 256 \]

  • 思路B(直接指定尺寸):在ITK中,你也可以直接通过 SetSize([256, 256, 128]) 强行指定输出尺寸。此时,ITK会自动反算出一个新的间距,使物理范围不变,这也叫下采样。

2. 严格意义上的“下采样”其实包含一步预处理(防锯齿)

在数字图像处理(尤其是深度学习)中,我们通常将“尺寸缩小”笼统地称为下采样。但在严格的信号处理理论中,直接使用插值(如线性插值)进行尺寸缩小,可能会产生“混叠伪影”(Aliasing),因为高频细节在缩小时会产生失真。

因此,教科书级别的严谨下采样流程应该是:

  1. 先低通滤波(平滑):对原始图像进行高斯模糊(Gaussian Blur),滤除高频细节。
  2. 后重采样(抽取):再进行间隔采样或插值缩小。

然而,在医学图像处理的实际工程中(包括这篇论文),为了简化流程并保证CT值(HU值)的物理准确性,绝大多数场景下直接使用“三线性插值(Linear Interpolation)”进行尺寸缩小就足够了。因为CT图像本身信噪比较高,且线性插值自带一定的平滑效果,能够满足后续分割模型的精度要求。


3. 论文中的具体操作(基于ITK)

结合论文,他们使用了ITK库的程序进行自动化转换。如果我们要模拟论文中的“下采样4倍”,具体代码逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import SimpleITK as sitk

# 1. 读取原始重建的CT图像(假设原始间距为 1.41mm)
img = sitk.ReadImage("original_ct.nii.gz")
old_spacing = img.GetSpacing() # 例如 (1.41, 1.41, 1.41)
old_size = img.GetSize() # 例如 (1024, 1024, 200)

# 2. 定义下采样的目标:间距变为原来的4倍
new_spacing = [old_spacing[0] * 4, old_spacing[1] * 4, old_spacing[2] * 4] # 变成 5.64mm

# 3. 自动换算出新分辨率(这就是你说的“自动换算”)
new_size = [
int(old_size[0] * (old_spacing[0] / new_spacing[0])), # 1024 / 4 = 256
int(old_size[1] * (old_spacing[1] / new_spacing[1])), # 1024 / 4 = 256
int(old_size[2] * (old_spacing[2] / new_spacing[2])) # 200 / 4 = 50 (论文是128,说明Z轴未等比缩4倍)
]

# 4. 配置重采样器执行下采样(CT图像用线性插值)
resampler = sitk.ResampleImageFilter()
resampler.SetSize(new_size)
resampler.SetOutputSpacing(new_spacing)
resampler.SetInterpolator(sitk.sitkLinear) # 这里就是下采样的实际执行者
output = resampler.Execute(img)

总结一句话

  • 重采样:是通用大概念(包含上采样、下采样、改变各向异性)。
  • 下采样:是重采样在 “新间距 > 旧间距” 情况下的具体应用。
  • 在ITK的实现中,不管你叫它下采样还是重采样,底层调用的都是同一个 ResampleImageFilter 滤波器,区别只在于你给它的 Spacing 或 Size 参数数值大小不同。
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2026 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信