Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/cli_scripts/fit_statistical_model_to_patient.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The registration pipeline consists of four stages:

1. **ICP Alignment**: Rigid/affine alignment using surface matching
2. **PCA Registration** (optional): Statistical shape model fitting
3. **Mask-to-Mask Registration**: Deformable registration using distance maps
3. **Mask-to-Mask Registration**: Greedy affine + ICON deformable registration using distance maps
4. **Mask-to-Image Refinement** (optional): Final intensity-based refinement

Installation
Expand Down Expand Up @@ -120,7 +120,9 @@ Registration Configuration
``--template-labelmap`` and template label IDs. Disabled by default.

``--use-ICON-refinement``
Enable ICON deep learning registration refinement (default: disabled)
Enable ICON deep learning refinement in the mask-to-image stage (Stage 4).
The mask-to-mask stage always uses Greedy affine + ICON deformable.
Default: disabled

Output Options
--------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
# %% [markdown]
# # Registration-Based Correspondence
#
# This notebook aligns ICP-aligned models from step 2 to the average surface using **ANTs SyN (Symmetric Normalization)** deformable registration via mask-based registration.
# This notebook aligns ICP-aligned models from step 2 to the average surface using **Greedy affine + ICON deformable** registration via mask-based registration.
#
# **Workflow:**
# 1. Load ICP-aligned models from `kcl-heart-model/surfaces_aligned/`
# 2. Load average surface (`average_surface.vtp`)
# 3. Use `RegisterModelsDistanceMaps` to perform ANTs SyN deformable registration
# 3. Use `RegisterModelsDistanceMaps` to perform Greedy affine + ICON deformable registration
# 4. Save corresponded models to `kcl-heart-model/surfaces_aligned_corresponded/`
# 5. Visualize before/after comparisons
# 6. Analyze deformation magnitude and registration statistics
#
# **Method:**
# - **ANTs SyN** provides diffeomorphic (smooth, invertible) deformation fields
# - Progressive registration stages: rigid → affine → SyN deformable
# - **Greedy** performs fast CPU-based affine pre-alignment
# - **ICON** provides deep learning deformable registration on the affine-pre-aligned masks
# - Mask-based approach focuses registration on the anatomical structures

# %%
Expand Down Expand Up @@ -110,11 +110,9 @@
roi_dilation_mm=20.0, # Dilation for ROI mask
)

# Perform ANTs SyN deformable registration
# This performs progressive multi-stage registration: rigid → affine → SyN deformable
# Perform Greedy affine + ICON deformable registration
result = registrar.register(
transform_type="Deformable", # Uses ANTs SyN (Symmetric Normalization)
use_ICON=False, # Set to True for additional ICON deep learning refinement
transform_type="Deformable",
)

forward_transform = result["forward_transform"]
Expand Down Expand Up @@ -203,7 +201,7 @@
plotter.show_axes()
plotter.camera_position = "iso"

# Right: After distance map registration (ICP + ANTs SyN)
# Right: After distance map registration (Greedy affine + ICON deformable)
plotter.subplot(0, 1)
plotter.add_mesh(
fixed_model, color="lightblue", opacity=1.0, label="Average Surface"
Expand All @@ -212,7 +210,7 @@
after_mesh, color="green", opacity=1.0, label=f"Case {case_id} (Corresponded)"
)
plotter.add_text(
f"After Distance Map Registration (ANTs SyN)\nCase {case_id}",
f"After Distance Map Registration (Greedy + ICON)\nCase {case_id}",
position="upper_left",
font_size=10,
)
Expand Down Expand Up @@ -329,18 +327,16 @@
# %% [markdown]
# ## Summary
#
# This notebook performed mask-based deformable registration using **ANTs SyN (Symmetric Normalization)** to establish correspondence between the ICP-aligned models and the average surface.
# This notebook performed mask-based deformable registration using **Greedy affine + ICON deformable** to establish correspondence between the ICP-aligned models and the average surface.
#
# **Next Steps:**
# - Proceed to step 4: `4-surfaces_aligned_correspond_to_pca_inputs.ipynb` to prepare data for PCA analysis
# - The corresponded models in `kcl-heart-model/surfaces_aligned_corresponded/` now have improved point-to-point correspondence
# - The registration statistics show the deformation applied to each model
#
# **Registration Details:**
# - The `RegisterModelsDistanceMaps` class uses **ANTs SyN** for progressive registration:
# 1. Rigid alignment
# 2. Affine transformation
# 3. SyN deformable registration (diffeomorphic)
# - Setting `use_ICON=True` in the `register()` call would add ICON deep learning refinement after SyN
# - The `RegisterModelsDistanceMaps` class uses a two-stage pipeline:
# 1. **Greedy affine** registration (fast CPU-based alignment)
# 2. **ICON deformable** registration on the affine-pre-aligned masks (deep learning)
# - The `roi_dilation_mm` parameter controls the dilation of the ROI mask (default 20mm)
# - SyN registration provides smooth, invertible deformation fields for anatomical correspondence
# - Composed Greedy + ICON transforms provide smooth, invertible deformation fields for anatomical correspondence
Comment on lines +338 to +342
46 changes: 22 additions & 24 deletions experiments/Heart-Create_Statistical_Model/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ This experiment follows a fully automated multi-step process. Each step is a
- Prepares aligned data for correspondence computation

