PCA-Based Registration

Shape-based registration using principal component analysis.

Class Reference

class physiomotion4d.RegisterModelsPCA(pca_template_model, pca_eigenvectors, pca_std_deviations, pca_number_of_modes=0, pca_template_model_point_subsample=4, post_pca_transform=None, fixed_distance_map=None, fixed_model=None, reference_image=None, log_level=20)[source]

Bases: PhysioMotion4DBase

Register PCA-based shape models to medical images using mean distance optimization.

This class implements a registration pipeline for fitting statistical shape models to patient-specific medical images:

PCA Deformable Registration
  • Optimizes PCA coefficients

  • Model equation: P = mean + Σ(b_i * std_i * pca_eigenvector_i)

  • Maximizes mean distance at deformed model points P

Optimization Objective:

Maximize the mean distance of the image sampled at model points using ITK’s LinearInterpolateImageFunction. This aligns the model with bright regions in contrast-enhanced images (e.g., blood pool in cardiac CT).

pca_template_model

Mean shape model

Type:

pv.DataSet

pca_eigenvectors

PCA eigenvectors/components (modes × n_points*3)

Type:

np.ndarray

pca_std_deviations

Standard deviations per mode (modes,)

Type:

np.ndarray

fixed_distance_map

Patient image providing distance data

Type:

itk.Image

n_points

Number of points in the model

Type:

int

pca_number_of_modes

Number of PCA modes available

Type:

int

pca_coefficients

Optimized PCA coefficients

Type:

np.ndarray

registered_model

Final registered and deformed model

Type:

pv.DataSet

post_pca_transform

Transform to apply after PCA registration

Type:

itk.Transform

forward_point_transform

Forward displacement field transform (Does not include the post-PCA transform)

Type:

itk.DisplacementFieldTransform

inverse_point_transform

Inverse displacement field transform (Does not include the post-PCA transform)

Type:

itk.DisplacementFieldTransform

Example

>>> # Load PCA model data
>>> pca_template_model = pv.read('pca_All_mean.vtk')
>>> with open('pca.json', 'r') as f:
...     pca_data = json.load(f)
>>> pca_group_data = pca_data['All']
>>> pca_std_deviations = np.sqrt(np.array(pca_group_data['eigenvalues']))
>>> pca_eigenvectors = np.array(pca_group_data['components'])
>>>
>>> # Initialize registrar with loaded data
>>> registrar = RegisterModelsPCA(
...     pca_template_model=pca_template_model,
...     pca_eigenvectors=pca_eigenvectors,
...     pca_std_deviations=pca_std_deviations,
... )
>>>
>>> # Run full registration pipeline
>>> result = registrar.register(pca_number_of_modes=10)
>>>
>>> # Save registered model
>>> result['registered_model'].save('registered_heart.vtk')
>>>
>>> # Print optimization results
>>> print(f'Final mean distance: {result["mean_distance"]:.2f}')
>>> print(f'PCA coefficients: {result["pca_coefficients"]}')
__init__(pca_template_model, pca_eigenvectors, pca_std_deviations, pca_number_of_modes=0, pca_template_model_point_subsample=4, post_pca_transform=None, fixed_distance_map=None, fixed_model=None, reference_image=None, log_level=20)[source]

Initialize the PCA-based model-to-image registration.

Parameters:
  • pca_template_model (DataSet) – PyVista model containing the mean 3D shape model (unstructured grid or polydata)

  • pca_eigenvectors (ndarray) – Numpy array of PCA eigenvectors/components. Shape: (modes, n_points*3) Each row is a flattened eigenmode with 3D displacements: [x1,y1,z1, x2,y2,z2, …]

  • pca_std_deviations (ndarray) – Numpy array of standard deviations per PCA mode. Shape: (modes,) These are the square roots of pca_eigenvalues

  • pca_number_of_modes (int) – Number of PCA modes to use. Default: -1 (use all)

  • pca_template_model_point_subsample (int) – Step size for subsampling model points. Default: 4

  • post_pca_transform (Optional[Transform]) – Optional ITK transform to apply after PCA registration. Default: None

  • fixed_distance_map (Optional[Image]) – ITK image providing the distance map. Default: None

  • fixed_model (Optional[DataSet]) – PyVista model used to compute the distance map, if one isn’t provided.

  • reference_image (Optional[Image]) – ITK image providing coordinate frame for computing the distance map.

  • log_level (int | str) – Logging level (logging.DEBUG, logging.INFO, logging.WARNING). Default: logging.INFO

Raises:

ValueError – If pca_eigenvector dimensions don’t match model points

classmethod from_json(pca_template_model, pca_json_filename, pca_number_of_modes=0, pca_template_model_point_subsample=4, post_pca_transform=None, fixed_distance_map=None, fixed_model=None, reference_image=None, log_level=20)[source]

Create RegisterModelsPCA from PCA model JSON file.

This method reads PCA statistical shape model data from a JSON file containing eigenvalues and principal component vectors.

