Image Registration Developer Guide
This guide provides technical details for developers working on image registration features in MedImages.jl.
Architecture Overview
MedImages.jl implements image registration through the Resample_to_target module. The registration workflow involves:
Orientation alignment
Coordinate transformation
Interpolation at target grid points
Moving Image --> Orientation Change --> Coordinate Mapping --> Interpolation --> Registered Image
| | |
v v v
Match fixed Map moving to Sample at fixed
orientation fixed space grid pointsCore Registration Function
resample_to_image
Location: src/Resample_to_target.jl
The main registration function that resamples a moving image to match a fixed image's geometry.
function resample_to_image(im_fixed::MedImage, im_moving::MedImage,
interpolator_enum::Interpolator_enum,
value_to_extrapolate=Nothing)::MedImageImplementation Steps:
Extrapolation Value Determination: If not provided, uses median of fixed image corner values.
Orientation Alignment: Changes moving image orientation to match fixed image.
Grid Generation: Creates target grid based on fixed image dimensions.
Coordinate Transformation: Maps target grid points to moving image coordinates.
Interpolation: Samples moving image at transformed coordinates.
Coordinate Transformation Details
Grid Point Calculation
The target grid is generated from the fixed image dimensions:
new_size = size(im_fixed.voxel_data)
points_to_interpolate = get_base_indicies_arr(new_size)Origin Difference Handling
The origin difference between images is incorporated into the transformation:
origin_diff = collect(im_fixed.origin) - collect(im_moving.origin)
points_to_interpolate = points_to_interpolate .+ origin_diffSpacing Transformation
Points are scaled by the target spacing:
points_to_interpolate = points_to_interpolate .- 1
points_to_interpolate = points_to_interpolate .* new_spacing
points_to_interpolate = points_to_interpolate .+ 1Interpolation Implementation
Utils Module Functions
Location: src/Utils.jl
interpolate_my
Batch interpolation function supporting multiple points:
function interpolate_my(points_to_interpolate, input_array, input_array_spacing,
interpolator_enum, keep_begining_same,
extrapolate_value=0, use_fast=true)Parameters:
points_to_interpolate: Array of 3D coordinatesinput_array: Source voxel datainput_array_spacing: Source image spacinginterpolator_enum: Interpolation methodkeep_begining_same: Whether to preserve starting indicesextrapolate_value: Value for out-of-bounds pointsuse_fast: Use optimized interpolation path
interpolate_point
Single point interpolation:
function interpolate_point(point, itp, keep_begining_same=false, extrapolate_value=0)Interpolation Methods
Nearest Neighbor
Uses Interpolations.Constant() for integer-preserving interpolation.
Characteristics:
No new values introduced
Sharp edges preserved
Block-like artifacts at large scale factors
Best for: Segmentation masks, label maps
Linear/Trilinear
Uses Interpolations.Linear() for smooth interpolation.
Characteristics:
Weighted average of surrounding voxels
Smooth transitions
May blur sharp features
Best for: General intensity images
B-Spline
Uses Interpolations.BSpline() for high-quality interpolation.
Characteristics:
Smooth derivative continuity
Best quality for smooth images
Computationally more expensive
Best for: High-quality resampling
GPU Acceleration
Kernel-Based Resampling
Location: src/Utils.jl
GPU acceleration is implemented using KernelAbstractions.jl:
function resample_kernel_launch(image_data, old_spacing, new_spacing,
new_dims, interpolator_enum)Supported Backends:
CPU (default)
CUDA (NVIDIA GPUs)
AMD (ROCm)
oneAPI (Intel)
Kernel Implementation
Two primary kernels:
Trilinear Kernel: For
Linear_eninterpolationNearest Neighbor Kernel: For
Nearest_neighbour_eninterpolation
Orientation Change Implementation
Spatial_metadata_change Module
Location: src/Spatial_metadata_change.jl
change_orientation
function change_orientation(im::MedImage, new_orientation::Orientation_code)::MedImageImplementation:
Lookup Transformation: Uses pre-computed transformation from
orientation_pair_to_operation_dictApply Permutation: Reorders array dimensions using
permutedimsApply Reversals: Flips axes as needed using
reverseUpdate Origin: Recalculates origin based on transformation
Update Spacing: Reorders spacing to match new orientation
Pre-computed Orientation Mappings
Orientation_dicts Module
Location: src/Orientation_dicts.jl
Contains dictionaries mapping between:
- Orientation codes and strings:
orientation_enum_to_string[ORIENTATION_LPS] # "LPS"
string_to_orientation_enum["RAS"] # ORIENTATION_RAS- Orientations and direction cosines:
orientation_dict_enum_to_number[ORIENTATION_LPS] # direction tuple
number_to_enum_orientation_dict[direction_tuple] # Orientation_code- Orientation pairs and transformations:
orientation_pair_to_operation_dict[(old_orient, new_orient)]
# Returns: (permutation, reverse_axes, origin_transforms, spacing_transforms)Testing Registration
Test Files
Registration tests are located in test/resample_to_target_tests/
Test Approach
Load fixed and moving images
Apply registration using both MedImages.jl and SimpleITK
Compare spatial metadata (origin, spacing, direction)
Optionally compare voxel values (with tolerance)
Key Test Considerations
Different interpolation implementations produce different values
Metadata comparison is more reliable than voxel comparison
Use
skip_voxel_comparison=truefor algorithm-dependent operations
Performance Considerations
Memory Usage
Registration creates new arrays rather than modifying in-place:
# Each step creates a new MedImage
aligned = change_orientation(moving, fixed_orientation)
resampled = resample_to_image(fixed, aligned, interp)Optimization Tip: For very large images, consider processing in chunks.
Computational Complexity
Grid generation: O(n) where n = total voxels
Interpolation: O(n) with constant factor per voxel
Orientation change: O(n) for permutation and reversal
Parallel Processing
The interpolation step is embarrassingly parallel:
# Points can be processed independently
for point in points_to_interpolate
result[point] = interpolate(moving, point)
endExtending Registration
Adding New Interpolation Methods
Add enum value to
Interpolator_enuminMedImage_data_struct.jlAdd case handling in
interpolate_myinUtils.jlOptionally add GPU kernel in
Utils.jlAdd tests in
test/directory
Adding Transform Types
Currently supports identity transform only. To add more:
Create transform struct (e.g.,
AffineTransform)Implement
apply_transform(transform, point)functionIntegrate into
resample_to_imageworkflow
Related Modules
Basic_transformations: Rotation, translation, scalingSpatial_metadata_change: Orientation and resampling operationsUtils: Core interpolation functionsLoad_and_save: Image I/O operations
References
ITK Software Guide: itk.org
SimpleITK Documentation: simpleitk.readthedocs.io
Julia Interpolations.jl: github.com/JuliaMath/Interpolations.jl