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
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ from physiomotion4d import RegisterImagesICON, WorkflowConvertImageToUSD
# Initialize processor
processor = WorkflowConvertImageToUSD(
input_filenames=["path/to/cardiac_4d_ct.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="cardiac_model",
registration_method=RegisterImagesICON(), # or RegisterImagesANTS()
Expand Down Expand Up @@ -334,20 +333,21 @@ registered_mesh = result["registered_template_model_surface"]
### Custom Segmentation

```python
from physiomotion4d import SegmentChestTotalSegmentator
from physiomotion4d import SegmentChestTotalSegmentatorWithContrast
import itk

# Initialize TotalSegmentator segmentation
segmenter = SegmentChestTotalSegmentator()
# Use SegmentChestTotalSegmentator instead for non-contrast studies
segmenter = SegmentChestTotalSegmentatorWithContrast()

# Load and segment image
image = itk.imread("chest_ct.nrrd")
masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

# Result always contains "labelmap" plus one entry per anatomy group the
# segmenter registered (heart, lung, bone, major_vessels, soft_tissue,
# contrast, other for SegmentChestTotalSegmentator). The exact key set is
# segmenter-specific; check membership when targeting multiple segmenters.
# contrast, other for SegmentChestTotalSegmentatorWithContrast). The exact
# key set is segmenter-specific; check membership when targeting multiple
# segmenters.
labelmap = masks["labelmap"]
heart_mask = masks["heart"]
if "lung" in masks:
Expand Down
2 changes: 1 addition & 1 deletion docs/api/segmentation/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Concrete segmenters accept an ITK image and return a dictionary of ITK images:

image = itk.imread("chest_ct.nrrd")
segmenter = SegmentChestTotalSegmentator()
masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

labelmap = masks["labelmap"]
if "heart" in masks:
Expand Down
2 changes: 1 addition & 1 deletion docs/api/segmentation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Basic Segmentation
from physiomotion4d import SegmentChestTotalSegmentator

segmenter = SegmentChestTotalSegmentator()
result = segmenter.segment(ct_image, contrast_enhanced_study=False)
result = segmenter.segment(ct_image)
labelmap = result['labelmap']

Module Documentation
Expand Down
2 changes: 1 addition & 1 deletion docs/api/segmentation/simpleware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Basic Usage

image = itk.imread("chest_ct.nrrd")
segmenter = SegmentHeartSimpleware()
masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

heart = masks["heart"]
vessels = masks["major_vessels"]
Expand Down
21 changes: 18 additions & 3 deletions docs/api/segmentation/totalsegmentator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Basic Usage
image = itk.imread("chest_ct.nrrd")
segmenter = SegmentChestTotalSegmentator()

masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

heart = masks["heart"]
lungs = masks["lung"]
Expand All @@ -53,15 +53,30 @@ keys:
* ``bone``
* ``soft_tissue``
* ``other``
* ``contrast``

The dictionary should be accessed by key. Do not unpack it positionally.
The exact key set is determined by the segmenter's :class:`AnatomyTaxonomy`
and may differ from other segmenters (see :doc:`base`). For
:class:`SegmentChestTotalSegmentator` specifically, all seven groups plus
:class:`SegmentChestTotalSegmentator` specifically, all six groups plus
``labelmap`` are always present; downstream code that targets a different
segmenter should check membership.

For contrast-enhanced studies, use
:class:`SegmentChestTotalSegmentatorWithContrast` instead of
:class:`SegmentChestTotalSegmentator`. It adds a ``contrast`` key to the
returned dictionary and exposes a ``contrast_threshold`` attribute
(default 500) that can be overridden before calling ``segment()``:

.. code-block:: python

from physiomotion4d import SegmentChestTotalSegmentatorWithContrast

segmenter = SegmentChestTotalSegmentatorWithContrast()
segmenter.contrast_threshold = 600 # optional override

masks = segmenter.segment(image)
contrast = masks["contrast"]

Operational Notes
=================

Expand Down
13 changes: 7 additions & 6 deletions docs/api/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,15 @@ Convert Image to USD

from physiomotion4d import (
RegisterImagesICON,
SegmentChestTotalSegmentator,
SegmentChestTotalSegmentatorWithContrast,
WorkflowConvertImageToUSD,
)

workflow = WorkflowConvertImageToUSD(
input_filenames=["cardiac_4d.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="patient_001",
segmentation_method=SegmentChestTotalSegmentator(),
segmentation_method=SegmentChestTotalSegmentatorWithContrast(),
registration_method=RegisterImagesICON(),
)
Comment on lines 57 to 63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## file list"
git ls-files docs/api/workflows.rst
git ls-files | rg '(^|/)(.*workflow.*|.*test.*|.*convert.*usd.*)\.(py|rst|md|ts|js)$' || true

echo
echo "## relevant snippets from docs/api/workflows.rst"
wc -l docs/api/workflows.rst
cat -n docs/api/workflows.rst | sed -n '45,85p'

echo
echo "## search for WorkflowConvertImageToUSD and usd_project_name"
rg -n "WorkflowConvertImageToUSD|usd_project_name|project_name" . -g '!**/.git/**' -g '!**/node_modules/**'

Repository: Project-MONAI/physiomotion4d

Length of output: 14494


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## docs/api/workflows.rst excerpt"
wc -l docs/api/workflows.rst
sed -n '55,70p' docs/api/workflows.rst | cat -n

echo
echo "## declarations/usages"
rg -n "class WorkflowConvertImageToUSD|def __init__|usd_project_name|project_name" . -g '!**/.git/**' -g '!**/node_modules/**'

Repository: Project-MONAI/physiomotion4d

Length of output: 11051


Rename project_name to usd_project_name in this example. WorkflowConvertImageToUSD takes usd_project_name, so this snippet doesn’t match the constructor as written.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/api/workflows.rst` around lines 57 - 63, The example for
WorkflowConvertImageToUSD uses the wrong constructor argument name, so update
the snippet to use usd_project_name instead of project_name. Keep the rest of
the example unchanged and make sure the parameter name matches the
WorkflowConvertImageToUSD initializer so the docs reflect the actual API.


Expand All @@ -77,15 +76,17 @@ Image to VTK

import itk

from physiomotion4d import SegmentChestTotalSegmentator, WorkflowConvertImageToVTK
from physiomotion4d import (
SegmentChestTotalSegmentatorWithContrast,
WorkflowConvertImageToVTK,
)

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

Expand Down
4 changes: 2 additions & 2 deletions docs/cli_scripts/byod_tutorials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ scene.

workflow = pm4d.WorkflowConvertImageToUSD(
input_filenames=["patient_dicom_dir"],
contrast_enhanced=False,
segmentation_method=pm4d.SegmentChestTotalSegmentator(),
output_directory="./results",
project_name="patient_heart",
)
Comment on lines 93 to 98

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the tutorial file and relevant workflow/API references.
git ls-files docs/cli_scripts/byod_tutorials.rst
rg -n "WorkflowConvertImageToUSD|usd_project_name|project_name" docs/cli_scripts/byod_tutorials.rst . -g '!**/.git/**'

Repository: Project-MONAI/physiomotion4d

Length of output: 11356


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the tutorial sections around the referenced lines.
sed -n '80,155p' docs/cli_scripts/byod_tutorials.rst

Repository: Project-MONAI/physiomotion4d

Length of output: 2354


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search tests or docs that define the expected keyword for this workflow.
rg -n "usd_project_name|project_name" . -g '!**/.git/**' -g '!docs/cli_scripts/byod_tutorials.rst'

Repository: Project-MONAI/physiomotion4d

Length of output: 5156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# If this file is generated from examples elsewhere, inspect nearby docs to compare.
rg -n "WorkflowConvertImageToUSD" docs . -g '!**/.git/**'

Repository: Project-MONAI/physiomotion4d

Length of output: 7905


Rename project_name to usd_project_name in both Python API snippets.

WorkflowConvertImageToUSD takes usd_project_name, so the calls at lines 93-98 and 140-146 will raise TypeError as written.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/cli_scripts/byod_tutorials.rst` around lines 93 - 98, Update the
WorkflowConvertImageToUSD Python API snippets to use the correct constructor
argument name: replace project_name with usd_project_name in the calls to
pm4d.WorkflowConvertImageToUSD. Make this change consistently in both tutorial
examples so the documented usage matches the actual WorkflowConvertImageToUSD
signature and avoids TypeError.

Expand Down Expand Up @@ -139,7 +139,7 @@ selects its default reference frame internally.

workflow = pm4d.WorkflowConvertImageToUSD(
input_filenames=["phase_000.mha", "phase_001.mha", "phase_002.mha"],
contrast_enhanced=False,
segmentation_method=pm4d.SegmentChestTotalSegmentator(),
output_directory="./results",
project_name="heart_animated",
times_per_second=30.0,
Expand Down
2 changes: 1 addition & 1 deletion docs/developer/segmentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Current Segmentation Contract

image = itk.imread("chest_ct.nrrd")
segmenter = SegmentChestTotalSegmentator()
masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

labelmap = masks["labelmap"]
if "heart" in masks:
Expand Down
1 change: 0 additions & 1 deletion docs/developer/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Workflow Example

workflow = WorkflowConvertImageToUSD(
input_filenames=["cardiac_4d.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="patient_001",
registration_method=RegisterImagesICON(),
Expand Down
23 changes: 10 additions & 13 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Complete end-to-end cardiac CT processing:
# Initialize workflow
workflow = WorkflowConvertImageToUSD(
input_filenames=["cardiac_4d.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="patient_001",
registration_method=RegisterImagesICON(),
Expand Down Expand Up @@ -83,15 +82,15 @@ Quick segmentation with TotalSegmentator:

.. code-block:: python

from physiomotion4d import SegmentChestTotalSegmentator
from physiomotion4d import SegmentChestTotalSegmentatorWithContrast
import itk

# Load image
image = itk.imread("chest_ct.nrrd")

# Segment
segmenter = SegmentChestTotalSegmentator()
masks = segmenter.segment(image, contrast_enhanced_study=True)
# Segment (use SegmentChestTotalSegmentator instead for non-contrast studies)
segmenter = SegmentChestTotalSegmentatorWithContrast()
masks = segmenter.segment(image)

# Extract masks by anatomy group
heart = masks["heart"]
Expand Down Expand Up @@ -409,7 +408,6 @@ Batch process multiple datasets:

workflow = WorkflowConvertImageToUSD(
input_filenames=[input_file],
contrast_enhanced=True,
output_directory=f"results/{patient_id}",
project_name=patient_id,
registration_method=RegisterImagesICON(),
Expand All @@ -428,15 +426,15 @@ Segment multiple images in parallel:

.. code-block:: python

from physiomotion4d import SegmentChestTotalSegmentator
from physiomotion4d import SegmentChestTotalSegmentatorWithContrast
import itk
import glob
from concurrent.futures import ProcessPoolExecutor

def segment_image(filename):
segmenter = SegmentChestTotalSegmentator()
segmenter = SegmentChestTotalSegmentatorWithContrast()
image = itk.imread(filename)
result = segmenter.segment(image, contrast_enhanced_study=True)
result = segmenter.segment(image)

# Save heart mask
output_name = filename.replace('.nrrd', '_heart.nrrd')
Expand Down Expand Up @@ -479,7 +477,6 @@ Run the supported end-to-end workflow API:

workflow = WorkflowConvertImageToUSD(
input_filenames=["cardiac_4d.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="cardiac_model",
registration_method=RegisterImagesICON(),
Expand All @@ -497,7 +494,7 @@ Mix and match different components:
.. code-block:: python

from physiomotion4d import (
SegmentChestTotalSegmentator,
SegmentChestTotalSegmentatorWithContrast,
RegisterImagesICON,
TransformTools,
ConvertVTKToUSD,
Expand All @@ -510,8 +507,8 @@ Mix and match different components:
frames = [itk.imread(f"frame_{i:03d}.mha") for i in range(10)]

# Segment reference
segmenter = SegmentChestTotalSegmentator()
result = segmenter.segment(reference, contrast_enhanced_study=True)
segmenter = SegmentChestTotalSegmentatorWithContrast()
result = segmenter.segment(reference)
heart_mask = result['heart']

# Extract reference contour
Expand Down
10 changes: 4 additions & 6 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ For more control, use the Python API:

processor = WorkflowConvertImageToUSD(
input_filenames=["path/to/cardiac_4d_ct.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="cardiac_model",
registration_method=RegisterImagesICON(),
Expand Down Expand Up @@ -152,7 +151,6 @@ For more control over individual steps:
# Initialize workflow
workflow = WorkflowConvertImageToUSD(
input_filenames=["cardiac_4d.nrrd"],
contrast_enhanced=True,
output_directory="./results",
project_name="cardiac_model",
registration_method=RegisterImagesICON(),
Expand All @@ -170,15 +168,15 @@ If you only need segmentation:

.. code-block:: python

from physiomotion4d import SegmentChestTotalSegmentator
from physiomotion4d import SegmentChestTotalSegmentatorWithContrast
import itk

# Initialize segmenter
segmenter = SegmentChestTotalSegmentator()
# Initialize segmenter (use SegmentChestTotalSegmentator for non-contrast studies)
segmenter = SegmentChestTotalSegmentatorWithContrast()

# Load and segment image
image = itk.imread("chest_ct.nrrd")
masks = segmenter.segment(image, contrast_enhanced_study=True)
masks = segmenter.segment(image)

# Extract individual anatomy masks by key
heart_mask = masks["heart"]
Expand Down
8 changes: 6 additions & 2 deletions docs/troubleshooting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,17 @@ Poor Segmentation Quality

**Solutions**:

1. Check if image is contrast-enhanced:
1. Check if image is contrast-enhanced. Use
:class:`SegmentChestTotalSegmentatorWithContrast` instead of
:class:`SegmentChestTotalSegmentator` for contrast-enhanced studies:

.. code-block:: python

from physiomotion4d import SegmentChestTotalSegmentatorWithContrast

workflow = WorkflowConvertImageToUSD(
...,
contrast_enhanced=True # or False
segmentation_method=SegmentChestTotalSegmentatorWithContrast(),
)
Comment on lines +72 to 83

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add the missing workflow import.

WorkflowConvertImageToUSD is used here without being imported, so the snippet is not copy/pasteable. Please add the workflow import alongside SegmentChestTotalSegmentatorWithContrast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/troubleshooting.rst` around lines 72 - 83, The troubleshooting example
uses WorkflowConvertImageToUSD without importing it, so update the documented
Python snippet to include the workflow import alongside
SegmentChestTotalSegmentatorWithContrast. Keep the import block in the same code
example and make sure the snippet is copy/pasteable by referencing
WorkflowConvertImageToUSD and SegmentChestTotalSegmentatorWithContrast together.


2. Preprocess intensity, spacing, and field of view before invoking the workflow.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def _build_registrar(
compression=True,
)

seg_result = segmenter.segment(warped_ref, contrast_enhanced_study=False)
seg_result = segmenter.segment(warped_ref)
warped_ref_labelmap = seg_result["labelmap"]
warped_ref_landmarks = segmenter.get_landmarks()

Expand Down
8 changes: 4 additions & 4 deletions experiments/Heart-GatedCT_To_USD/1-register_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import itk

from physiomotion4d.register_images_ants import RegisterImagesANTS
from physiomotion4d.segment_chest_total_segmentator import SegmentChestTotalSegmentator
from physiomotion4d.segment_chest_total_segmentator_with_contrast import (
SegmentChestTotalSegmentatorWithContrast,
)
from physiomotion4d.test_tools import TestTools
from physiomotion4d.transform_tools import TransformTools

Expand All @@ -32,10 +34,8 @@
fixed_image = itk.imread(str(fixed_image_filename))

# %%
seg = SegmentChestTotalSegmentator()
seg.contrast_threshold = 500
seg = SegmentChestTotalSegmentatorWithContrast()
seg.fast_mode = test_mode
seg.set_contrast_enhanced_study(True)
result = seg.segment(fixed_image)
# %%
labelmap_mask = result["labelmap"]
Expand Down
8 changes: 4 additions & 4 deletions experiments/Heart-GatedCT_To_USD/2-generate_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import pyvista as pv

from physiomotion4d.contour_tools import ContourTools
from physiomotion4d.segment_chest_total_segmentator import SegmentChestTotalSegmentator
from physiomotion4d.segment_chest_total_segmentator_with_contrast import (
SegmentChestTotalSegmentatorWithContrast,
)
from physiomotion4d.test_tools import TestTools

# nnUNetv2 (used by TotalSegmentator) spawns a multiprocessing.Pool. On Windows
Expand Down Expand Up @@ -68,10 +70,8 @@
)
outname = "slice_max"

seg = SegmentChestTotalSegmentator()
seg.contrast_threshold = 500
seg = SegmentChestTotalSegmentatorWithContrast()
seg.fast_mode = TestTools.running_as_test()
seg.set_contrast_enhanced_study(True)
if re_run_image_segmentation:
result = seg.segment(max_image)
labelmap_image = result["labelmap"]
Expand Down
Loading
Loading