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()
../_images/6fdf981f789f7f4ec183b09bc567e7b9baad6598e4973e1280e5a0af84f6314a.png

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,)