Time-Series Registration

RegisterTimeSeriesImages registers ordered 3D image phases to a reference frame using a caller-supplied RegisterImagesBase backend (e.g. RegisterImagesGreedy, RegisterImagesICON, or RegisterImagesGreedyICON for combined Greedy-then-ICON refinement).

Class Reference

class physiotwin4d.RegisterTimeSeriesImages(registration_method=None, log_level=20)[source]

Bases: RegisterImagesBase

Register a time series of images to a fixed image.

This class extends RegisterImagesBase to provide registration of multiple images (time series) to a fixed image, using a caller-supplied registration backend. Every frame is registered to the fixed image independently.

Key features:

  • Sequential registration of ordered image lists

  • Supports any RegisterImagesBase backend, including RegisterImagesChain / RegisterImagesGreedyICON for multi-stage registration

  • Configurable starting point in the time series

  • Returns all transforms and loss values for the entire series

registrar

The registration backend in use.

Type:

RegisterImagesBase

transform_tools

Utility for transform operations.

Type:

TransformTools

Example

>>> # Register a cardiac CT time series
>>> registrar = RegisterTimeSeriesImages()
>>> registrar.set_modality('ct')
>>> registrar.set_fixed_image(fixed_image)
>>>
>>> # 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,
... )
>>>
>>> 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=None, log_level=20)[source]

Initialize the time series image registration class.

Parameters:
  • registration_method (Optional[RegisterImagesBase]) – Registration backend instance to use. Defaults to a new RegisterImagesGreedy when None.

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

Raises:

TypeError – If registration_method is neither None nor a RegisterImagesBase instance.

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)[source]

Register a time series of images to the fixed image.

This method registers an ordered sequence of images to a common fixed frame. The reference frame is registered first, then every other frame, each independently of the others.

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, which is registered first. 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

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 moving_masks length doesn’t match moving_images length

Note

Every frame is registered independently, so an error in one frame cannot propagate along the series.

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

Example

>>> greedy = RegisterImagesGreedy()
>>> registrar = RegisterTimeSeriesImages(registration_method=greedy)
>>> registrar.set_fixed_image(fixed_image)
>>> registrar.set_fixed_mask(fixed_mask)  # Optional
>>>
>>> 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,
... )
>>>
>>> # 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

>>> greedy = RegisterImagesGreedy()
>>> 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 physiotwin4d import RegisterImagesGreedy, RegisterTimeSeriesImages

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

registrar = RegisterTimeSeriesImages(registration_method=RegisterImagesGreedy())
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