{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Image Registration with Multiple and Custom Parameter Maps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Registration" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import itk" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "We can run the registration with multiple stages, using the spatial transformation result from the current stage to initialize registration at the next stage.\n", "\n", "Typically, we start with a simple spatial transformation and advance to a more complex spatial transformation.\n", "\n", "The default, conservative, registration parameters progress from a *translation -> affine -> bspline* transformation, with 4 resolutions for each transformation type.\n", "\n", "When multiple resolutions are used, a multi-scale image pyramid is generated with downscaled versions of the image. Registration results at a lower resolution is used to initialize registration at a higher resolution.\n", "This improves speed and robustness.\n", "\n", "The number of resolutions can be manually specified aswell.\n", "\n", "A default set of registration parameters are available for\n", "\n", "- *translation*\n", "- *rigid*\n", "- *affine*\n", "- *bspline*\n", "- *spline*\n", "- *groupwise*\n", "\n", "transformations. More information on these transformation and on all possible parameter settings can be found in the [elastix manual](https://elastix.lumc.nl/download/elastix-5.0.1-manual.pdf)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ParameterObject (000001C71962FAD0)\n", " RTTI typeinfo: class elastix::ParameterObject\n", " Reference Count: 1\n", " Modified Time: 69\n", " Debug: Off\n", " Object Name: \n", " Observers: \n", " none\n", "ParameterMap 0: \n", " (AutomaticParameterEstimation \"true\")\n", " (AutomaticScalesEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 3)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"EulerTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 1: \n", " (AutomaticParameterEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FinalGridSpacingInPhysicalUnits 20)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (GridSpacingSchedule 1.9881 1.41 1)\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\" \"TransformBendingEnergyPenalty\")\n", " (Metric0Weight 1)\n", " (Metric1Weight 1)\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 3)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiMetricMultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"BSplineTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 2: \n", " (BSplineInterpolationOrder 1)\n", " (DefaultPixelValue 0)\n", " (ErodeMask \"false\")\n", " (FinalBSplineInterpolationOrder 3)\n", " (FinalGridSpacingInPhysicalUnits 16)\n", " (FixedImagePyramid \"FixedRecursiveImagePyramid\")\n", " (FixedInternalImagePixelType \"float\")\n", " (HowToCombineTransforms \"Compose\")\n", " (ImageSampler \"Random\")\n", " (Interpolator \"BSplineInterpolator\")\n", " (MaximumNumberOfIterations 500)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingRecursiveImagePyramid\")\n", " (MovingInternalImagePixelType \"float\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfHistogramBins 32)\n", " (NumberOfResolutions 1)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (ResultImagePixelType \"short\")\n", " (Transform \"BSplineTransform\")\n", " (UseDirectionCosines \"true\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 3: \n", " (AutomaticParameterEstimation \"true\")\n", " (AutomaticScalesEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 4)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"BSplineTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "\n" ] } ], "source": [ "# Import Images\n", "fixed_image = itk.imread('data/CT_2D_head_fixed.mha', itk.F)\n", "moving_image = itk.imread('data/CT_2D_head_moving.mha', itk.F)\n", "\n", "# Import Default Parameter Map\n", "parameter_object = itk.ParameterObject.New()\n", "resolutions = 3\n", "parameter_map_rigid = parameter_object.GetDefaultParameterMap('rigid',3)\n", "parameter_object.AddParameterMap(parameter_map_rigid)\n", "\n", "# For the bspline default parameter map, an extra argument can be specified that define the final bspline grid spacing in physical space. \n", "parameter_map_bspline = parameter_object.GetDefaultParameterMap(\"bspline\", resolutions, 20.0)\n", "parameter_object.AddParameterMap(parameter_map_bspline)\n", "\n", "\n", "# .. and/or load custom parameter maps from .txt file\n", "parameter_object.AddParameterFile('data/parameters_BSpline.txt')\n", "\n", "# ... and/or load and customize more\n", "parameter_map_custom = parameter_object.GetDefaultParameterMap('rigid')\n", "parameter_map_custom['Transform'] = ['BSplineTransform']\n", "parameter_map_custom['Metric'] = ['AdvancedMattesMutualInformation']\n", "parameter_object.AddParameterMap(parameter_map_custom)\n", "\n", "# ... or customize parameter maps afterwards\n", "# here the 'NumberOfResolutions' parameter of the 2nd parameter map of the parameter_object is set to 1.\n", "parameter_object.SetParameter(2, \"NumberOfResolutions\", \"1\")\n", "\n", "# here the 'DefaultPixelValue' of all parameter maps is in the parameter_object set to 0\n", "parameter_object.SetParameter(\"DefaultPixelValue\", \"0\")\n", "\n", "# Remove a parameter\n", "parameter_object.RemoveParameter(\"ResultImageFormat\")\n", "\n", "print(parameter_object)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The parameter object or the seperate parameter maps can be saved or serialized in several ways" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Save custom parameter map\n", "parameter_object.WriteParameterFile(\n", " parameter_map_custom, 'exampleoutput/parameters_custom.txt')\n", "\n", "# Or serialize each parameter map to a file.\n", "for index in range(parameter_object.GetNumberOfParameterMaps()):\n", " parameter_map = parameter_object.GetParameterMap(index)\n", " parameter_object.WriteParameterFile(parameter_map, \"exampleoutput/Parameters.{0}.txt\".format(index))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ParameterObject (00000246C4B724A0)\n", " RTTI typeinfo: class elastix::ParameterObject\n", " Reference Count: 1\n", " Modified Time: 61\n", " Debug: Off\n", " Object Name: \n", " Observers: \n", " none\n", "ParameterMap 0: \n", " (AutomaticParameterEstimation \"true\")\n", " (AutomaticScalesEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 3)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"EulerTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 1: \n", " (AutomaticParameterEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FinalGridSpacingInPhysicalUnits 20)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (GridSpacingSchedule 1.9881 1.41 1)\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\" \"TransformBendingEnergyPenalty\")\n", " (Metric0Weight 1)\n", " (Metric1Weight 1)\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 3)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiMetricMultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"BSplineTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 2: \n", " (BSplineInterpolationOrder 1)\n", " (DefaultPixelValue 0)\n", " (ErodeMask \"false\")\n", " (FinalBSplineInterpolationOrder 3)\n", " (FinalGridSpacingInPhysicalUnits 16)\n", " (FixedImagePyramid \"FixedRecursiveImagePyramid\")\n", " (FixedInternalImagePixelType \"float\")\n", " (HowToCombineTransforms \"Compose\")\n", " (ImageSampler \"Random\")\n", " (Interpolator \"BSplineInterpolator\")\n", " (MaximumNumberOfIterations 500)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingRecursiveImagePyramid\")\n", " (MovingInternalImagePixelType \"float\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfHistogramBins 32)\n", " (NumberOfResolutions 1)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (ResultImagePixelType \"short\")\n", " (Transform \"BSplineTransform\")\n", " (UseDirectionCosines \"true\")\n", " (WriteResultImage \"true\")\n", "ParameterMap 3: \n", " (AutomaticParameterEstimation \"true\")\n", " (AutomaticScalesEstimation \"true\")\n", " (CheckNumberOfSamples \"true\")\n", " (DefaultPixelValue 0)\n", " (FinalBSplineInterpolationOrder 3)\n", " (FixedImagePyramid \"FixedSmoothingImagePyramid\")\n", " (ImageSampler \"RandomCoordinate\")\n", " (Interpolator \"LinearInterpolator\")\n", " (MaximumNumberOfIterations 256)\n", " (MaximumNumberOfSamplingAttempts 8)\n", " (Metric \"AdvancedMattesMutualInformation\")\n", " (MovingImagePyramid \"MovingSmoothingImagePyramid\")\n", " (NewSamplesEveryIteration \"true\")\n", " (NumberOfResolutions 4)\n", " (NumberOfSamplesForExactGradient 4096)\n", " (NumberOfSpatialSamples 2048)\n", " (Optimizer \"AdaptiveStochasticGradientDescent\")\n", " (Registration \"MultiResolutionRegistration\")\n", " (ResampleInterpolator \"FinalBSplineInterpolator\")\n", " (Resampler \"DefaultResampler\")\n", " (Transform \"BSplineTransform\")\n", " (WriteIterationInfo \"false\")\n", " (WriteResultImage \"true\")\n", "\n" ] } ], "source": [ "# To deserialize the parameters\n", "parameter_files = [\"exampleoutput/Parameters.{0}.txt\".format(i) for i in range(4)]\n", "parameter_object = itk.ParameterObject.New()\n", "parameter_object.ReadParameterFile(parameter_files)\n", "print(parameter_object)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information on the available parameters and their meaning, see the [Elastix Parameters](https://elastix.lumc.nl/doxygen/parameter.html) documentation.\n", "\n", "A database of parameter sets that work well for specific types of data can be found on the [Elastix parameter modelzoo](https://elastix.lumc.nl/modelzoo/), in sync with it's [Github repo](https://github.com/SuperElastix/ElastixModelZoo) where parameter files can be uploaded." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Registration can either be done in one line with the registration function..." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Call registration function\n", "result_image, result_transform_parameters = itk.elastix_registration_method(\n", " fixed_image, moving_image,\n", " parameter_object=parameter_object,\n", " log_to_console=False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ ".. or by initiating an elastix image filter object." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# Load Elastix Image Filter Object\n", "elastix_object = itk.ElastixRegistrationMethod.New(fixed_image, moving_image)\n", "# elastix_object.SetFixedImage(fixed_image)\n", "# elastix_object.SetMovingImage(moving_image)\n", "elastix_object.SetParameterObject(parameter_object)\n", "\n", "# Set additional options\n", "elastix_object.SetLogToConsole(False)\n", "\n", "# Update filter object (required)\n", "elastix_object.UpdateLargestPossibleRegion()\n", "\n", "# Results of Registration\n", "result_image = elastix_object.GetOutput()\n", "result_transform_parameters = elastix_object.GetTransformParameterObject()" ] } ], "metadata": { "kernelspec": { "display_name": "ITKElastix", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.11" } }, "nbformat": 4, "nbformat_minor": 4 }