Time-Series Registration

RegisterTimeSeriesImages registers ordered 3D image phases to a reference frame using Greedy, ICON, or combined Greedy_ICON methods.

Class Reference

class physiomotion4d.RegisterTimeSeriesImages(registration_method='Greedy', log_level=20)[source]

Bases: RegisterImagesBase

Register a time series of images to a fixed image.

This class extends RegisterImagesBase to provide sequential registration of multiple images (time series) to a fixed image. It supports Greedy, ICON, and combined Greedy initialization followed by ICON refinement. It can propagate information from prior registrations to initialize subsequent ones.

The registration proceeds in two passes from a reference frame: 1. Forward pass: from reference_frame to the end of the series 2. Backward pass: from reference_frame-1 to the beginning

This bidirectional approach helps maintain temporal coherence in the registration results.

Key features: - Sequential registration of ordered image lists - Support for Greedy, ICON, and Greedy+ICON backends - Optional use of prior transforms to initialize next registration - Configurable starting point in the time series - Returns all transforms and loss values for the entire series

registration_method_name

Registration method in use (‘Greedy’, ‘ICON’, or ‘Greedy_ICON’).

Type:

str

registrar_greedy

Internal Greedy registrar.

Type:

RegisterImagesGreedy

registrar_ICON

Internal ICON registrar (also used as the refinement stage for ‘Greedy_ICON’).

Type:

RegisterImagesICON

transform_tools

Utility for transform operations.

Type:

TransformTools

Example

>>> # Register a cardiac CT time series
>>> registrar = RegisterTimeSeriesImages(registration_method='Greedy')
>>> registrar.set_modality('ct')
>>> registrar.set_fixed_image(fixed_image)
>>> registrar.set_number_of_iterations_greedy([40, 20, 10])
>>>
>>> # Register all time points to fixed image
>>> result = registrar.register_time_series(
...     moving_images=time_series_images,
...     reference_frame=5,  # Start from middle of cardiac cycle
...     register_reference=True,
...     prior_weight=0.5,
... )
>>>
>>> forward_tfms = result['forward_transforms']  # warp moving images -> fixed grid
>>> inverse_tfms = result['inverse_transforms']  # warp fixed image -> moving grids
>>> losses = result['losses']
>>>
>>> # Reconstruct time series with optional upsampling
>>> reconstructed = registrar.reconstruct_time_series(
...     moving_images=time_series_images,
...     inverse_transforms=inverse_tfms,
...     upsample_to_fixed_resolution=True,
... )
__init__(registration_method='Greedy', log_level=20)[source]

Initialize the time series image registration class.

Parameters:
  • registration_method (str) – Registration method to use. Options: ‘Greedy’, ‘ICON’, or ‘Greedy_ICON’. Default: ‘Greedy’

  • log_level (int | str) – Logging level (default: logging.INFO)

Raises:

ValueError – If registration_method is not supported.

set_number_of_iterations_ICON(number_of_iterations_ICON)[source]

Set the number of iterations for ICON registration.

Parameters:

number_of_iterations_ICON (Optional[int]) – Number of fine-tuning steps for ICON

Return type:

None

set_number_of_iterations_greedy(number_of_iterations_greedy)[source]

Set the number of iterations for Greedy registration.

Parameters:

number_of_iterations_greedy (list[int]) – List of iterations per Greedy resolution level, for example [40, 20, 10].

Return type:

None

set_smooth_prior_transform_sigma(smooth_prior_transform_sigma)[source]

Set the sigma for smoothing the prior transform.

Parameters:

smooth_prior_transform_sigma (float) – Sigma for smoothing the prior transform.

Return type:

None

set_mask_dilation(mask_dilation_mm)[source]

Set the dilation of the fixed and moving image masks.

This passes through to the underlying registration method.

Parameters:

mask_dilation_mm (float) – The dilation in millimeters.

Return type:

None

set_modality(modality)[source]

Set the imaging modality for registration optimization.

This passes through to the underlying registration method.

Parameters:

modality (str) – The imaging modality (e.g., ‘ct’, ‘mri’)

Return type:

None

set_fixed_image(fixed_image)[source]

Set the fixed image for registration.

All moving images in the time series will be registered to this fixed image.

Parameters:

fixed_image (itk.Image) – The 3D fixed image

Return type:

None

set_fixed_mask(fixed_mask)[source]

Set a binary mask for the fixed image region of interest.

This passes through to the underlying registration method.

Parameters:

fixed_mask (itk.Image) – Binary mask defining ROI

Return type:

None

set_fixed_labelmap(fixed_labelmap)[source]

Set a labelmap for the fixed image region of interest.

This passes through to the underlying registration method.

Parameters:

fixed_labelmap (Optional[itk.Image]) – Labelmap defining ROI

Return type:

None

register_time_series(moving_images, moving_masks=None, moving_labelmaps=None, reference_frame=0, register_reference=True, prior_weight=0.0)[source]

Register a time series of images to the fixed image.