The JSON file must contain: - ‘eigenvalues’: Array of eigenvalues (variance) for each component - ‘components’: Array of principal component vectors (flattened shape deformations)

Parameters:
  • pca_template_model (DataSet) – Mean surface mesh to use as template

  • pca_json_filename (str) – Path to the PCA model JSON file

  • pca_number_of_modes (int) – Number of PCA modes to use. Default: 0 (use all)

  • pca_template_model_point_subsample (int) – Step size for subsampling model points. Default: 4

  • post_pca_transform (Optional[Transform]) – Optional ITK transform to apply after PCA registration. Default: None

  • fixed_distance_map (Optional[Image]) – ITK image providing the distance values for registration. If None, must be set later before registration.

  • fixed_model (Optional[DataSet]) – Target surface mesh to register to. Default: None

  • reference_image (Optional[Image]) – Reference image defining coordinate space. Default: None

  • log_level (int | str) – Logging level (logging.DEBUG, logging.INFO, logging.WARNING). Default: logging.INFO

Return type:

Self

Returns:

RegisterModelsPCA instance

Raises:

Example

>>> registrar = RegisterModelsPCA.from_json(
...     pca_template_model=pca_template_model,
...     pca_json_filename='path/to/pca_model.json',
...     fixed_model=fixed_model,
...     reference_image=reference_image,
... )
classmethod from_pca_model(pca_template_model, pca_model, pca_number_of_modes=0, pca_template_model_point_subsample=4, post_pca_transform=None, fixed_distance_map=None, fixed_model=None, reference_image=None, log_level=20)[source]

Create RegisterModelsPCA from a PCA model dictionary.

The dict must match the structure produced by WorkflowCreateStatisticalModel (key pca_model): explained_variance_ratio, eigenvalues, components.

Parameters:
  • pca_template_model (DataSet) – Mean surface mesh to use as template

  • pca_model (dict) – PCA model dict with ‘eigenvalues’ and ‘components’ (and optionally ‘explained_variance_ratio’)

  • pca_number_of_modes (int) – Number of PCA modes to use. Default: 0 (use all)

  • pca_template_model_point_subsample (int) – Step size for subsampling model points. Default: 4

  • post_pca_transform (Optional[Transform]) – Optional ITK transform to apply after PCA registration.

  • fixed_distance_map (Optional[Image]) – ITK image providing the distance values for registration.

  • fixed_model (Optional[DataSet]) – Target surface mesh to register to.

  • reference_image (Optional[Image]) – Reference image defining coordinate space.

  • log_level (int | str) – Logging level.

Return type:

Self

Returns:

RegisterModelsPCA instance

Raises:

ValueError – If required keys are missing or dimensions invalid

set_fixed_model(fixed_model, reference_image)[source]

Set the fixed model for registration.

If this is set, the fixed distance map will be set to None.

Parameters:
  • fixed_model (UnstructuredGrid) – PyVista model used to compute the distance map, if one isn’t provided.

  • reference_image (Optional[Image]) – ITK image providing coordinate frame for computing the distance map.

Return type:

None

set_fixed_distance_map(fixed_distance_map)[source]

Set the reference image for registration.

If this is set, the fixed model will be set to None.

Parameters:

fixed_distance_map (Optional[Image]) – ITK image providing distance data

Return type:

None

set_pca_template_model(pca_template_model)[source]

Set the average model for registration.

Parameters:

pca_template_model (UnstructuredGrid) – PyVista model containing the mean 3D shape model (unstructured grid or polydata)

Return type:

None

transform_template_model()[source]

Create the final registered model by applying PCA deformation.

Return type:

DataSet

Returns:

Final registered and deformed model as a PyVista dataset.

Raises:

ValueError – If registration has not been performed

transform_point(point, include_post_pca_transform=True)[source]

Transform an arbitrary point using nearest neighbor interpolation.

Parameters:

point (Point) – ITK point to transform (itk.Point[itk.D, 3])

Return type:

Point

Returns:

Transformed ITK point

Notes

  1. if the point is outside the image bounds, the point is not transformed.

  2. if the forward point transform is set, it is applied.

  3. if the post_pca_transform is set and enabled, it is applied.

  4. if the forward point transform is not set, no errors are raised.

Example

>>> p = itk.Point[itk.D, 3]()
>>> p[0], p[1], p[2] = 10.0, 20.0, 30.0
>>> transformed_p = registrar.transform_point(p)
compute_pca_transforms(reference_image)[source]

Compute PCA transforms.

Returns:

  • ‘forward_point_transform’: Forward displacement field transform

  • ’inverse_point_transform’: Inverse displacement field transform

Return type:

Dictionary containing

classmethod get_log_classes()

Get the list of classes currently showing logs.

Return type:

list[str]

Returns:

List of class names that are allowed to show logs. Empty list if filter is disabled (all classes shown).

Example

>>> classes = PhysioMotion4DBase.get_log_classes()
>>> print(classes)
['RegisterModelsPCA', 'WorkflowFitStatisticalModelToPatient']
log_critical(message, *args)

