SimpleITK is a simplified interface to the Insight Toolkit (ITK) for image registration and segmentation
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Complete guide to image registration workflows in SimpleITK.
Image registration aligns two images (fixed and moving) by finding an optimal geometric transformation. SimpleITK provides flexible registration through the ImageRegistrationMethod framework.
Measures similarity between fixed and moving images:
Finds optimal transform parameters:
Geometric transformation type:
Resamples moving image:
import SimpleITK as sitk
def rigid_registration(fixed_image, moving_image):
"""Perform rigid (6 DOF) registration."""
# Initialize transform at image centers
initial_transform = sitk.CenteredTransformInitializer(
fixed_image,
moving_image,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY
)
# Setup registration
registration = sitk.ImageRegistrationMethod()
# Metric
registration.SetMetricAsMattesMutualInformation(numberOfHistogramBins=50)
registration.SetMetricSamplingStrategy(registration.RANDOM)
registration.SetMetricSamplingPercentage(0.01)
# Optimizer
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100,
convergenceMinimumValue=1e-6,
convergenceWindowSize=10
)
registration.SetOptimizerScalesFromPhysicalShift()
# Interpolator
registration.SetInterpolator(sitk.sitkLinear)
# Initial transform
registration.SetInitialTransform(initial_transform, inPlace=False)
# Execute
final_transform = registration.Execute(fixed_image, moving_image)
print(f"Optimizer stop condition: {registration.GetOptimizerStopConditionDescription()}")
print(f"Final metric value: {registration.GetMetricValue()}")
return final_transformdef multiresolution_registration(fixed_image, moving_image):
"""Multi-resolution pyramid registration for better convergence."""
initial_transform = sitk.CenteredTransformInitializer(
fixed_image,
moving_image,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY
)
registration = sitk.ImageRegistrationMethod()
# Metric and optimizer
registration.SetMetricAsMattesMutualInformation(50)
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100
)
registration.SetOptimizerScalesFromPhysicalShift()
# Multi-resolution pyramid
registration.SetShrinkFactorsPerLevel([4, 2, 1])
registration.SetSmoothingSigmasPerLevel([2.0, 1.0, 0.0])
registration.SetSmoothingSigmasAreSpecifiedInPhysicalUnits(True)
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(initial_transform)
# Monitor progress
def iteration_callback():
print(f"Level {registration.GetCurrentLevel()}, "
f"Iteration {registration.GetOptimizerIteration()}, "
f"Metric: {registration.GetMetricValue():.4f}")
registration.AddCommand(sitk.sitkIterationEvent, iteration_callback)
final_transform = registration.Execute(fixed_image, moving_image)
return final_transformdef affine_registration(fixed_image, moving_image):
"""Affine registration (12 DOF) with initialization from rigid."""
# Start with rigid registration
rigid_transform = rigid_registration(fixed_image, moving_image)
# Convert to affine
affine_transform = sitk.AffineTransform(3)
affine_transform.SetMatrix(rigid_transform.GetMatrix())
affine_transform.SetTranslation(rigid_transform.GetTranslation())
affine_transform.SetCenter(rigid_transform.GetCenter())
# Affine registration
registration = sitk.ImageRegistrationMethod()
registration.SetMetricAsMattesMutualInformation(50)
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100
)
registration.SetOptimizerScalesFromPhysicalShift()
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(affine_transform, inPlace=True)
# Multi-resolution
registration.SetShrinkFactorsPerLevel([4, 2, 1])
registration.SetSmoothingSigmasPerLevel([2.0, 1.0, 0.0])
registration.SetSmoothingSigmasAreSpecifiedInPhysicalUnits(True)
final_transform = registration.Execute(fixed_image, moving_image)
return final_transformdef demons_registration(fixed_image, moving_image, initial_transform=None):
"""Deformable registration using demons algorithm."""
# Apply initial transform if provided
if initial_transform:
moving_image = sitk.Resample(
moving_image,
fixed_image,
initial_transform,
sitk.sitkLinear,
0.0,
moving_image.GetPixelID()
)
# Demons registration
demons = sitk.DemonsRegistrationFilter()
demons.SetNumberOfIterations(50)
demons.SetStandardDeviations(1.0)
displacement_field = demons.Execute(fixed_image, moving_image)
# Create displacement field transform
transform = sitk.DisplacementFieldTransform(3)
transform.SetDisplacementField(displacement_field)
return transformdef bspline_registration(fixed_image, moving_image, initial_transform=None):
"""B-spline deformable registration."""
# Apply initial transform if provided
if initial_transform:
moving_image = sitk.Resample(
moving_image,
fixed_image,
initial_transform,
sitk.sitkLinear,
0.0,
moving_image.GetPixelID()
)
# Create B-spline transform
transform_domain = fixed_image
bspline = sitk.BSplineTransform(transform_domain, 3)
bspline.SetTransformDomainMeshSize((8, 8, 8))
# Initialize with no deformation
params = [0.0] * bspline.GetNumberOfParameters()
bspline.SetParameters(params)
# Registration
registration = sitk.ImageRegistrationMethod()
registration.SetMetricAsMattesMutualInformation(50)
registration.SetOptimizerAsLBFGSB(
gradientConvergenceTolerance=1e-5,
numberOfIterations=100
)
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(bspline, inPlace=True)
final_transform = registration.Execute(fixed_image, moving_image)
return final_transformdef complete_registration_pipeline(fixed_path, moving_path, output_path):
"""Complete registration pipeline: rigid -> affine -> deformable."""
# Read images
fixed = sitk.ReadImage(fixed_path, sitk.sitkFloat32)
moving = sitk.ReadImage(moving_path, sitk.sitkFloat32)
print("1. Rigid registration...")
rigid_transform = rigid_registration(fixed, moving)
print("2. Affine registration...")
affine_transform = affine_registration(fixed, moving)
print("3. Deformable registration...")
deformable_transform = demons_registration(fixed, moving, affine_transform)
# Apply final transform
print("4. Applying final transform...")
registered = sitk.Resample(
moving,
fixed,
deformable_transform,
sitk.sitkLinear,
0.0,
moving.GetPixelID()
)
# Save result
sitk.WriteImage(registered, output_path)
print(f"Saved registered image to {output_path}")
return registered, deformable_transformdef register_with_monitoring(fixed_image, moving_image):
"""Registration with detailed progress monitoring."""
initial_transform = sitk.CenteredTransformInitializer(
fixed_image,
moving_image,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.GEOMETRY
)
registration = sitk.ImageRegistrationMethod()
registration.SetMetricAsMattesMutualInformation(50)
registration.SetOptimizerAsGradientDescent(
learningRate=1.0,
numberOfIterations=100
)
registration.SetInterpolator(sitk.sitkLinear)
registration.SetInitialTransform(initial_transform)
# Callbacks for monitoring
def start_callback():
print("Registration started")
def iteration_callback():
print(f"Iteration {registration.GetOptimizerIteration()}: "
f"Metric = {registration.GetMetricValue():.6f}, "
f"Position = {registration.GetOptimizerPosition()}")
def end_callback():
print(f"Registration completed: {registration.GetOptimizerStopConditionDescription()}")
registration.AddCommand(sitk.sitkStartEvent, start_callback)
registration.AddCommand(sitk.sitkIterationEvent, iteration_callback)
registration.AddCommand(sitk.sitkEndEvent, end_callback)
final_transform = registration.Execute(fixed_image, moving_image)
return final_transform# Save transform
sitk.WriteTransform(final_transform, 'transform.tfm')
# Load transform
loaded_transform = sitk.ReadTransform('transform.tfm')
# Apply loaded transform
registered = sitk.Resample(moving, fixed, loaded_transform)def assess_registration_quality(fixed, moving, transform):
"""Assess registration quality using multiple metrics."""
# Apply transform
registered = sitk.Resample(
moving,
fixed,
transform,
sitk.sitkLinear,
0.0,
moving.GetPixelID()
)
# Compute difference images
difference_before = fixed - moving
difference_after = fixed - registered
# Statistics
stats_before = sitk.StatisticsImageFilter()
stats_before.Execute(sitk.Abs(difference_before))
stats_after = sitk.StatisticsImageFilter()
stats_after.Execute(sitk.Abs(difference_after))
print(f"Mean absolute difference before: {stats_before.GetMean():.4f}")
print(f"Mean absolute difference after: {stats_after.GetMean():.4f}")
print(f"Improvement: {(1 - stats_after.GetMean()/stats_before.GetMean())*100:.2f}%")
return registeredSolution: Reduce learning rate or use multi-resolution
registration.SetOptimizerAsGradientDescent(
learningRate=0.1, # Reduced from 1.0
numberOfIterations=200
)Solution: Use sampling and multi-resolution
registration.SetMetricSamplingPercentage(0.01) # Sample 1% of pixels
registration.SetShrinkFactorsPerLevel([8, 4, 2, 1]) # More levelsSolution: Use geometric initialization
initial_transform = sitk.CenteredTransformInitializer(
fixed_image,
moving_image,
sitk.Euler3DTransform(),
sitk.CenteredTransformInitializerFilter.MOMENTS # Use moments instead of geometry
)Install with Tessl CLI
npx tessl i tessl/pypi-simpleitk