2. Exploring registration parameters#
In this notebook we’ll look at some of the registration parameters.
All parameters are explained in detail in the elastix manual.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import itk
from skimage import io
Load data#
fixed = io.imread("../example_data/fluo_ch0.tif")
moving = io.imread("../example_data/fluo_ch1.tif")
fixed_image_itk = itk.GetImageFromArray(fixed)
moving_image_itk = itk.GetImageFromArray(moving)
# show the images next to each other
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
ax[0].imshow(fixed_image_itk, cmap='gray')
ax[0].set_title('Fluo channel 0')
ax[0].axis('off')
ax[1].imshow(moving_image_itk, cmap='gray')
ax[1].set_title('Fluo channel 1')
ax[1].axis('off')
plt.show()
Registering using different transforms#
Different types of transformations can be used to register images, depending on the expected spatial transformations between the images. itk-elastix provides a set of default parameters for the following types of transformations:
translation
rigid
affine
bspline
spline
groupwise
Transform: Translation#
pmap_map_translation = itk.ParameterObject.New().GetDefaultParameterMap('translation')
registration_parameter_object = itk.ParameterObject.New()
registration_parameter_object.AddParameterMap(pmap_map_translation)
transformed_moving_image_translation, result_transform_parameters = itk.elastix_registration_method(
fixed_image_itk,
moving_image_itk,
parameter_object=registration_parameter_object,
log_to_console=False
)
Transform: Rigid#
A rigid transformation accounts for translation and rotation between the fixed and moving images.
pmap_map_rigid = itk.ParameterObject.New().GetDefaultParameterMap('rigid')
registration_parameter_object = itk.ParameterObject.New()
registration_parameter_object.AddParameterMap(pmap_map_rigid)
transformed_moving_image_rigid, result_transform_parameters = itk.elastix_registration_method(
fixed_image_itk,
moving_image_itk,
parameter_object=registration_parameter_object,
log_to_console=False
)
Comparing the registration results#
Which transform suits the data better?
import napari
def add_itk_image_to_viewer(viewer, image_itk, layer_name, colormap):
viewer.add_image(
image_itk,
translate=fixed_image_itk.GetOrigin(),
scale=fixed_image_itk.GetSpacing(),
name=layer_name, colormap=colormap, blending='additive')
viewer = napari.Viewer()
add_itk_image_to_viewer(viewer, fixed_image_itk, 'fixed', 'Grey')
add_itk_image_to_viewer(viewer, moving_image_itk, 'moving', 'Red')
add_itk_image_to_viewer(viewer, transformed_moving_image_translation, 'translation', 'Red')
add_itk_image_to_viewer(viewer, transformed_moving_image_rigid, 'rigid', 'Red')
Multi-resolution registration#
The registration can be performed at multiple resolutions. This can be useful to speed up the registration process, and to avoid local minima in the optimization process.
pmap = itk.ParameterObject.New().GetDefaultParameterMap('rigid')
# print the default parameter map
pd.DataFrame(
{'Parameters': pmap.keys(),
'Values': pmap.values()}
)
Parameters | Values | |
---|---|---|
0 | AutomaticParameterEstimation | (true,) |
1 | AutomaticScalesEstimation | (true,) |
2 | CheckNumberOfSamples | (true,) |
3 | DefaultPixelValue | (0,) |
4 | FinalBSplineInterpolationOrder | (3,) |
5 | FixedImagePyramid | (FixedSmoothingImagePyramid,) |
6 | ImageSampler | (RandomCoordinate,) |
7 | Interpolator | (LinearInterpolator,) |
8 | MaximumNumberOfIterations | (256,) |
9 | MaximumNumberOfSamplingAttempts | (8,) |
10 | Metric | (AdvancedMattesMutualInformation,) |
11 | MovingImagePyramid | (MovingSmoothingImagePyramid,) |
12 | NewSamplesEveryIteration | (true,) |
13 | NumberOfResolutions | (4,) |
14 | NumberOfSamplesForExactGradient | (4096,) |
15 | NumberOfSpatialSamples | (2048,) |
16 | Optimizer | (AdaptiveStochasticGradientDescent,) |
17 | Registration | (MultiResolutionRegistration,) |
18 | ResampleInterpolator | (FinalBSplineInterpolator,) |
19 | Resampler | (DefaultResampler,) |
20 | ResultImageFormat | (nii,) |
21 | Transform | (EulerTransform,) |
22 | WriteIterationInfo | (false,) |
23 | WriteResultImage | (true,) |
# we can change the number of resolutions used for registration
pmap["NumberOfResolutions"] = ("1",)
# print the default parameter map
pd.DataFrame(
{'Parameters': pmap.keys(),
'Values': pmap.values()}
)
Parameters | Values | |
---|---|---|
0 | AutomaticParameterEstimation | (true,) |
1 | AutomaticScalesEstimation | (true,) |
2 | CheckNumberOfSamples | (true,) |
3 | DefaultPixelValue | (0,) |
4 | FinalBSplineInterpolationOrder | (3,) |
5 | FixedImagePyramid | (FixedSmoothingImagePyramid,) |
6 | ImageSampler | (RandomCoordinate,) |
7 | Interpolator | (LinearInterpolator,) |
8 | MaximumNumberOfIterations | (256,) |
9 | MaximumNumberOfSamplingAttempts | (8,) |
10 | Metric | (AdvancedMattesMutualInformation,) |
11 | MovingImagePyramid | (MovingSmoothingImagePyramid,) |
12 | NewSamplesEveryIteration | (true,) |
13 | NumberOfResolutions | (1,) |
14 | NumberOfSamplesForExactGradient | (4096,) |
15 | NumberOfSpatialSamples | (2048,) |
16 | Optimizer | (AdaptiveStochasticGradientDescent,) |
17 | Registration | (MultiResolutionRegistration,) |
18 | ResampleInterpolator | (FinalBSplineInterpolator,) |
19 | Resampler | (DefaultResampler,) |
20 | ResultImageFormat | (nii,) |
21 | Transform | (EulerTransform,) |
22 | WriteIterationInfo | (false,) |
23 | WriteResultImage | (true,) |
# Runnning the registration
registration_parameter_object = itk.ParameterObject.New()
registration_parameter_object.AddParameterMap(pmap)
transformed_moving_image, result_transform_parameters = itk.elastix_registration_method(
fixed_image_itk,
moving_image_itk,
parameter_object=registration_parameter_object,
log_to_console=False
)
import napari
def add_itk_image_to_viewer(viewer, image_itk, layer_name, colormap):
viewer.add_image(
image_itk,
translate=fixed_image_itk.GetOrigin(),
scale=fixed_image_itk.GetSpacing(),
name=layer_name, colormap=colormap, blending='additive')
viewer = napari.Viewer()
add_itk_image_to_viewer(viewer, fixed_image_itk, 'fixed', 'Grey')
add_itk_image_to_viewer(viewer, moving_image_itk, 'moving', 'Red')
add_itk_image_to_viewer(viewer, transformed_moving_image, 'transformed', 'Red')
Visualization#
In this case the registration worked well with only one resolution, but in general it is a good idea to use multiple resolutions.
import napari
def add_itk_image_to_viewer(viewer, image_itk, layer_name, colormap):
viewer.add_image(
image_itk,
translate=fixed_image_itk.GetOrigin(),
scale=fixed_image_itk.GetSpacing(),
name=layer_name, colormap=colormap, blending='additive')
viewer = napari.Viewer()
add_itk_image_to_viewer(viewer, fixed_image_itk, 'fixed', 'Grey')
add_itk_image_to_viewer(viewer, moving_image_itk, 'moving', 'Red')
add_itk_image_to_viewer(viewer, transformed_moving_image_translation, 'translation', 'Red')
add_itk_image_to_viewer(viewer, transformed_moving_image_rigid, 'rigid', 'Red')
Image metrics#
According to the elastix manual: “The AdvancedMattesMutualInformation usually works well, both for mono- and multi-modal images”.
In my experience, sometimes the “AdvancedNormalizedCorrelation” metric works better for mono-modal images.
pmap = itk.ParameterObject.New().GetDefaultParameterMap('rigid')
pmap['Metric'] = ("AdvancedNormalizedCorrelation", )
# print the default parameter map
pd.DataFrame(
{'Parameters': pmap.keys(),
'Values': pmap.values()}
)
Parameters | Values | |
---|---|---|
0 | AutomaticParameterEstimation | (true,) |
1 | AutomaticScalesEstimation | (true,) |
2 | CheckNumberOfSamples | (true,) |
3 | DefaultPixelValue | (0,) |
4 | FinalBSplineInterpolationOrder | (3,) |
5 | FixedImagePyramid | (FixedSmoothingImagePyramid,) |
6 | ImageSampler | (RandomCoordinate,) |
7 | Interpolator | (LinearInterpolator,) |
8 | MaximumNumberOfIterations | (256,) |
9 | MaximumNumberOfSamplingAttempts | (8,) |
10 | Metric | (AdvancedNormalizedCorrelation,) |
11 | MovingImagePyramid | (MovingSmoothingImagePyramid,) |
12 | NewSamplesEveryIteration | (true,) |
13 | NumberOfResolutions | (4,) |
14 | NumberOfSamplesForExactGradient | (4096,) |
15 | NumberOfSpatialSamples | (2048,) |
16 | Optimizer | (AdaptiveStochasticGradientDescent,) |
17 | Registration | (MultiResolutionRegistration,) |
18 | ResampleInterpolator | (FinalBSplineInterpolator,) |
19 | Resampler | (DefaultResampler,) |
20 | ResultImageFormat | (nii,) |
21 | Transform | (EulerTransform,) |
22 | WriteIterationInfo | (false,) |
23 | WriteResultImage | (true,) |