diff --git a/data/Duke-Heart-4DLabelmaps/README.md b/data/Duke-Heart-4DLabelmaps/README.md index 3444b536..1dedb4de 100644 --- a/data/Duke-Heart-4DLabelmaps/README.md +++ b/data/Duke-Heart-4DLabelmaps/README.md @@ -1,14 +1,37 @@ # Duke-Heart-4DLabelmaps -Gated 4D cardiac labelmaps acquired at Duke University by Dr. Paul Segars. +Gated 4D cardiac labelmaps and landmark files derived from the PROMISE study +CT data acquired at Duke University, Dr. Paul Segars' lab. + +## Provenance + +The labelmaps were created from the original PROMISE study CT data using the +AS Cardio (ASCardio) segmentation module in Synopsys' Simpleware application. +MONAI Physio calls that module through the `SegmentHeartSimplewareTrimmedBranches` +class. + +**Important:** any AI physiological simulation model trained using these +labelmaps must be applied to labelmaps that were also generated with the +ASCardio module in Simpleware (for example via +`SegmentHeartSimplewareTrimmedBranches`). Applying such a model to labelmaps +from a different segmenter will degrade the results. ## Availability -This dataset is **scheduled for public release soon**. It is not distributed -with this repository and has no automatic downloader yet. +The labelmaps (`*_labelmap.nii.gz`) and landmark files (`*_landmark.mrk.json`) +can be downloaded automatically: -In the meantime, contact Stephen Aylward () to request -access. +```bash +monai-physio-download-data Duke-Heart-4DLabelmaps --directory data/Duke-Heart-4DLabelmaps +``` + +or from Python with `DownloadData.DownloadDukeHeart4DLabelmapsData`. The archive +is fetched from + +and extracted into this directory, creating the `pm00*/` case subdirectories. +Re-running is a no-op once the data is present. + +The original PROMISE CT images are not distributed with this repository. ## Effect on the tutorials @@ -20,17 +43,12 @@ their organ field, for example: There are ten of them. Nine form their own chain: Tutorial 4 (duke heart) -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -> 12. The tenth, Tutorial 2 (duke heart), is a separate optional ICON finetuning variant that the chain does not require. -They will not run until the data is -available. The other 19 tutorial scripts use publicly available datasets and are -unaffected - see [../README.md](../README.md) for download instructions. - -Downstream tutorials that consume `duke_heart` outputs (such as the finetuned -distance-map ICON weights used by -`tutorials/tutorial_07_heart_fit_statistical_model_to_patient.py`) fall back to -stock weights and still run, with reduced accuracy. +Download the data before running them. The other 19 tutorial scripts use +publicly available datasets and are unaffected - see [../README.md](../README.md) +for download instructions. ## Expected layout -When available, the data is expected under `data/Duke-Heart-4DLabelmaps/` as -one directory per case (`pm0002/`, `pm0003/`, ...), each holding one labelmap -per gated frame. +The data is placed under `data/Duke-Heart-4DLabelmaps/` as one directory per +case (`pm0002/`, `pm0003/`, ...), each holding one labelmap and one landmark +file per gated frame. diff --git a/data/README.md b/data/README.md index 6c4634bf..3b1c6de9 100644 --- a/data/README.md +++ b/data/README.md @@ -16,12 +16,13 @@ the source of truth. | `KCL-Heart-Model/` | Statistical shape model of the heart | King's College London (KCL) | Automatic | [KCL-Heart-Model/README.md](KCL-Heart-Model/README.md) | | `CHOP-Valve4D/` | 4D valve reconstruction models | Jolley Lab, CHOP (original FEBio model) | Automatic | [CHOP-Valve4D/README.md](CHOP-Valve4D/README.md) | | `Chest-CT/` | Ungated 3D chest CT, single static volume | AREN0534 trial, The Cancer Imaging Archive (TCIA) | Automatic | [Chest-CT/README.md](Chest-CT/README.md) | +| `Duke-Heart-4DLabelmaps/` | Gated 4D cardiac labelmaps and landmarks from PROMISE study CT, segmented with Simpleware ASCardio | Duke University (Dr. Paul Segars' lab) | Automatic | [Duke-Heart-4DLabelmaps/README.md](Duke-Heart-4DLabelmaps/README.md) | | `test/` | pytest-managed cache; not a downloadable dataset | - | N/A | [test/README.md](test/README.md) | ## Automatic Download -`Slicer-Heart-CT`, `KCL-Heart-Model`, `CHOP-Valve4D`, `Chest-CT`, and -`TCIA-4DLung` can be fetched with the `monai-physio-download-data` CLI or +`Slicer-Heart-CT`, `KCL-Heart-Model`, `CHOP-Valve4D`, `Chest-CT`, +`TCIA-4DLung`, and `Duke-Heart-4DLabelmaps` can be fetched with the `monai-physio-download-data` CLI or `DownloadData`; see each dataset's README for the exact command. `TCIA-4DLung`'s automatic download is a converted tutorial subset (the `.nii.gz` phases the tutorials read); the full TCIA 4D-Lung collection is diff --git a/docs/cli_scripts/download_data.rst b/docs/cli_scripts/download_data.rst index c454e67a..b4ada457 100644 --- a/docs/cli_scripts/download_data.rst +++ b/docs/cli_scripts/download_data.rst @@ -33,6 +33,13 @@ Supported Datasets Physio GitHub release. See ``data/TCIA-4DLung/README.md`` for the data source, the full-collection manual download, and required citation. + * - ``Duke-Heart-4DLabelmaps`` + - Gated 4D cardiac labelmaps and JSON landmark files from the PROMISE + study CT data (Duke University, Dr. Paul Segars' lab), segmented with + the ASCardio module of Simpleware, from the MONAI Physio GitHub + release. Models trained on them should only be applied to labelmaps + also generated with ASCardio (``SegmentHeartSimplewareTrimmedBranches``). + See ``data/Duke-Heart-4DLabelmaps/README.md``. * - ``PhysicsNeMo-MGN-Lung-Motion`` - Pretrained PhysicsNeMo MeshGraphNet checkpoint for lung motion, from the MONAI Physio GitHub release. Used by Lung Tutorial 10 and later @@ -55,12 +62,12 @@ Options .. code-block:: bash - monai-physio-download-data [Slicer-Heart-CT|KCL-Heart-Model|CHOP-Valve4D|Chest-CT|TCIA-4DLung|PhysicsNeMo-MGN-Lung-Motion] [--directory DIRECTORY] + monai-physio-download-data [Slicer-Heart-CT|KCL-Heart-Model|CHOP-Valve4D|Chest-CT|TCIA-4DLung|Duke-Heart-4DLabelmaps|PhysicsNeMo-MGN-Lung-Motion] [--directory DIRECTORY] ``data_name`` Dataset to download. One of ``Slicer-Heart-CT``, ``KCL-Heart-Model``, - ``CHOP-Valve4D``, ``Chest-CT``, ``TCIA-4DLung``, or - ``PhysicsNeMo-MGN-Lung-Motion``. Required - omitting it prints help and + ``CHOP-Valve4D``, ``Chest-CT``, ``TCIA-4DLung``, ``Duke-Heart-4DLabelmaps``, + or ``PhysicsNeMo-MGN-Lung-Motion``. Required - omitting it prints help and exits. ``--directory`` @@ -136,6 +143,18 @@ the download entirely once any case's phase volumes are already present - this is a converted tutorial subset; see ``data/TCIA-4DLung/README.md`` for how to obtain the full TCIA 4D-Lung collection manually. +For ``Duke-Heart-4DLabelmaps``, the command downloads, extracts, and reuses: + +.. code-block:: text + + data/Duke-Heart-4DLabelmaps/pm0002/*_labelmap.nii.gz + data/Duke-Heart-4DLabelmaps/pm0002/*_landmark.mrk.json ... + +The command uses +:meth:`monai_physio.download_data.DownloadData.DownloadDukeHeart4DLabelmapsData`, +which fetches ``Duke-Heart-4DLabelmaps.zip`` from the MONAI Physio GitHub +release and skips the download once any case's labelmaps are already present. + For ``PhysicsNeMo-MGN-Lung-Motion``, the command downloads, extracts, and reuses: @@ -162,7 +181,7 @@ See Also ``monai-physio-download-data`` entry - DIR-Lab distributes each case individually and may require registration, so it is manual-only, see ``data/DirLab-4DCT/README.md``. ``Duke-Heart-4DLabelmaps``, which drives the - ten ``duke_heart`` variants, is being released soon; see + ten ``duke_heart`` variants, is downloaded with the CLI; see ``data/Duke-Heart-4DLabelmaps/README.md``. ``PhysicsNeMo-MGN-Lung-Motion`` is the pretrained-checkpoint shortcut for Lung Tutorial 10 (and everything downstream of it: 11-14), letting a reader skip running Tutorial 9's diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 67a6a707..6b9c31f6 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -69,6 +69,9 @@ Then fetch the sample datasets, again from the top level of the clone: # Lung Tutorial 7 monai-physio-download-data Chest-CT --directory data/Chest-CT + # Duke heart tutorials 2 and 4 through 12 + monai-physio-download-data Duke-Heart-4DLabelmaps --directory data/Duke-Heart-4DLabelmaps + # No tutorial - transcatheter pulmonary valve experiments only, >2 GB monai-physio-download-data CHOP-Valve4D --directory data/CHOP-Valve4D @@ -97,7 +100,7 @@ Which dataset each tutorial needs: - CLI - Lung Tutorial 7, and Tutorial 13 * - ``Duke-Heart-4DLabelmaps`` - - Releasing soon + - CLI - The ten ``duke_heart`` variants, Tutorials 2 and 4 through 12 * - ``CHOP-Valve4D`` - CLI @@ -106,9 +109,11 @@ Which dataset each tutorial needs: Tutorials 5 and 9 need no dataset of their own: they consume the outputs of Tutorials 4 and 8 respectively. -``Duke-Heart-4DLabelmaps`` is scheduled for public release soon. Until then the -ten ``duke_heart`` variants cannot be run; contact Stephen Aylward -(saylward@nvidia.com) to request access, and see +``Duke-Heart-4DLabelmaps`` downloads as labelmaps and JSON landmark files +created from the PROMISE study CT data (Duke University, Dr. Paul Segars' lab) +with the ASCardio module of Simpleware. Models trained on them should only be +applied to labelmaps also generated with ASCardio, which MONAI Physio runs via +``SegmentHeartSimplewareTrimmedBranches``; see ``data/Duke-Heart-4DLabelmaps/README.md``. ``TCIA-4DLung``'s CLI download is a converted tutorial subset; the full TCIA diff --git a/src/monai_physio/cli/download_data.py b/src/monai_physio/cli/download_data.py index 7379491f..ac587fdf 100644 --- a/src/monai_physio/cli/download_data.py +++ b/src/monai_physio/cli/download_data.py @@ -14,6 +14,7 @@ CHOP_VALVE4D = "CHOP-Valve4D" CHEST_CT = "Chest-CT" TCIA_4D_LUNG = "TCIA-4DLung" +DUKE_HEART_4D_LABELMAPS = "Duke-Heart-4DLabelmaps" PHYSICSNEMO_MGN_LUNG_MOTION = "PhysicsNeMo-MGN-Lung-Motion" @@ -29,6 +30,7 @@ def main(argv: Optional[list[str]] = None) -> int: %(prog)s {CHOP_VALVE4D} --directory data/CHOP-Valve4D %(prog)s {CHEST_CT} --directory data/Chest-CT %(prog)s {TCIA_4D_LUNG} --directory data/TCIA-4DLung + %(prog)s {DUKE_HEART_4D_LABELMAPS} --directory data/Duke-Heart-4DLabelmaps %(prog)s {PHYSICSNEMO_MGN_LUNG_MOTION} --directory tutorials/network_weights """, ) @@ -41,6 +43,7 @@ def main(argv: Optional[list[str]] = None) -> int: CHOP_VALVE4D, CHEST_CT, TCIA_4D_LUNG, + DUKE_HEART_4D_LABELMAPS, PHYSICSNEMO_MGN_LUNG_MOTION, ], default=None, @@ -90,6 +93,11 @@ def main(argv: Optional[list[str]] = None) -> int: print(f"Downloaded {TCIA_4D_LUNG} to: {data_dir}") return 0 + if args.data_name == DUKE_HEART_4D_LABELMAPS: + data_dir = DownloadData.DownloadDukeHeart4DLabelmapsData(output_dir) + print(f"Downloaded {DUKE_HEART_4D_LABELMAPS} to: {data_dir}") + return 0 + if args.data_name == PHYSICSNEMO_MGN_LUNG_MOTION: data_dir = DownloadData.DownloadPhysicsNeMoMGNLungMotionData(output_dir) print(f"Downloaded {PHYSICSNEMO_MGN_LUNG_MOTION} to: {data_dir}") diff --git a/src/monai_physio/download_data.py b/src/monai_physio/download_data.py index 9f857036..339a9def 100644 --- a/src/monai_physio/download_data.py +++ b/src/monai_physio/download_data.py @@ -1,8 +1,8 @@ """ Dataset download and verification helpers. -Slicer-Heart-CT, KCL-Heart-Model, CHOP-Valve4D, Chest-CT, and TCIA-4DLung -are downloaded automatically. Other datasets require manual download, and +Slicer-Heart-CT, KCL-Heart-Model, CHOP-Valve4D, Chest-CT, TCIA-4DLung, and +Duke-Heart-4DLabelmaps are downloaded automatically. Other datasets require manual download, and the verification helpers check the file layouts used by the repository tutorials, experiments, and tests. """ @@ -397,6 +397,46 @@ def VerifyTCIA4DLungData(dirname: Union[str, Path]) -> bool: """Return True when TCIA-4DLung has at least one case's phase volumes.""" return any(Path(dirname).glob("*_HM10395/*_g0??.nii.gz")) + DUKE_HEART_4D_LABELMAPS_URL = ( + "https://github.com/Project-MONAI/monai-physio/releases/download/2026.07.1/" + "Duke-Heart-4DLabelmaps.zip" + ) + + @staticmethod + def DownloadDukeHeart4DLabelmapsData(dirname: Union[str, Path]) -> Path: + """Download the Duke-Heart-4DLabelmaps release into ``dirname``. + + Fetches ``Duke-Heart-4DLabelmaps.zip`` from the MONAI Physio 2026.07.1 + GitHub release and extracts it directly into ``dirname``, giving the + per-case ``pm00*/`` directories of ``*_labelmap.nii.gz`` and + ``*_landmark.mrk.json`` files. The labelmaps were generated with the + ASCardio module of Simpleware (``SegmentHeartSimplewareTrimmedBranches``); + models trained on them should only be applied to labelmaps generated + the same way. See ``data/Duke-Heart-4DLabelmaps/README.md``. + Already-populated data is left alone, so re-running is a no-op once + the data is present. + + Args: + dirname: Directory where the Duke-Heart-4DLabelmaps dataset should + live. + + Returns: + Path to ``dirname``. + """ + data_dir = Path(dirname) + if DownloadData.VerifyDukeHeart4DLabelmapsData(data_dir): + return data_dir + DownloadData._DownloadAndExtractZip( + DownloadData.DUKE_HEART_4D_LABELMAPS_URL, data_dir, extract_dir=data_dir + ) + _logger.info("Downloaded Duke-Heart-4DLabelmaps.zip") + return data_dir + + @staticmethod + def VerifyDukeHeart4DLabelmapsData(dirname: Union[str, Path]) -> bool: + """Return True when Duke-Heart-4DLabelmaps has at least one labelmap.""" + return any(Path(dirname).glob("pm*/*_labelmap.nii.gz")) + @staticmethod def _MetaImageHeaderHasBackingData(mhd_file: Path) -> bool: """Return True when a MetaImage ``.mhd`` header's pixel data exists. diff --git a/tests/test_download_data_cli.py b/tests/test_download_data_cli.py index 50dab3f8..2c39bcb4 100644 --- a/tests/test_download_data_cli.py +++ b/tests/test_download_data_cli.py @@ -118,3 +118,23 @@ def fake_download(dirname: Union[str, Path]) -> Path: assert result == 0 assert calls == [Path("data/TCIA-4DLung")] assert "Downloaded TCIA-4DLung" in capsys.readouterr().out + + +def test_download_data_cli_routes_duke_heart_4d_labelmaps( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """Duke-Heart-4DLabelmaps routes to its own downloader and default directory.""" + calls: list[Path] = [] + + def fake_download(dirname: Union[str, Path]) -> Path: + calls.append(Path(dirname)) + return Path(dirname) + + monkeypatch.setattr(DownloadData, "DownloadDukeHeart4DLabelmapsData", fake_download) + + result = download_data.main(["Duke-Heart-4DLabelmaps"]) + + assert result == 0 + assert calls == [Path("data/Duke-Heart-4DLabelmaps")] + assert "Downloaded Duke-Heart-4DLabelmaps" in capsys.readouterr().out