SuperElastix / elastix

Official elastix repository
http://elastix.dev
Apache License 2.0
481 stars 116 forks source link

make registration deterministic? #969

Closed zapaishchykova closed 1 year ago

zapaishchykova commented 1 year ago

Hi there! Thanks for the great repo!

I have the following Parateters_Rigid.txt file(see below) and I am trying to find which parameters would make the registration deterministic. What I mean by that, is that for each run of the registration procedure, I want to get exactly the same result. I am using it for template matching of brain MRIs and I have observed a tiny delta between my images that occurs each time I run the registration (I am using python, itk-elastix library, itk.elastix_registration_method).

I have looked into the manual and I think that might have to do with ImageSampler, but I am not 100% sure ( https://readthedocs.org/projects/simpleelastix/downloads/pdf/latest/). Any guidance on this?

Thanks!

// Example parameter file for rotation registration
// C-style comments: //

// The internal pixel type, used for internal computations
// Leave to float in general. 
// NB: this is not the type of the input images! The pixel 
// type of the input images is automatically read from the 
// images themselves.
// This setting can be changed to "short" to save some memory
// in case of very large 3D images.
(FixedInternalImagePixelType "float")
(MovingInternalImagePixelType "float")

// **************** Main Components **************************

// The following components should usually be left as they are:
(Registration "MultiResolutionRegistration")
(Interpolator "BSplineInterpolator")
(ResampleInterpolator "FinalBSplineInterpolator")
(Resampler "DefaultResampler")

// These may be changed to Fixed/MovingSmoothingImagePyramid.
// See the manual.
(FixedImagePyramid "FixedRecursiveImagePyramid")
(MovingImagePyramid "MovingRecursiveImagePyramid")

// The following components are most important:
// The optimizer AdaptiveStochasticGradientDescent (ASGD) works
// quite ok in general. The Transform and Metric are important
// and need to be chosen careful for each application. See manual.
(Optimizer "AdaptiveStochasticGradientDescent")
(Transform "EulerTransform")
(Metric "AdvancedMattesMutualInformation")

// ***************** Transformation **************************

// Scales the rotations compared to the translations, to make
// sure they are in the same range. In general, it's best to  
// use automatic scales estimation:
(AutomaticScalesEstimation "true")

// Automatically guess an initial translation by aligning the
// geometric centers of the fixed and moving.
(AutomaticTransformInitialization "true")

// Whether transforms are combined by composition or by addition.
// In generally, Compose is the best option in most cases.
// It does not influence the results very much.
(HowToCombineTransforms "Compose")

// ******************* Similarity measure *********************

// Number of grey level bins in each resolution level,
// for the mutual information. 16 or 32 usually works fine.
// You could also employ a hierarchical strategy:
//(NumberOfHistogramBins 16 32 64)
(NumberOfHistogramBins 32)

// If you use a mask, this option is important. 
// If the mask serves as region of interest, set it to false.
// If the mask indicates which pixels are valid, then set it to true.
// If you do not use a mask, the option doesn't matter.
(ErodeMask "false")

// ******************** Multiresolution **********************

// The number of resolutions. 1 Is only enough if the expected
// deformations are small. 3 or 4 mostly works fine. For large
// images and large deformations, 5 or 6 may even be useful.
(NumberOfResolutions 4)

// The downsampling/blurring factors for the image pyramids.
// By default, the images are downsampled by a factor of 2
// compared to the next resolution.
// So, in 2D, with 4 resolutions, the following schedule is used:
//(ImagePyramidSchedule 8 8  4 4  2 2  1 1 )
// And in 3D:
//(ImagePyramidSchedule 8 8 8  4 4 4  2 2 2  1 1 1 )
// You can specify any schedule, for example:
//(ImagePyramidSchedule 4 4  4 3  2 1  1 1 )
// Make sure that the number of elements equals the number
// of resolutions times the image dimension.

// ******************* Optimizer ****************************

// Maximum number of iterations in each resolution level:
// 200-500 works usually fine for rigid registration.
// For more robustness, you may increase this to 1000-2000.
(MaximumNumberOfIterations 250)

// The step size of the optimizer, in mm. By default the voxel size is used.
// which usually works well. In case of unusual high-resolution images
// (eg histology) it is necessary to increase this value a bit, to the size
// of the "smallest visible structure" in the image:
//(MaximumStepLength 1.0)

// **************** Image sampling **********************

// Number of spatial samples used to compute the mutual
// information (and its derivative) in each iteration.
// With an AdaptiveStochasticGradientDescent optimizer,
// in combination with the two options below, around 2000
// samples may already suffice.
(NumberOfSpatialSamples 2048)

// Refresh these spatial samples in every iteration, and select
// them randomly. See the manual for information on other sampling
// strategies.
(NewSamplesEveryIteration "true")
(ImageSampler "Random")

// ************* Interpolation and Resampling ****************

// Order of B-Spline interpolation used during registration/optimisation.
// It may improve accuracy if you set this to 3. Never use 0.
// An order of 1 gives linear interpolation. This is in most 
// applications a good choice.
(BSplineInterpolationOrder 1)

// Order of B-Spline interpolation used for applying the final
// deformation.
// 3 gives good accuracy; recommended in most cases.
// 1 gives worse accuracy (linear interpolation)
// 0 gives worst accuracy, but is appropriate for binary images
// (masks, segmentations); equivalent to nearest neighbor interpolation.
(FinalBSplineInterpolationOrder 3)

//Default pixel value for pixels that come from outside the picture:
(DefaultPixelValue 0)

// Choose whether to generate the deformed moving image.
// You can save some time by setting this to false, if you are
// only interested in the final (nonrigidly) deformed moving image
// for example.
(WriteResultImage "true")

// The pixel type and format of the resulting deformed moving image
(ResultImagePixelType "short")
(ResultImageFormat "mhd")
N-Dekker commented 1 year ago

@zapaishchykova Thank very much for submitting this issue, Anna. The issue is indeed caused by the image sampler using a random number generator (ImageSampler "Random"). Internally it uses the MersenneTwisterRandomVariateGenerator from ITK. Coincidentally I just submitted a proposal to allow resetting the next seed of that random number generator:

As far as I can see now, the proposed new function, ResetNextSeed(), should allow making the registration deterministic! So please "like" (👍) that pull request!

Kind regards, Niels

mstaring commented 1 year ago

And then it would be good to make use of it in elastix. This should also synchronize behavior with the command line executable, as that one is perfectly reproducible.

zapaishchykova commented 1 year ago

@N-Dekker awesome, what a great coincidence!

@mstaring do you by chance know the command line alternative for the itk.elastix_registration_method? is it this one https://manpages.ubuntu.com/manpages/trusty/man1/elastix.1.html? Then I can replace Python wrapper with some shell commands via subprocess.

mstaring commented 1 year ago

Yes it seems someone made a binary package in ubuntu of elastix.

You can also get it from https://github.com/SuperElastix/elastix/releases/tag/5.1.0

zapaishchykova commented 1 year ago

perfect, thanks! Just tested the cmd - its indeed deterministic. I will stick to cmd version for now, while waiting for the pull request to be propagated to the Python library.