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:
PhysioMotion4DBaseRegister 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
- 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_eigenvaluespca_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: 4post_pca_transform (
Optional[Transform]) – Optional ITK transform to apply after PCA registration. Default: Nonefixed_distance_map (
Optional[Image]) – ITK image providing the distance map. Default: Nonefixed_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 templatepca_json_filename (
str) – Path to the PCA model JSON filepca_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: 4post_pca_transform (
Optional[Transform]) – Optional ITK transform to apply after PCA registration. Default: Nonefixed_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: Nonereference_image (
Optional[Image]) – Reference image defining coordinate space. Default: Nonelog_level (
int|str) – Logging level (logging.DEBUG, logging.INFO, logging.WARNING). Default: logging.INFO
- Return type:
Self- Returns:
RegisterModelsPCA instance
- Raises:
FileNotFoundError – If JSON file not found
ValueError – If data format is invalid or required fields are missing
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(keypca_model):explained_variance_ratio,eigenvalues,components.- Parameters:
pca_template_model (
DataSet) – Mean surface mesh to use as templatepca_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: 4post_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.
- 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.
- 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.
- 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:
- 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
if the point is outside the image bounds, the point is not transformed.
if the forward point transform is set, it is applied.
if the post_pca_transform is set and enabled, it is applied.
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:
- 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:
- Return type:
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:
- Return type:
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:
- Return type:
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:
- Return type:
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:
- Return type:
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:
- Return type:
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:
- Return type:
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:
- 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:
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:
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.0method (
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