3. **`3-registration_based_correspondence.py`**
- Establishes point correspondences across the population using ANTs SyN deformable registration
- Establishes point correspondences across the population using Greedy affine + ICON deformable registration
- Uses mask-based distance map registration via `RegisterModelsDistanceMaps`
- Performs diffeomorphic (smooth, invertible) deformation to the average surface
- Greedy affine pre-aligns masks; ICON deep learning refines with a deformable field
- Critical step for meaningful PCA analysis

4. **`4-surfaces_aligned_correspond_to_pca_inputs.py`**
Expand All @@ -75,15 +75,15 @@ This experiment uses a fully automated approach combining:

Instead of traditional mesh parameterization methods (e.g., SPHARM-PDM), this pipeline uses **deformable image registration** to establish correspondences:

- **ANTs SyN (Symmetric Normalization)** performs diffeomorphic registration
- **Greedy affine** (PICSL Greedy) performs fast CPU-based affine pre-alignment
- **ICON deformable** applies deep learning registration on the affine-pre-aligned masks
- Distance maps from surface meshes create continuous fields for registration
- Progressive registration stages: rigid → affine → SyN deformable
- Mask-based approach focuses registration on anatomical structures

**Advantages:**
- Fully automated (no manual parameter tuning)
- Handles complex topologies naturally
- Diffeomorphic guarantees smooth, invertible deformations
- Composed Greedy + ICON transforms provide smooth, invertible deformation fields
- Integrates seamlessly with medical imaging pipelines
Comment on lines 83 to 87

