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
40 changes: 31 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ Project guidance for Claude Code in this repository.
Codex and other AI agents should use `AGENTS.md` as the primary shared
instructions file. Claude-specific behavior and slash-command usage remain here.

## Role:
We are developing open-source code for scientific AI libraries. Leverage GPU-accelerated methods when appropriate.

## Priorities (Ordered)
1) accuracy
2) clarity/maintainability/simplicity
3) consistency with the rest of the platform and open source standards
4) documentation
5) testing

## Behavior Guidelines
1) Don't assume. Don't hide confusion. Surface tradeoffs.
2) Minimum code that solves the problem. Nothing speculative.
3) Touch only what you must. Clean up only your own mess.
4) Define success criteria. Loop until verified.

## Commands

**Python launcher:** Use `py` on this Windows system (not `python`).
Expand Down Expand Up @@ -47,8 +63,6 @@ py -m pytest tests/ --create-baselines

## Architecture

Pipeline: `4D CT → Segmentation → Registration → Contour Extraction → USD Export`

All classes inherit from `PhysioMotion4DBase` (`physiomotion4d_base.py`), which provides
a shared logger. Use `self.log_info()`, `self.log_debug()` — never `print()`.

Expand All @@ -58,31 +72,38 @@ Regenerate it after any public API change: `py utils/generate_api_map.py`
**Key data conventions:**
- Images: `itk.Image`, axes X, Y, Z [, T] in LPS world space (ITK's native
frame; `itk.imread` normalizes DICOM, NIfTI, MHA, and NRRD inputs to LPS)
stored using itk.imwrite with compression=True
- 4D time series: shape `(X, Y, Z, T)` — never silently squeeze or permute axes
- Surfaces: `pv.PolyData` in LPS (inherited from the source `itk.Image` via
`itk.vtk_image_from_image`); converted to USD right-handed Y-up only at USD
export by `vtk_to_usd.lps_points_to_usd` (USD +X=Left, +Y=Superior, +Z=Anterior)
- Masks: ITK images with integer labels; consistent anatomy group IDs across all segmenters
- Transforms: ITK composite transforms stored in `.hdf` files
- Transforms: ITK composite transforms stored in `.hdf` files with compression
- State axis order and shape explicitly in every docstring and comment that touches arrays

## Testing

- Baselines in `tests/baselines/` via Git LFS — run `git lfs pull` after cloning
- `tests/conftest.py`: session-scoped fixtures chaining download → convert → segment → register
- `src/physiomotion4d/test_tools.py`: baseline comparison utilities (`TestTools`, etc.)
- Markers: `slow`, `requires_gpu`, `requires_data`, `experiment` (skipped by default)
- Prefer synthetic `itk.Image` / `pv.PolyData` over real data; keep volumes ≤64 voxels/side
- Markers: `slow`, `requires_gpu`, `requires_data`, `experiment`, `tutorial`
- Prefer images from `ROOT/data/test/slicer_heart_small` for tests
- Prefer storing results in subdirs `./results/<test_name>`

## Working Process

Before editing any code:
1. Read the relevant source file(s) in full.
2. Summarize current behavior in 2–4 sentences.
3. Propose a numbered plan; confirm before implementing non-trivial changes.
4. Implement in small, reviewable diffs.
5. Update docstrings and tests for every changed public method.
6. Call out breaking changes explicitly.
3. Identify success criteria / metrics
4. Refer to *_tools.py files for commonly used routines
5. Refer to workspace/reference_code (when available) for third-party libraries
6. Propose a numbered plan; confirm before implementing non-trivial changes.
7. Follow the behavior guidelines given above.
8. Implement in small, reviewable diffs.
9. Update docstrings and tests for every changed public method.
10. Call out breaking changes explicitly.
11. Do not commit changes or make pull requests unless specifically told to do so.

Breaking changes are acceptable. Backward-compatibility shims are not.