This method registers an ordered sequence of images to a common fixed frame. Registration proceeds bidirectionally from a reference frame: forward to the end and backward to the beginning.

For each image after the reference image, the method can optionally use the transform from the previous image to initialize the registration, which can improve convergence and temporal coherence.

Parameters:
  • moving_images (list[itk.Image]) – List of 3D images to register

  • moving_masks (list[itk.Image], optional) – List of binary masks, one for each moving image. If None, no masks are used. If provided, must have the same length as moving_images. Default: None

  • moving_labelmaps (list[itk.Image], optional) – Per-frame multi-label segmentations, one for each moving image. If None, no labelmaps are used. If provided, must have the same length as moving_images. Default: None

  • reference_frame (int, optional) – Index of the reference image to register first. Registration proceeds forward from this index to the end, then backward from this index to the beginning. Default: 0

  • register_reference (bool, optional) – If True, register the reference image to the fixed image. If False, use identity transform for the reference image. Default: True

  • prior_weight (float, optional) – Weight (0.0 to 1.0) for using the prior image’s transform to initialize the next registration. 0.0 means no prior information is used (each registration starts from identity). Higher values provide more temporal smoothness but may propagate errors. Default: 0.0

Returns:

Dictionary containing results:
  • ”forward_transforms” (list[itk.Transform]): one per image; each warps its moving image onto the fixed grid (warping moving points/landmarks into fixed space uses the matching inverse transform instead – see docs/developer/transform_conventions)

  • ”inverse_transforms” (list[itk.Transform]): one per image; each warps the fixed image onto that moving image’s grid (used by reconstruct_time_series)

  • ”losses” (list[float]): Registration loss value for each image

Return type:

dict

Raises:
  • ValueError – If fixed_image is not set

  • ValueError – If reference_frame is out of range

  • ValueError – If prior_weight not in [0, 1]

  • ValueError – If moving_masks length doesn’t match moving_images length

Note

The method compares registration with identity initialization versus prior transform initialization and selects the result with lower loss. This helps prevent error propagation in the temporal sequence.

The fixed image mask can be set using set_fixed_mask() before calling this method.

Example

>>> registrar = RegisterTimeSeriesImages(registration_method='Greedy')
>>> registrar.set_fixed_image(fixed_image)
>>> registrar.set_fixed_mask(fixed_mask)  # Optional
>>> registrar.set_number_of_iterations_greedy([30, 15, 5])
>>>
>>> # Use new intuitive parameter names
>>> result = registrar.register_time_series(
...     moving_images=image_list,
...     moving_masks=mask_list,  # Optional
...     moving_labelmaps=labelmap_list,  # Optional
...     reference_frame=5,
...     register_reference=True,
...     prior_weight=0.5,
... )
>>>
>>> # Access results using new intuitive names
>>> for i, (forward_tfm, loss) in enumerate(
...     zip(result['forward_transforms'], result['losses'])
... ):
...     # Apply forward transform to align moving image i to fixed
...     registered = transform_tools.transform_image(
...         moving_images[i], forward_tfm, fixed_image
...     )
reconstruct_time_series(moving_images, inverse_transforms, upsample_to_fixed_resolution=False)[source]

Reconstruct time series images using inverse transforms.

This method applies the inverse transforms to reconstruct each moving image in the fixed image space. If upsample_to_fixed_resolution is enabled, the reconstructed images will use isotropic spacing (mean of fixed image’s X and Y spacing) while maintaining each moving image’s original origin and direction.

Parameters:
  • moving_images (list[itk.Image]) – List of moving images to reconstruct

  • inverse_transforms (list[itk.Transform]) – List of inverse transforms (one per moving image), each used to warp the fixed image onto that moving image’s grid

  • upsample_to_fixed_resolution (bool, optional) – If True, reconstructed images will be upsampled to isotropic resolution (mean of fixed image’s X and Y spacing) while maintaining their original origin and direction. Default: False

Returns:

List of reconstructed images in fixed image space

Return type:

list[itk.Image]

Raises:
  • ValueError – If fixed_image is not set

  • ValueError – If lengths of moving_images and inverse_transforms don’t match

Example

>>> registrar = RegisterTimeSeriesImages(registration_method='Greedy')
>>> registrar.set_fixed_image(fixed_image)
>>>
>>> result = registrar.register_time_series(
...     moving_images=time_series_images,
...     reference_frame=0,
... )
>>>
>>> reconstructed_images = registrar.reconstruct_time_series(
...     moving_images=time_series_images,
...     inverse_transforms=result['inverse_transforms'],
...     upsample_to_fixed_resolution=True,
... )

Basic Usage

import itk

from physiomotion4d import RegisterTimeSeriesImages

images = [itk.imread(f"phase_{idx:02d}.mha") for idx in range(10)]

registrar = RegisterTimeSeriesImages(registration_method="Greedy")
registrar.set_fixed_image(images[0])

result = registrar.register_time_series(
    moving_images=images,
    reference_frame=0,
    register_reference=False,
)
forward_transforms = result["forward_transforms"]
inverse_transforms = result["inverse_transforms"]

See Also