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
1 change: 0 additions & 1 deletion .github/scripts/build_dashboard.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python3
"""
Build the nightly health dashboard for PhysioTwin4D.

Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ for users and contributors. Key sections:

- **Personalized digital twins**: build subject-specific anatomic models and physiological AI surrogates from 3D/4D medical images
- **Statistical shape models**: capture subject-specific anatomy and establish cross-subject correspondence, aiding AI surrogate generalization and simplifying traditional solver setup
- **Simplified workflows on industry-leading open-source tools**: ICON and Greedy for registration; MONAI with TotalSegmentator and Simpleware for segmentation; Netgen for meshing (LGPL license); scikit-learn for statistical shape modeling; ITK for image processing; PyVista and OpenUSD/Omniverse for geometry manipulation; CuPy for accelerated computing; and PhysicsNeMo for AI surrogates
- **Simplified workflows on industry-leading open-source tools**: ICON and Greedy for registration; MONAI with TotalSegmentator and Simpleware for segmentation; scikit-learn for statistical shape modeling; ITK for image processing; PyVista and OpenUSD/Omniverse for geometry manipulation; CuPy for accelerated computing; and PhysicsNeMo for AI surrogates
- **Extensible class hierarchy**: add new segmentation and registration methods, and extend to new data types, organs, and physiological processes, without reworking the workflow layer
- **Physiological motion**: cardiac and respiratory motion today, expanding to electrophysiology, blood flow, and organ perfusion
- **NVIDIA Omniverse as the simulation hub**: the end goal for simulation — a simulation-information hub and gateway to other engines (e.g., Ansys solvers), interactive simulations for treatment planning (e.g., Isaac Sim, Newton), visualization systems (e.g., AR/VR devices), and physical systems (e.g., robots via ROS)
Expand Down Expand Up @@ -71,9 +71,8 @@ See the [contributing guide](https://project-monai.github.io/physiotwin4d/contri

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.

Note that some optional dependencies carry their own license terms: meshing
via Netgen is LGPL-licensed, and NVIDIA Omniverse is distributed under its
own customer license, which is free for academic and commercial use. To our
knowledge, none of the dependencies carry commercially prohibitive licenses
such as GPL, but no guarantees are provided — review the license of each
dependency for your own use case.
Note that some optional dependencies carry their own license terms: NVIDIA
Omniverse is distributed under its own customer license, which is free for
academic and commercial use. To our knowledge, none of the dependencies
carry commercially prohibitive licenses such as GPL, but no guarantees are
provided — review the license of each dependency for your own use case.
1 change: 0 additions & 1 deletion data/DirLab-4DCT/Convert4DCTToMHD.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
18 changes: 12 additions & 6 deletions data/Slicer-Heart-CT/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ assert DataDownloadTools.VerifySlicerHeartCTData("data/Slicer-Heart-CT")
```

This fetches a single ~1.2 GB file from
[github.com/SlicerHeart/SlicerHeart](https://github.com/SlicerHeart/SlicerHeart/releases/download/TestingData/TruncalValve_4DCT.seq.nrrd).
An existing non-empty file is reused, so re-running the command resumes an
interrupted download.
[github.com/SlicerHeart/SlicerHeart](https://github.com/SlicerHeart/SlicerHeart/releases/download/TestingData/TruncalValve_4DCT.seq.nrrd),
then splits it into per-phase 3D `slice_???.mha` volumes in the same
directory via `ConvertImage4DTo3D`. An existing non-empty `.seq.nrrd` is
reused, and the split is skipped once the `slice_???.mha` files are present —
so re-running the command resumes an interrupted download or conversion.

**Directory structure after download:**
```
```text
data/Slicer-Heart-CT/
├── TruncalValve_4DCT.seq.nrrd
├── slice_000.mha ... slice_020.mha
└── README.md (this file)
```
Comment thread
aylward marked this conversation as resolved.

Expand Down Expand Up @@ -54,11 +57,14 @@ Philadelphia):
- Primary dataset for tutorials
- Primary dataset for `experiments/Heart-GatedCT_To_USD/` and
`experiments/Heart-VTKSeries_To_USD/`, whose
`0-download_and_convert_4d_to_3d.py` scripts download this sequence and
split it into per-phase 3D `.mha` slices via `ConvertImage4DTo3D`
`0-download_and_convert_4d_to_3d.py` scripts call
`DataDownloadTools.DownloadSlicerHeartCTData`, which downloads this
sequence and splits it into per-phase 3D `.mha` slices
- Used in the test suite (`tests/test_download_heart_data.py`)
- Example data for cardiac motion visualization in NVIDIA Omniverse

### Files in This Directory

- `TruncalValve_4DCT.seq.nrrd` — the downloaded 4D CT sequence
- `slice_000.mha` ... `slice_020.mha` — the 21 cardiac phases split out of
the sequence above
2 changes: 1 addition & 1 deletion docs/api/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Available Workflows
* - :class:`WorkflowConvertImageToUSD`
- Convert a 4D cardiac CT sequence into animated USD anatomy.
* - :class:`WorkflowConvertImageToVTK`
- Segment one CT image and export anatomy-group VTK surfaces and meshes.
- Segment one CT image and export anatomy-group VTK surfaces.
* - :class:`WorkflowConvertVTKToUSD`
- Convert VTK/VTP/VTU meshes or time series into USD.
* - :class:`WorkflowCreateStatisticalModel`
Expand Down
3 changes: 1 addition & 2 deletions docs/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ Primary Workflows
contours and painted animated USD files.

``WorkflowConvertImageToVTK``
Segments a 3D CT image and exports anatomy groups as VTK surfaces and voxel
meshes.
Segments a 3D CT image and exports anatomy groups as VTK surfaces.

``WorkflowCreateStatisticalModel``
Aligns a population of meshes to a reference and builds a PCA statistical
Expand Down
7 changes: 5 additions & 2 deletions docs/cli_scripts/download_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,18 @@ Options
Output
======

For ``Slicer-Heart-CT``, the command downloads or reuses:
For ``Slicer-Heart-CT``, the command downloads the 4-D sequence and splits it
into per-phase 3-D volumes:

.. code-block:: text

data/Slicer-Heart-CT/TruncalValve_4DCT.seq.nrrd
data/Slicer-Heart-CT/slice_000.mha ... slice_020.mha

The command uses
:meth:`physiotwin4d.data_download_tools.DataDownloadTools.DownloadSlicerHeartCTData`,
so repeated runs reuse the existing non-empty file.
so repeated runs reuse the existing non-empty file and skip the split once
the ``slice_???.mha`` files are present.

For ``KCL-Heart-Model``, the command downloads, extracts, and reuses:

Expand Down
2 changes: 1 addition & 1 deletion docs/cli_scripts/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Current Scripts
* - :doc:`heart_gated_ct`
- Process cardiac gated CT to animated heart models with physiological motion
* - ``physiotwin4d-convert-image-to-vtk``
- Segment one 3D image and export anatomy-group VTK surfaces and meshes
- Segment one 3D image and export anatomy-group VTK surfaces
* - ``physiotwin4d-convert-image-4d-to-3d``
- Split a 4D medical image into a 3D time series using ITK readers
* - :doc:`create_statistical_model`
Expand Down
2 changes: 1 addition & 1 deletion docs/developer/architecture.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Workflow Classes
transformation, and animated USD generation.

``WorkflowConvertImageToVTK``
Converts one 3D image into labeled VTK surface and voxel-mesh outputs.
Converts one 3D image into labeled VTK surface outputs.

``WorkflowCreateStatisticalModel``
Builds a PCA statistical shape model from aligned population meshes.
Expand Down
10 changes: 8 additions & 2 deletions docs/tutorials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Preview

Inner API usage
The workflow owns a segmentation method and turns each anatomy group into
decimated VTK surfaces and volume meshes:
a decimated VTK surface:

.. code-block:: python

Expand All @@ -213,11 +213,17 @@ Inner API usage
result = workflow.process(
input_image=ct_image,
surface_target_reduction=0.5,
mesh_target_reduction=0.7,
)

Use ``SegmentChestTotalSegmentator`` instead for non-contrast studies.

By default the script saves one combined ``patient_surfaces.vtp``. Set
``SAVE_GROUP_SURFACES = True`` and/or ``SAVE_LABEL_SURFACES = True`` near
the top of the script to additionally save one VTP per anatomy group
(e.g. ``patient_heart.vtp``) and/or one VTP per individual anatomical
structure (e.g. ``patient_left_ventricle.vtp``); the latter passes
``extract_label_surfaces=True`` to :meth:`WorkflowConvertImageToVTK.process`.

Run
.. code-block:: bash

Expand Down
1 change: 0 additions & 1 deletion experiments/Colormap-VTK_To_USD/colormap_vtk_to_usd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Colormap Features for VTK to USD Conversion
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Cardiac Valve 4D Time-Series Conversion to USD
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
from pathlib import Path

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Cardiac Valve 4D Time-Series Conversion to USD
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # VTK to USD Converter Test Notebook
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Displacement Field to USD for Omniverse Visualization
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Convert VTK to VTP Surface Files
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # ICP Affine Registration: Align Heart Models to Average
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Registration-Based Correspondence
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
from pathlib import Path

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Compute PCA Statistical Model
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#!/usr/bin/env python
# %%
import shutil
from pathlib import Path

from physiotwin4d.convert_image_4d_to_3d import ConvertImage4DTo3D
from physiotwin4d.data_download_tools import DataDownloadTools

_HERE = Path(__file__).resolve().parent
Expand All @@ -12,15 +10,11 @@
data_dir = _HERE.parent.parent / "data" / "Slicer-Heart-CT"
output_dir = _HERE / "results"

data_dir.mkdir(parents=True, exist_ok=True)
output_dir.mkdir(parents=True, exist_ok=True)

input_image_filename = DataDownloadTools.DownloadSlicerHeartCTData(data_dir)
# Downloads TruncalValve_4DCT.seq.nrrd and splits it into slice_???.mha.
DataDownloadTools.DownloadSlicerHeartCTData(data_dir)

# %%
conv = ConvertImage4DTo3D()
conv.load_image_4d(str(input_image_filename))
conv.save_3d_images(data_dir, "slice")

# Save the mid-stroke slice as the fixed/reference image
shutil.copyfile(data_dir / "slice_007.mha", output_dir / "slice_fixed.mha")
1 change: 0 additions & 1 deletion experiments/Heart-GatedCT_To_USD/1-register_images.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
from pathlib import Path

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # Heart Segmentation using Simpleware Medical ASCardio
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # ICP via ITK Heart Model to Image Registration Experiment
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # PCA-based Heart Model to Image Registration Experiment
#
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# ## Setup and Imports

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# ## Setup and Imports

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#!/usr/bin/env python
# %%
import os

from physiotwin4d.convert_image_4d_to_3d import ConvertImage4DTo3D
from physiotwin4d.data_download_tools import DataDownloadTools

_HERE = os.path.dirname(os.path.abspath(__file__))
Expand All @@ -11,16 +9,8 @@
data_dir = os.path.join(_HERE, "..", "..", "data", "Slicer-Heart-CT")
output_dir = os.path.join(_HERE, "results")

if not os.path.exists(data_dir):
os.makedirs(data_dir)

if not os.path.exists(output_dir):
os.makedirs(output_dir)

input_image_filename = DataDownloadTools.DownloadSlicerHeartCTData(data_dir)

# %%
if not os.path.exists(f"{data_dir}/slice_000.mha"):
conv = ConvertImage4DTo3D()
conv.load_image_4d(str(input_image_filename))
conv.save_3d_images(data_dir, "slice")
# Downloads TruncalValve_4DCT.seq.nrrd and splits it into slice_???.mha.
DataDownloadTools.DownloadSlicerHeartCTData(data_dir)
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import glob
import os
Expand Down
1 change: 0 additions & 1 deletion experiments/Lung-GatedCT_To_USD/0-register_dirlab_4dct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os
from typing import Optional
Expand Down
1 change: 0 additions & 1 deletion experiments/Lung-GatedCT_To_USD/1-make_dirlab_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import itk
import numpy as np
Expand Down
1 change: 0 additions & 1 deletion experiments/Lung-GatedCT_To_USD/2-paint_dirlab_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
from data_dirlab_4d_ct import DataDirLab4DCT
from pxr import Usd
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
1 change: 0 additions & 1 deletion experiments/Lung-GatedCT_To_USD/Experiment_SegReg.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
1 change: 0 additions & 1 deletion experiments/Lung-VesselsAirways/0-GenData.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
from datetime import datetime

Expand Down
1 change: 0 additions & 1 deletion experiments/Reconstruct4DCT/reconstruct_4d_ct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %%
import os

Expand Down
1 change: 0 additions & 1 deletion experiments/Reconstruct4DCT/reconstruct_4d_ct_class.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# %% [markdown]
# # 4D CT Reconstruction Using RegisterTimeSeriesImages Class
#
Expand Down
3 changes: 0 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ dependencies = [
"pyvista[all]>=0.47.0",
"usd-core>=23.11",
"trimesh>=4.0.0",
"netgen-mesher>=6.2.2606",

# Utilities
"ipykernel>=6.0.0",
Expand Down Expand Up @@ -229,8 +228,6 @@ module = [
"itk.*",
"matplotlib",
"matplotlib.*",
"netgen",
"netgen.*",
"nibabel",
"nibabel.*",
"nrrd",
Expand Down
1 change: 0 additions & 1 deletion src/physiotwin4d/cli/convert_image_4d_to_3d.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
"""Command-line interface for splitting a 3D/4D image into a 3D time series.

Reads a 3D or 4D medical image and writes one ``.mha`` file per temporal frame
Expand Down
1 change: 0 additions & 1 deletion src/physiotwin4d/cli/convert_image_to_usd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env python
"""
Command-line interface for the Image-to-USD workflow.

Expand Down
Loading
Loading