介紹如何在 Python 中使用 ITK 進行影像的線性仿射影像對準(affine registration)。
準備測試用影像
這裡我們使用一張影像作為基準影像(fixed image),以 AffineTransform 線性仿射轉換套用至基準影像上,產生一張調動影像(moving image)。
import itk
import matplotlib.pyplot as plt
# 影像像素資料類型
PixelType = itk.SS
# 實體座標系統用的資料類型
ScalarType = itk.D
# 影像維度
Dimension = 2
# 影像類型
ImageType = itk.Image[PixelType, Dimension]
# 建立影像 Reader
ReaderType = itk.ImageFileReader[ImageType]
reader = ReaderType.New()
reader.SetFileName("fixed.mhd")
reader.Update()
# 讀取 Fixed 影像
fixedImage = reader.GetOutput()
# 建立 AffineTransform 線性轉換
affineTransform = itk.AffineTransform[ScalarType, Dimension].New()
parameters = affineTransform.GetParameters()
parameters[] = 0.9 # 旋轉與變形
parameters[1] = 0.1 # 旋轉與變形
parameters[2] = 0.1 # 旋轉與變形
parameters[3] = 0.9 # 旋轉與變形
parameters[4] = 25 # X 軸平移
parameters[5] = -5 # Y 軸平移
affineTransform.SetParameters(parameters)
# 建立 Resampler
resamplerType = itk.ResampleImageFilter[ImageType, ImageType]
resampleFilter = resamplerType.New()
# 設定 Resampler 各項參數
resampleFilter.SetInput(fixedImage)
resampleFilter.SetTransform(affineTransform)
resampleFilter.SetReferenceImage(fixedImage)
resampleFilter.SetUseReferenceImage(True)
resampleFilter.Update()
# 產生 Moving 影像
movingImage = resampleFilter.GetOutput()
# 分開顯示兩張影像
fig, axs = plt.subplots(1, 2)
axs[].imshow(itk.GetArrayViewFromImage(fixedImage), cmap='gray')
axs[].set_title('Fixed Image')
axs[1].imshow(itk.GetArrayViewFromImage(movingImage), cmap='gray')
axs[1].set_title('Moving Image')
plt.show()

仿射對準(Affine Registration)
由於為了縮減 Python 模組的大小,Python 中只支援特定幾種影像類型的對準,所以要進行對準之前,必須先把影像轉換為支援的類型,再進行後續的對準動作。
# 影像轉型
InternalImageType = itk.Image[itk.F, Dimension]
fixedCastImageFilter = itk.CastImageFilter[ImageType, InternalImageType].New()
fixedCastImageFilter.SetInput(fixedImage)
movingCastImageFilter = itk.CastImageFilter[ImageType, InternalImageType].New()
movingCastImageFilter.SetInput(movingImage)
# 影像對準所使用的初始 Transform
TransformType = itk.AffineTransform[ScalarType, Dimension]
initialTransform = TransformType.New()
initialTransform.SetIdentity()
# 參數數量
numOfParam = initialTransform.GetNumberOfParameters()
print("Number of parameters: {}".format(numOfParam))
Number of parameters: 6
# 影像對準所使用的 Optimizer
optimizer = itk.RegularStepGradientDescentOptimizerv4.New()
optimizer.SetLearningRate(1)
optimizer.SetMinimumStepLength(0.0001)
optimizer.SetNumberOfIterations(200)
optimizer.SetRelaxationFactor(0.9)
# Optimizer Scales
optimizerScales = itk.OptimizerParameters.D(numOfParam)
optimizerScales.SetElement(0, 1.0) # 旋轉與變形
optimizerScales.SetElement(1, 1.0) # 旋轉與變形
optimizerScales.SetElement(2, 1.0) # 旋轉與變形
optimizerScales.SetElement(3, 1.0) # 旋轉與變形
optimizerScales.SetElement(4, 0.05) # X 軸平移
optimizerScales.SetElement(5, 0.05) # X 軸平移
optimizer.SetScales(optimizerScales)
# 影像對準所使用的 Metric
#MetricType = itk.MeanSquaresImageToImageMetricv4[InternalImageType, InternalImageType]
MetricType = itk.MattesMutualInformationImageToImageMetricv4[InternalImageType, InternalImageType]
metric = MetricType.New()
# 影像對準
registration = itk.ImageRegistrationMethodv4[InternalImageType, InternalImageType].New(
FixedImage=fixedCastImageFilter.GetOutput(),
MovingImage=movingCastImageFilter.GetOutput(),
Metric=metric,
Optimizer=optimizer,
InitialTransform=initialTransform)
# Moving 影像所使用的初始 Transform
movingInitialTransform = TransformType.New()
movingInitialTransform.SetIdentity()
registration.SetMovingInitialTransform(movingInitialTransform)
# Fixed 影像所使用的初始 Transform
identityTransform = TransformType.New()
identityTransform.SetIdentity()
registration.SetFixedInitialTransform(identityTransform)
# 設定 Levels 相關參數
registration.SetNumberOfLevels(1)
registration.SetSmoothingSigmasPerLevel([0])
registration.SetShrinkFactorsPerLevel([1])
# 進行影像對準
registration.Update()
# 取得影像對準結果
transform = registration.GetTransform()
finalParameters = transform.GetParameters()
numberOfIterations = optimizer.GetCurrentIteration()
bestMetricValue = optimizer.GetValue()
print("Registration result: ")
print(" Iterations = " + str(numberOfIterations))
print(" Metric value = " + str(bestMetricValue))
Registration result: Iterations = 200 Metric value = -0.6965207175899221
影像轉換
做完影像對準之後,接著進行影像轉換:
# 建立對準結果的 Transform
CompositeTransformType = itk.CompositeTransform[ScalarType, Dimension]
outputCompositeTransform = CompositeTransformType.New()
outputCompositeTransform.AddTransform(movingInitialTransform)
outputCompositeTransform.AddTransform(registration.GetModifiableTransform())
# 建立輸出對準結果用的 Resampler
resampler = itk.ResampleImageFilter.New(Input=movingImage,
Transform=outputCompositeTransform,
UseReferenceImage=True,
ReferenceImage=fixedImage)
resampler.SetDefaultPixelValue(100)
# 分開顯示兩張影像
fig, axs = plt.subplots(1, 2)
axs[].imshow(itk.GetArrayViewFromImage(fixedImage), cmap='gray')
axs[].set_title('Fixed Image')
axs[1].imshow(itk.GetArrayViewFromImage(resampler.GetOutput()), cmap='gray')
axs[1].set_title('Result Image')
plt.show()

