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:
RegisterImagesBaseRegister 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:
- transform_tools
Utility for transform operations.
- Type:
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.
- set_modality(modality)[source]
Set the imaging modality for registration optimization.
This passes through to the underlying registration method.
- 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:
- 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:
- 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:
- 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:
- 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"]