Log a critical message with optional %-style formatting.

Parameters:
  • message (str) – The critical message to log (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting

Return type:

None

Example

>>> self.log_critical('System failure at %s', timestamp)
>>> self.log_critical('Critical error: %(msg)s', {'msg': 'Out of memory'})
log_debug(message, *args)

Log a debug message with optional %-style formatting.

Parameters:
  • message (str) – The debug message to log (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting

Return type:

None

Example

>>> self.log_debug('Processing %s with %d items', filename, count)
>>> self.log_debug('Value is %(value)d', {'value': 42})
log_error(message, *args)

Log an error message with optional %-style formatting.

Parameters:
  • message (str) – The error message to log (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting

Return type:

None

Example

>>> self.log_error('Failed to load %s: %s', filename, error_msg)
>>> self.log_error('Error code: %(code)d', {'code': 404})
log_info(message, *args)

Log an info message with optional %-style formatting.

Parameters:
  • message (str) – The info message to log (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting

Return type:

None

Example

>>> self.log_info('Loading file: %s', filepath)
>>> self.log_info('Iteration %(iter)d of %(total)d', {'iter': 5, 'total': 10})
log_progress(current, total, prefix='Progress')

Log progress information.

Parameters:
  • current (int) – Current step/iteration number

  • total (int) – Total number of steps/iterations

  • prefix (str) – Prefix text for the progress message. Default: ‘Progress’

Return type:

None

Example

>>> for i in range(100):
...     self.log_progress(i + 1, 100)
>>> self.log_progress(5, 10, prefix='Processing')

Note

For custom formatted progress messages, use log_info() directly: >>> self.log_info(‘Loading %s: %d/%d’, filename, current, total)

log_section(title, *args, width=70, char='=')

Log a formatted section header with optional %-style formatting.

Useful for visually separating major sections of output.

Parameters:
  • title (str) – The section title (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting of title

  • width (int) – Total width of the header line. Default: 70

  • char (str) – Character to use for the header line. Default: ‘=’

Return type:

None

Example

>>> self.log_section('Stage 1: Initialization')
>>> self.log_section('Processing file: %s', filename)
>>> self.log_section('Stage %(num)d: %(name)s', {'num': 2, 'name': 'Analysis'})
# Outputs:
# ======================================================================
# Stage 2: Analysis
# ======================================================================
log_warning(message, *args)

Log a warning message with optional %-style formatting.

Parameters:
  • message (str) – The warning message to log (can contain %-style placeholders)

  • *args (Any) – Arguments for %-style string formatting

Return type:

None

Example

>>> self.log_warning('Memory usage at %d%%', usage_percent)
>>> self.log_warning('Parameter %(name)s out of range', {'name': 'threshold'})
classmethod set_log_all_classes()

Enable logging output from all PhysioMotion4D classes.

Disables the class filter so all classes show their logs.

Example

>>> PhysioMotion4DBase.set_log_all_classes()
>>> # Now all classes will show their logs
Return type:

None

classmethod set_log_classes(class_names)

Set which classes should show their logging output.

Only log messages from the specified classes will be displayed. All other classes will have their logs hidden.

Parameters:

class_names (list[str]) – List of class names to show logs from. Example: [“RegisterModelsPCA”, “WorkflowFitStatisticalModelToPatient”]

Return type:

None

Example

>>> PhysioMotion4DBase.set_log_classes(['RegisterModelsPCA'])
>>> # Now only RegisterModelsPCA logs will be shown
classmethod set_log_level(log_level)

Set the logging level for all PhysioMotion4D classes.

Parameters:

log_level (int | str) – Logging level. Can be an integer (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL) or a string (‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’).

Return type:

None

Example

>>> import logging
>>> PhysioMotion4DBase.set_log_level(logging.DEBUG)
>>> # or
>>> PhysioMotion4DBase.set_log_level('DEBUG')
register(pca_number_of_modes=0, pca_coefficient_bounds=3.5, method='L-BFGS-B', max_iterations=100)[source]

Optimize PCA coefficients to deform the model to better match low values in the distance map.

Parameters:
  • pca_number_of_modes (int) – Number of PCA modes to use. Default: 0 (use all available modes)

  • pca_coefficient_bounds (float) – PCA coefficient bounds (±std devs). Default: 3.0

  • method (str) – Optimization method for scipy.optimize.minimize. Default: ‘L-BFGS-B’ (supports bounds)

  • max_iterations (int) – Maximum number of optimization iterations. Default: 50

Returns:

  • ‘registered_model’: Final registered PyVista model

  • ’pca_coefficients’: Optimized PCA coefficients

  • ’mean_distance’: Final mean distance metric value

Return type:

Dictionary containing

Raises:

ValueError – If reference image is not set

Example

>>> result = registrar.register(pca_number_of_modes=10)
>>> result['registered_model'].save('registered_heart.vtk')

Navigation

Distance Map Registration | Model Registration Modules | USD Generation Modules