# 將像素值型態轉為 unsigned char
OutputPixelType = itk.ctype('unsigned char')
OutputImageType = itk.Image[OutputPixelType, Dimension]
fixedIntensityRescaler = itk.RescaleIntensityImageFilter[ImageType,
OutputImageType].New(
Input=fixedImage,
OutputMinimum=itk.NumericTraits[OutputPixelType].min(),
OutputMaximum=itk.NumericTraits[OutputPixelType].max())
movingIntensityRescaler = itk.RescaleIntensityImageFilter[ImageType,
OutputImageType].New(
Input=movingImage,
OutputMinimum=itk.NumericTraits[OutputPixelType].min(),
OutputMaximum=itk.NumericTraits[OutputPixelType].max())
resultIntensityRescaler = itk.RescaleIntensityImageFilter[ImageType,
OutputImageType].New(
Input=resampler.GetOutput(),
OutputMinimum=itk.NumericTraits[OutputPixelType].min(),
OutputMaximum=itk.NumericTraits[OutputPixelType].max())
# 合併為 RGB 影像
ComponentType = itk.UC
RGBPixelType = itk.RGBPixel[ComponentType]
RGBImageType = itk.Image[RGBPixelType, Dimension]
composeFilter = itk.ComposeImageFilter[OutputImageType, RGBImageType].New()
composeFilter.SetInput(0, fixedIntensityRescaler.GetOutput())
composeFilter.SetInput(1, movingIntensityRescaler.GetOutput())
composeFilter.SetInput(2, fixedIntensityRescaler.GetOutput())
composeFilter2 = itk.ComposeImageFilter[OutputImageType, RGBImageType].New()
composeFilter2.SetInput(0, fixedIntensityRescaler.GetOutput())
composeFilter2.SetInput(1, resultIntensityRescaler.GetOutput())
composeFilter2.SetInput(2, fixedIntensityRescaler.GetOutput())
# 分開顯示兩張影像
fig, axs = plt.subplots(1, 2)
axs[].imshow(itk.GetArrayViewFromImage(composeFilter.GetOutput()))
axs[].set_title('Initial Image')
axs[1].imshow(itk.GetArrayViewFromImage(composeFilter2.GetOutput()))
axs[1].set_title('Result Image')
plt.show()