Expand Down Expand Up @@ -113,3 +134,4 @@ Document via docstrings and inline comments.
- `Optional[X]` not `X | None` (ruff `UP007` suppressed)
- Breaking changes are acceptable — backward compatibility is not a priority
- Max line length: 88 characters
- Follow behavior guidelines.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ a CUDA-capable GPU are required for practical runtime.
python -c "from physiomotion4d import DataDownloadTools; DataDownloadTools.DownloadSlicerHeartCTData('data/test')"

physiomotion4d-convert-image-to-usd data/test/TruncalValve_4DCT.seq.nrrd \
--registration-method ants \
--registration-method ANTS \
--output-dir output/quickstart \
--project-name slicer_heart_quickstart
```
Expand Down Expand Up @@ -286,7 +286,7 @@ processor = WorkflowConvertImageToUSD(
contrast_enhanced=True,
output_directory="./results",
project_name="cardiac_model",
registration_method='icon' # or 'ants'
registration_method='ICON' # or 'ANTS'
)

# Run complete workflow
Expand Down
77 changes: 39 additions & 38 deletions docs/API_MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,6 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
- `def group_for_id(self, label_id)` (line 132): Return the group containing *label_id*; :data:`OTHER_GROUP` if absent.
- `def fill_other_group(self, id_range=range(1, 256), name_template='other_{id}')` (line 139): Populate the ``other`` group with any ids not already claimed.

## src/physiomotion4d/cli/convert_ct_to_vtk.py

- `def main()` (line 26): CLI entry point for CT to VTK conversion.

## src/physiomotion4d/cli/convert_image_4d_to_3d.py

- `def main()` (line 20): CLI entry point for 4D-to-3D image conversion.
Expand All @@ -74,6 +70,10 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

- `def main()` (line 14): Command-line interface for the Image-to-USD workflow.

## src/physiomotion4d/cli/convert_image_to_vtk.py

- `def main()` (line 26): CLI entry point for image to VTK conversion.

## src/physiomotion4d/cli/convert_vtk_to_usd.py

- `def main()` (line 22): Command-line interface for VTK to USD conversion.
Expand Down Expand Up @@ -202,14 +202,15 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

## src/physiomotion4d/register_images_icon.py

- **class RegisterImagesICON** (line 25): ICON-based deformable image registration implementation.
- `def __init__(self, log_level=logging.INFO)` (line 61): Initialize the ICON image registration class.
- `def set_weights_path(self, weights_path)` (line 79): Set a custom weights file for the uniGradICON network.
- `def set_number_of_iterations(self, number_of_iterations)` (line 93): Set the number of iterations for ICON registration.
- `def set_multi_modality(self, enable)` (line 101): Enable or disable multi-modality registration.
- `def set_mass_preservation(self, enable)` (line 118): Enable or disable mass preservation constraint.
- `def preprocess(self, image, modality='ct')` (line 135): Preprocess the image for ICON registration.
- `def registration_method(self, moving_image, moving_mask=None, moving_labelmap=None, moving_image_pre=None, initial_forward_transform=None)` (line 155): Register moving image to fixed image using ICON registration algorithm.
- **class RegisterImagesICON** (line 32): ICON-based deformable image registration implementation.
- `def __init__(self, log_level=logging.INFO)` (line 68): Initialize the ICON image registration class.
- `def set_weights_path(self, weights_path)` (line 86): Set a custom weights file for the uniGradICON network.
- `def set_number_of_iterations(self, number_of_iterations)` (line 100): Set the number of iterations for ICON registration.
- `def set_multi_modality(self, enable)` (line 108): Enable or disable multi-modality registration.
- `def set_mass_preservation(self, enable)` (line 125): Enable or disable mass preservation constraint.
- `def preprocess(self, image, modality='ct')` (line 142): Preprocess the image for ICON registration.
- `def registration_method(self, moving_image, moving_mask=None, moving_labelmap=None, moving_image_pre=None, initial_forward_transform=None)` (line 162): Register moving image to fixed image using ICON registration algorithm.
- `def finetune(self, image_pairs, output_model_filename, mask_pairs=None, epochs=1, learning_rate=DEFAULT_FINETUNE_LEARNING_RATE)` (line 386): Fine-tune the ICON network on a cohort of image pairs.

## src/physiomotion4d/register_models_distance_maps.py

Expand Down Expand Up @@ -285,11 +286,11 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._

- **class SegmentHeartSimpleware** (line 23): Heart CT segmentation using Simpleware Medical's ASCardio module.
- `def __init__(self, log_level=logging.INFO)` (line 58): Initialize the Simpleware Medical based heart segmentation.
- `def set_trim_mask_to_essentials(self, trim_mask)` (line 118): Set whether to trim mask to common and critical structures.
- `def set_simpleware_executable_path(self, path)` (line 126): Set the path to the Simpleware Medical console executable.
- `def segmentation_method(self, preprocessed_image)` (line 139): Run Simpleware Medical ASCardio segmentation on the preprocessed image.
- `def get_landmarks(self)` (line 339): Get the landmarks.
- `def trim_mask_to_essentials(self, labelmap_image)` (line 343): Trim mask to essentials.
- `def set_trim_branches(self, trim_branches)` (line 118): Enable trimming of pulmonary and great-vessel branches.
- `def set_simpleware_executable_path(self, path)` (line 133): Set the path to the Simpleware Medical console executable.
- `def segmentation_method(self, preprocessed_image)` (line 146): Run Simpleware Medical ASCardio segmentation on the preprocessed image.
- `def get_landmarks(self)` (line 346): Get the landmarks.
- `def trim_branches(self, labelmap_image)` (line 350): Trim pulmonary and great-vessel branches back to the cardiac region.

## src/physiomotion4d/test_tools.py

Expand Down Expand Up @@ -425,22 +426,22 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
- `def read_vtk_file(filename, extract_surface=True)` (line 581): Auto-detect VTK file format and read appropriately.
- `def validate_time_series_topology(mesh_data_sequence, filenames=None)` (line 609): Validate topology consistency across a time series of meshes.

## src/physiomotion4d/workflow_convert_ct_to_vtk.py
## src/physiomotion4d/workflow_convert_image_to_usd.py

- **class WorkflowConvertImageToUSD** (line 41): Complete workflow for converting 4D CT images to dynamic USD models.
- `def __init__(self, input_filenames, contrast_enhanced, output_directory, project_name, reference_image_filename=None, number_of_registration_iterations=1, segmentation_method='ChestTotalSegmentator', registration_method='ICON', log_level=logging.INFO, save_registered_images=True, save_registration_transforms=True, save_labelmaps=True)` (line 49): Initialize the image-to-USD workflow.
- `def process(self)` (line 214): Execute the complete workflow from 4D CT to dynamic USD models.

## src/physiomotion4d/workflow_convert_image_to_vtk.py

- **class WorkflowConvertCTToVTK** (line 58): Segment a CT image and produce per-anatomy-group VTK surfaces and meshes.
- `def __init__(self, segmentation_method='total_segmentator', log_level=logging.INFO)` (line 98): Initialize the workflow.
- **class WorkflowConvertImageToVTK** (line 58): Segment a CT image and produce per-anatomy-group VTK surfaces and meshes.
- `def __init__(self, segmentation_method='ChestTotalSegmentator', log_level=logging.INFO)` (line 98): Initialize the workflow.
- `def run_workflow(self, input_image, contrast_enhanced_study=False, anatomy_groups=None)` (line 241): Segment the CT image and extract per-anatomy-group VTK objects.
- `def save_surfaces(surfaces, output_dir, prefix='')` (line 344): Save each group surface to its own VTP file.
- `def save_meshes(meshes, output_dir, prefix='')` (line 371): Save each group voxel mesh to its own VTU file.
- `def save_combined_surface(surfaces, output_dir, prefix='')` (line 397): Merge all group surfaces into a single VTP file.
- `def save_combined_mesh(meshes, output_dir, prefix='')` (line 432): Merge all group meshes into a single VTU file.

## src/physiomotion4d/workflow_convert_image_to_usd.py

- **class WorkflowConvertImageToUSD** (line 30): Complete workflow for converting 4D CT images to dynamic USD models.
- `def __init__(self, input_filenames, contrast_enhanced, output_directory, project_name, reference_image_filename=None, number_of_registration_iterations=1, registration_method='icon', log_level=logging.INFO, save_registered_images=True, save_registration_transforms=True, save_labelmaps=True)` (line 38): Initialize the image-to-USD workflow.
- `def process(self)` (line 184): Execute the complete workflow from 4D CT to dynamic USD models.

## src/physiomotion4d/workflow_convert_vtk_to_usd.py

- **class WorkflowConvertVTKToUSD** (line 23): Workflow to convert one or more VTK files to USD with configurable
Expand All @@ -458,18 +459,18 @@ _Re-run `py utils/generate_api_map.py` whenever public APIs change._
## src/physiomotion4d/workflow_fit_statistical_model_to_patient.py

- **class WorkflowFitStatisticalModelToPatient** (line 56): Register anatomical models using multi-stage ICP, mask-based, and image-based
- `def __init__(self, template_model, patient_models=None, patient_image=None, segmentation_method='simpleware_heart', log_level=logging.INFO)` (line 135): Initialize the model-to-image-and-model registration pipeline.
- `def set_mask_dilation_mm(self, mask_dilation_mm)` (line 356): Set mask dilation amount for auto-generated masks.
- `def set_roi_dilation_mm(self, roi_dilation_mm)` (line 365): Set ROI mask dilation amount.
- `def set_use_pca_registration(self, use_pca_registration, pca_model=None, pca_number_of_modes=0, pca_uses_surface=True)` (line 374): Set whether to use PCA-based registration and provide the PCA model.
- `def set_use_mask_to_mask_registration(self, use_mask_to_mask_registration)` (line 409): Set whether to use mask-to-mask registration.
- `def set_use_mask_to_image_registration(self, use_mask_to_image_registration, template_labelmap=None, template_labelmap_organ_mesh_ids=None, template_labelmap_organ_extra_ids=None, template_labelmap_background_ids=None)` (line 420): Set whether to use mask-to-image registration.
- `def register_model_to_model_icp(self)` (line 494): Perform ICP alignment of template model to patient model.
- `def register_model_to_model_pca(self)` (line 552): Perform PCA-based registration after ICP alignment.
- `def register_mask_to_mask(self, use_icon_refinement=False)` (line 678): Perform mask-based deformable registration of model to patient model.
- `def register_labelmap_to_image(self, use_icon_refinement=False)` (line 746): Perform labelmap-to-image refinement.
- `def transform_model(self, base_model=None)` (line 866): Apply registration transforms to the model.
- `def run_workflow(self, use_icon_registration_refinement=False)` (line 931): Execute the complete multi-stage registration workflow.
- `def __init__(self, template_model, patient_models=None, patient_image=None, segmentation_method='HeartSimpleware', log_level=logging.INFO)` (line 135): Initialize the model-to-image-and-model registration pipeline.
- `def set_mask_dilation_mm(self, mask_dilation_mm)` (line 361): Set mask dilation amount for auto-generated masks.
- `def set_roi_dilation_mm(self, roi_dilation_mm)` (line 370): Set ROI mask dilation amount.
- `def set_use_pca_registration(self, use_pca_registration, pca_model=None, pca_number_of_modes=0, pca_uses_surface=True)` (line 379): Set whether to use PCA-based registration and provide the PCA model.
- `def set_use_mask_to_mask_registration(self, use_mask_to_mask_registration)` (line 414): Set whether to use mask-to-mask registration.
- `def set_use_mask_to_image_registration(self, use_mask_to_image_registration, template_labelmap=None, template_labelmap_organ_mesh_ids=None, template_labelmap_organ_extra_ids=None, template_labelmap_background_ids=None)` (line 425): Set whether to use mask-to-image registration.
- `def register_model_to_model_icp(self)` (line 499): Perform ICP alignment of template model to patient model.
- `def register_model_to_model_pca(self)` (line 557): Perform PCA-based registration after ICP alignment.
- `def register_mask_to_mask(self, use_icon_refinement=False)` (line 683): Perform mask-based deformable registration of model to patient model.
- `def register_labelmap_to_image(self, use_icon_refinement=False)` (line 751): Perform labelmap-to-image refinement.
- `def transform_model(self, base_model=None)` (line 871): Apply registration transforms to the model.
- `def run_workflow(self, use_icon_registration_refinement=False)` (line 936): Execute the complete multi-stage registration workflow.

## src/physiomotion4d/workflow_reconstruct_highres_4d_ct.py

Expand Down
10 changes: 0 additions & 10 deletions docs/api/cli/convert_ct_to_vtk.rst

This file was deleted.

10 changes: 10 additions & 0 deletions docs/api/cli/convert_image_to_vtk.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
==========================
convert_image_to_vtk (CLI)
==========================

.. automodule:: physiomotion4d.cli.convert_image_to_vtk
:members:
:undoc-members:
:show-inheritance:

See :doc:`../../cli_scripts/overview` for usage.
2 changes: 1 addition & 1 deletion docs/api/cli/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Module Index.
:maxdepth: 1

convert_image_to_usd
convert_ct_to_vtk
convert_image_to_vtk
convert_vtk_to_usd
create_statistical_model
fit_statistical_model_to_patient
Expand Down
2 changes: 1 addition & 1 deletion docs/api/segmentation/totalsegmentator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Operational Notes

TotalSegmentator model inference may download model assets and can be slow on a
CPU-only environment. For repeatable workflows, prefer the tutorial scripts or
the ``physiomotion4d-convert-ct-to-vtk`` CLI.
the ``physiomotion4d-convert-image-to-vtk`` CLI.

See Also
========
Expand Down
19 changes: 10 additions & 9 deletions docs/api/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Workflow Classes
================

.. module:: physiomotion4d.workflow_convert_image_to_usd
.. module:: physiomotion4d.workflow_convert_ct_to_vtk
.. module:: physiomotion4d.workflow_convert_image_to_vtk
.. module:: physiomotion4d.workflow_convert_vtk_to_usd
.. module:: physiomotion4d.workflow_create_statistical_model
.. module:: physiomotion4d.workflow_fit_statistical_model_to_patient
Expand All @@ -26,7 +26,7 @@ Available Workflows
- Typical use
* - :class:`WorkflowConvertImageToUSD`
- Convert a 4D cardiac CT sequence into animated USD anatomy.
* - :class:`WorkflowConvertCTToVTK`
* - :class:`WorkflowConvertImageToVTK`
- Segment one CT image and export anatomy-group VTK surfaces and meshes.
* - :class:`WorkflowConvertVTKToUSD`
- Convert VTK/VTP/VTU meshes or time series into USD.
Expand Down Expand Up @@ -55,15 +55,16 @@ Convert Image to USD
contrast_enhanced=True,
output_directory="./results",
project_name="patient_001",
registration_method="ants",
segmentation_method="ChestTotalSegmentator",
registration_method="ANTS",
)

final_usd = workflow.process()

CT to VTK
=========
Image to VTK
============

.. autoclass:: WorkflowConvertCTToVTK
.. autoclass:: WorkflowConvertImageToVTK
:members:
:undoc-members:
:show-inheritance:
Expand All @@ -72,17 +73,17 @@ CT to VTK

import itk

from physiomotion4d import WorkflowConvertCTToVTK
from physiomotion4d import WorkflowConvertImageToVTK

image = itk.imread("chest_ct.nii.gz")
workflow = WorkflowConvertCTToVTK(segmentation_method="total_segmentator")
workflow = WorkflowConvertImageToVTK(segmentation_method="ChestTotalSegmentator")
result = workflow.run_workflow(
input_image=image,
contrast_enhanced_study=True,
anatomy_groups=["heart", "major_vessels"],
)

WorkflowConvertCTToVTK.save_combined_surface(
WorkflowConvertImageToVTK.save_combined_surface(
result["surfaces"],
"./output",
prefix="patient01",
Expand Down
Loading
Loading