### PCA Computation
Expand All @@ -108,12 +108,12 @@ cd experiments/Heart-Create_Statistical_Model/
# in VS Code or Cursor via the `# %%` cell markers):
python 1-input_meshes_to_input_surfaces.py # Extract surfaces from volumetric meshes
python 2-input_surfaces_to_surfaces_aligned.py # Rigid ICP alignment + compute average
python 3-registration_based_correspondence.py # ANTs SyN deformable correspondence
python 3-registration_based_correspondence.py # Greedy affine + ICON deformable correspondence
python 4-surfaces_aligned_correspond_to_pca_inputs.py # Prepare PCA input matrices
python 5-compute_pca_model.py # Compute PCA and export JSON model
```

**Total Runtime:** Approximately 2-4 hours depending on hardware (20 heart meshes, ANTs registration is computationally intensive).
**Total Runtime:** Approximately 1-3 hours depending on hardware (20 heart meshes; Greedy affine is fast on CPU, ICON requires a GPU for reasonable speed).

## Outputs

Expand Down Expand Up @@ -167,7 +167,7 @@ registered_mesh = workflow.run_workflow()
- VS Code or Cursor with the Python extension for cell-by-cell execution
(optional; scripts also run end-to-end as plain Python)
- ITK, VTK, PyVista (included with PhysioMotion4D)
- ANTs (Advanced Normalization Tools) - installed automatically with PhysioMotion4D
- picsl-greedy and ICON (included with PhysioMotion4D)
- scikit-learn for PCA computation

### Data
Expand All @@ -176,19 +176,19 @@ registered_mesh = workflow.run_workflow()
- ~2GB for final outputs

### Compute
- CPU: Multi-core processor (8+ cores recommended for ANTs registration)
- CPU: Multi-core processor (4+ cores recommended for Greedy affine registration)
- RAM: 16GB minimum (32GB recommended)
- GPU: Not required for this experiment
- Time: ~2-4 hours total (ANTs deformable registration is computationally intensive)
- GPU: Recommended for ICON deformable registration (CUDA-capable GPU)
- Time: ~1-3 hours total (Greedy is fast; ICON speed depends on GPU availability)

## Citation

If you use this experiment or the KCL dataset, please cite:

> Rodero et al. (2021), "Linking statistical shape models and simulated function in the healthy adult human heart". *PLOS Computational Biology*. DOI: [10.1371/journal.pcbi.1008851](https://doi.org/10.1371/journal.pcbi.1008851)

For ANTs registration:
> Avants BB, et al. (2011). "A reproducible evaluation of ANTs similarity metric performance in brain image registration". *NeuroImage*. DOI: [10.1016/j.neuroimage.2010.09.025](https://doi.org/10.1016/j.neuroimage.2010.09.025)
For ICON registration:
> Greer et al. (2021). "ICON: Learning Regular Maps Through Inverse Consistency". *ICCV*. DOI: [10.1109/ICCV48922.2021.00129](https://doi.org/10.1109/ICCV48922.2021.00129)

## Related Experiments

Expand All @@ -199,7 +199,7 @@ For ANTs registration:
## Support and Resources

- **KCL Dataset**: [https://zenodo.org/records/4590294](https://zenodo.org/records/4590294)
- **ANTs Documentation**: [https://github.com/ANTsX/ANTs](https://github.com/ANTsX/ANTs)
- **Greedy Documentation**: [https://greedy.readthedocs.io/](https://greedy.readthedocs.io/)
- **PhysioMotion4D Documentation**: See main repository README and API documentation
- **Issues**: Report bugs or request features on the PhysioMotion4D GitHub repository

Expand All @@ -210,27 +210,25 @@ For ANTs registration:
- Check `data/KCL-Heart-Model/README.md` for download instructions
- Verify all 20 heart mesh files (`.vtk` format) are present

### ANTs Registration Taking Too Long
- ANTs SyN registration is computationally intensive (5-15 minutes per subject)
- Total time for 20 subjects: 2-4 hours is normal
- Consider using a machine with more CPU cores
- Progress is saved incrementally - can resume if interrupted
### Registration Taking Too Long
- Greedy affine is fast (< 1 minute per subject on CPU)
- ICON deformable is GPU-accelerated; without a GPU it falls back to CPU and will be significantly slower
- Total time for 20 subjects: 1-3 hours depending on GPU availability

### Memory Issues
- Close other applications to free RAM
- ANTs registration can use 4-8GB per process
- ICON can use 4-8GB GPU VRAM; reduce batch size or iterations if needed
- Process fewer meshes initially to test pipeline
- Use a machine with more RAM (32GB+ recommended)

### Correspondence Quality Issues
- Check alignment quality from step 2 (ICP should produce good initial alignment)
- Verify average surface looks reasonable before step 3
- ANTs parameters are pre-tuned for cardiac anatomy
- If registration fails, check input mesh quality and topology
- If Greedy affine fails, check input mesh quality and topology
- If ICON deformable quality is poor, increase `icon_iterations` in the `register()` call

### Import Errors
- Ensure all PhysioMotion4D dependencies are installed
- Check that ANTs is available: `python -c "import ants; print(ants.__version__)"`
- Check Greedy is available: `python -c "from picsl_greedy import Greedy3D; print('ok')"`
- Reinstall environment if needed: `pip install -e .` in repository root

---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@
# Perform deformable registration
print("Starting deformable mask-to-mask registration...")

m2m_results = registrar.register_mask_to_mask(use_ICON_refinement=False)
m2m_results = registrar.register_mask_to_mask()
m2m_inverse_transform = m2m_results["inverse_transform"]
m2m_forward_transform = m2m_results["forward_transform"]
m2m_model_surface = m2m_results["registered_template_model_surface"]
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/convert_image_4d_to_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def main() -> int:
print(f"Error: input image not found: {args.input_image}")
return 1
try:
from physiomotion4d import ConvertImage4DTo3D
from .. import ConvertImage4DTo3D

converter = ConvertImage4DTo3D()
print(f"Loading 4D image: {args.input_image}")
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/convert_image_to_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def main() -> int:
# Initialize processor
print("Initializing Image-to-USD processor...")
try:
from physiomotion4d import WorkflowConvertImageToUSD
from .. import WorkflowConvertImageToUSD

processor = WorkflowConvertImageToUSD(
input_filenames=args.input_files,
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/convert_image_to_vtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def main() -> int:
print("=" * 70)

try:
from physiomotion4d import WorkflowConvertImageToVTK
from .. import WorkflowConvertImageToVTK

workflow = WorkflowConvertImageToVTK(
segmentation_method=args.segmentation_method,
Expand Down
4 changes: 2 additions & 2 deletions src/physiomotion4d/cli/convert_vtk_to_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import os
import sys

from physiomotion4d.usd_anatomy_tools import DEFAULT_RENDER_PARAMS
from ..usd_anatomy_tools import DEFAULT_RENDER_PARAMS

# Anatomy types accepted by --anatomy-type, sourced from the renderer's
# registered defaults so that new groups/organs registered in
Expand Down Expand Up @@ -193,7 +193,7 @@ def main() -> int:
return 1

try:
from physiomotion4d import WorkflowConvertVTKToUSD
from .. import WorkflowConvertVTKToUSD

workflow = WorkflowConvertVTKToUSD(
vtk_files=args.vtk_files,
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/create_statistical_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def main() -> int:
# Run workflow
print("\nInitializing create statistical model workflow...")
try:
from physiomotion4d import WorkflowCreateStatisticalModel
from .. import WorkflowCreateStatisticalModel

workflow = WorkflowCreateStatisticalModel(
sample_meshes=sample_meshes,
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/download_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pathlib import Path
from typing import Optional

from physiomotion4d.data_download_tools import DataDownloadTools
from ..data_download_tools import DataDownloadTools

SLICER_HEART_CT = "Slicer-Heart-CT"

Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/fit_statistical_model_to_patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ def main() -> int:
# Initialize workflow
print("\nInitializing heart model to patient registration workflow...")
try:
from physiomotion4d import WorkflowFitStatisticalModelToPatient
from .. import WorkflowFitStatisticalModelToPatient

workflow = WorkflowFitStatisticalModelToPatient(
template_model=template_model,
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/cli/reconstruct_highres_4d_ct.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def main() -> int:
# Initialize workflow
print("\nInitializing high-resolution 4D CT reconstruction workflow...")
try:
from physiomotion4d import WorkflowReconstructHighres4DCT
from .. import WorkflowReconstructHighres4DCT

workflow = WorkflowReconstructHighres4DCT(
time_series_images=time_series_images,
Expand Down
6 changes: 3 additions & 3 deletions src/physiomotion4d/contour_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
import pyvista as pv
import trimesh

from physiomotion4d.image_tools import ImageTools
from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from physiomotion4d.transform_tools import TransformTools
from .image_tools import ImageTools
from .physiomotion4d_base import PhysioMotion4DBase
from .transform_tools import TransformTools


class ContourTools(PhysioMotion4DBase):
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/convert_image_4d_to_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import numpy as np
import pydicom

from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from .physiomotion4d_base import PhysioMotion4DBase


class ConvertImage4DTo3D(PhysioMotion4DBase):
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/image_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import SimpleITK as sitk
from numpy.typing import NDArray

from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from .physiomotion4d_base import PhysioMotion4DBase


class ImageTools(PhysioMotion4DBase):
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/labelmap_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import itk
import numpy as np

from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from .physiomotion4d_base import PhysioMotion4DBase


class LabelmapTools(PhysioMotion4DBase):
Expand Down
2 changes: 1 addition & 1 deletion src/physiomotion4d/landmark_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import logging
from pathlib import Path

from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from .physiomotion4d_base import PhysioMotion4DBase

LandmarkDict = dict[str, tuple[float, float, float]]

Expand Down
6 changes: 3 additions & 3 deletions src/physiomotion4d/register_images_ants.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
import numpy as np
from numpy.typing import NDArray

from physiomotion4d.register_images_base import RegisterImagesBase
from physiomotion4d.transform_tools import TransformTools
from .register_images_base import RegisterImagesBase
from .transform_tools import TransformTools


class RegisterImagesANTS(RegisterImagesBase):
Expand Down Expand Up @@ -296,7 +296,7 @@ def _antsfile_to_itk_displacement_field_transform(

# Convert to the correct Image[Vector[D, 3], 3] type for DisplacementFieldTransform
# Use ImageTools helper to convert array to vector image with correct type
from physiomotion4d.image_tools import ImageTools
from .image_tools import ImageTools

image_tools = ImageTools()

Expand Down
6 changes: 3 additions & 3 deletions src/physiomotion4d/register_images_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@

import itk

from physiomotion4d.labelmap_tools import LabelmapTools
from physiomotion4d.physiomotion4d_base import PhysioMotion4DBase
from physiomotion4d.transform_tools import TransformTools
from .labelmap_tools import LabelmapTools
from .physiomotion4d_base import PhysioMotion4DBase
from .transform_tools import TransformTools


class RegisterImagesBase(PhysioMotion4DBase):
Expand Down
Loading
Loading