From c180f08d045ec4b769c8e49b14725f649e01baf4 Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Wed, 8 Jul 2026 13:42:29 -0400 Subject: [PATCH 1/3] ENH: Add CHOP-Valve4D auto-download; restructure dataset docs Extend DataDownloadTools/physiotwin4d-download-data with automatic downloaders for KCL-Heart-Model (Zenodo) and CHOP-Valve4D (the three CHOP-Valve4D-{Alterra,CT,TPV25}.zip assets on the 2026.07.1 GitHub release, extracted into matching subdirs). Both are idempotent/resumable and covered by unit tests. Replace the monolithic data/README.md with a short top-level overview table, and give each dataset its own README (Slicer-Heart-CT, DirLab-4DCT, KCL-Heart-Model, CHOP-Valve4D, plus the pytest-only data/test cache) documenting the download command, source URL(s), directory layout, sizes, and a dedicated Citation section. Fold the content of citation.txt/citations.txt/reading_img_files.url into those Citation sections and remove the now-redundant files. Commit the DirLab-4DCT .mhd MetaImage headers (metadata only, a few hundred bytes each) so the expected case/phase layout is documented in-repo; .gitignore now excludes *.mha instead of *.mhd so the large raw volumes stay untracked. Update tutorials/experiments docs to reflect KCL-Heart-Model's new auto-download path. --- .gitignore | 2 +- data/CHOP-Valve4D/README.md | 95 +++++ data/DirLab-4DCT/Case10Pack_T00.mhd | 11 + data/DirLab-4DCT/Case10Pack_T10.mhd | 11 + data/DirLab-4DCT/Case10Pack_T20.mhd | 11 + data/DirLab-4DCT/Case10Pack_T30.mhd | 11 + data/DirLab-4DCT/Case10Pack_T40.mhd | 11 + data/DirLab-4DCT/Case10Pack_T50.mhd | 11 + data/DirLab-4DCT/Case10Pack_T60.mhd | 11 + data/DirLab-4DCT/Case10Pack_T70.mhd | 11 + data/DirLab-4DCT/Case10Pack_T80.mhd | 11 + data/DirLab-4DCT/Case10Pack_T90.mhd | 11 + data/DirLab-4DCT/Case1Pack_T00.mhd | 11 + data/DirLab-4DCT/Case1Pack_T10.mhd | 11 + data/DirLab-4DCT/Case1Pack_T20.mhd | 11 + data/DirLab-4DCT/Case1Pack_T30.mhd | 11 + data/DirLab-4DCT/Case1Pack_T40.mhd | 11 + data/DirLab-4DCT/Case1Pack_T50.mhd | 11 + data/DirLab-4DCT/Case1Pack_T60.mhd | 11 + data/DirLab-4DCT/Case1Pack_T70.mhd | 11 + data/DirLab-4DCT/Case1Pack_T80.mhd | 11 + data/DirLab-4DCT/Case1Pack_T90.mhd | 11 + data/DirLab-4DCT/Case2Pack_T00.mhd | 11 + data/DirLab-4DCT/Case2Pack_T10.mhd | 11 + data/DirLab-4DCT/Case2Pack_T20.mhd | 11 + data/DirLab-4DCT/Case2Pack_T30.mhd | 11 + data/DirLab-4DCT/Case2Pack_T40.mhd | 11 + data/DirLab-4DCT/Case2Pack_T50.mhd | 11 + data/DirLab-4DCT/Case2Pack_T60.mhd | 11 + data/DirLab-4DCT/Case2Pack_T70.mhd | 11 + data/DirLab-4DCT/Case2Pack_T80.mhd | 11 + data/DirLab-4DCT/Case2Pack_T90.mhd | 11 + data/DirLab-4DCT/Case3Pack_T00.mhd | 11 + data/DirLab-4DCT/Case3Pack_T10.mhd | 11 + data/DirLab-4DCT/Case3Pack_T20.mhd | 11 + data/DirLab-4DCT/Case3Pack_T30.mhd | 11 + data/DirLab-4DCT/Case3Pack_T40.mhd | 11 + data/DirLab-4DCT/Case3Pack_T50.mhd | 11 + data/DirLab-4DCT/Case3Pack_T60.mhd | 11 + data/DirLab-4DCT/Case3Pack_T70.mhd | 11 + data/DirLab-4DCT/Case3Pack_T80.mhd | 11 + data/DirLab-4DCT/Case3Pack_T90.mhd | 11 + data/DirLab-4DCT/Case4Pack_T00.mhd | 11 + data/DirLab-4DCT/Case4Pack_T10.mhd | 11 + data/DirLab-4DCT/Case4Pack_T20.mhd | 11 + data/DirLab-4DCT/Case4Pack_T30.mhd | 11 + data/DirLab-4DCT/Case4Pack_T40.mhd | 11 + data/DirLab-4DCT/Case4Pack_T50.mhd | 11 + data/DirLab-4DCT/Case4Pack_T60.mhd | 11 + data/DirLab-4DCT/Case4Pack_T70.mhd | 11 + data/DirLab-4DCT/Case4Pack_T80.mhd | 11 + data/DirLab-4DCT/Case4Pack_T90.mhd | 11 + data/DirLab-4DCT/Case5Pack_T00.mhd | 11 + data/DirLab-4DCT/Case5Pack_T10.mhd | 11 + data/DirLab-4DCT/Case5Pack_T20.mhd | 11 + data/DirLab-4DCT/Case5Pack_T30.mhd | 11 + data/DirLab-4DCT/Case5Pack_T40.mhd | 11 + data/DirLab-4DCT/Case5Pack_T50.mhd | 11 + data/DirLab-4DCT/Case5Pack_T60.mhd | 11 + data/DirLab-4DCT/Case5Pack_T70.mhd | 11 + data/DirLab-4DCT/Case5Pack_T80.mhd | 11 + data/DirLab-4DCT/Case5Pack_T90.mhd | 11 + data/DirLab-4DCT/Case6Pack_T00.mhd | 11 + data/DirLab-4DCT/Case6Pack_T10.mhd | 11 + data/DirLab-4DCT/Case6Pack_T20.mhd | 11 + data/DirLab-4DCT/Case6Pack_T30.mhd | 11 + data/DirLab-4DCT/Case6Pack_T40.mhd | 11 + data/DirLab-4DCT/Case6Pack_T50.mhd | 11 + data/DirLab-4DCT/Case6Pack_T60.mhd | 11 + data/DirLab-4DCT/Case6Pack_T70.mhd | 11 + data/DirLab-4DCT/Case6Pack_T80.mhd | 11 + data/DirLab-4DCT/Case6Pack_T90.mhd | 11 + data/DirLab-4DCT/Case7Pack_T00.mhd | 11 + data/DirLab-4DCT/Case7Pack_T10.mhd | 11 + data/DirLab-4DCT/Case7Pack_T20.mhd | 11 + data/DirLab-4DCT/Case7Pack_T30.mhd | 11 + data/DirLab-4DCT/Case7Pack_T40.mhd | 11 + data/DirLab-4DCT/Case7Pack_T50.mhd | 11 + data/DirLab-4DCT/Case7Pack_T60.mhd | 11 + data/DirLab-4DCT/Case7Pack_T70.mhd | 11 + data/DirLab-4DCT/Case7Pack_T80.mhd | 11 + data/DirLab-4DCT/Case7Pack_T90.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T00.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T10.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T20.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T30.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T40.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T50.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T60.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T70.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T80.mhd | 11 + data/DirLab-4DCT/Case8Deploy_T90.mhd | 11 + data/DirLab-4DCT/Case9Pack_T00.mhd | 11 + data/DirLab-4DCT/Case9Pack_T10.mhd | 11 + data/DirLab-4DCT/Case9Pack_T20.mhd | 11 + data/DirLab-4DCT/Case9Pack_T30.mhd | 11 + data/DirLab-4DCT/Case9Pack_T40.mhd | 11 + data/DirLab-4DCT/Case9Pack_T50.mhd | 11 + data/DirLab-4DCT/Case9Pack_T60.mhd | 11 + data/DirLab-4DCT/Case9Pack_T70.mhd | 11 + data/DirLab-4DCT/Case9Pack_T80.mhd | 11 + data/DirLab-4DCT/Case9Pack_T90.mhd | 11 + data/DirLab-4DCT/README.md | 114 ++++++ data/DirLab-4DCT/citations.txt | 7 - data/DirLab-4DCT/reading_img_files.url | 2 - data/KCL-Heart-Model/README.md | 23 +- data/KCL-Heart-Model/citation.txt | 1 - data/README.md | 333 ++---------------- data/Slicer-Heart-CT/README.md | 64 ++++ data/Slicer-Heart-CT/download_and_convert.py | 27 -- data/test/README.md | 32 ++ docs/cli_scripts/download_data.rst | 44 ++- docs/tutorials.rst | 4 +- .../Heart-Create_Statistical_Model/README.md | 12 +- src/physiotwin4d/cli/download_data.py | 23 +- src/physiotwin4d/data_download_tools.py | 152 +++++++- tests/test_download_data_cli.py | 40 +++ tests/test_download_heart_data.py | 107 ++++++ tutorials/README.md | 2 +- 119 files changed, 1819 insertions(+), 365 deletions(-) create mode 100644 data/CHOP-Valve4D/README.md create mode 100644 data/DirLab-4DCT/Case10Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case10Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case1Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case2Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case3Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case4Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case5Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case6Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case7Pack_T90.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T00.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T10.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T20.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T30.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T40.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T50.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T60.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T70.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T80.mhd create mode 100644 data/DirLab-4DCT/Case8Deploy_T90.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T00.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T10.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T20.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T30.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T40.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T50.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T60.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T70.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T80.mhd create mode 100644 data/DirLab-4DCT/Case9Pack_T90.mhd create mode 100644 data/DirLab-4DCT/README.md delete mode 100644 data/DirLab-4DCT/citations.txt delete mode 100644 data/DirLab-4DCT/reading_img_files.url delete mode 100644 data/KCL-Heart-Model/citation.txt create mode 100644 data/Slicer-Heart-CT/README.md delete mode 100644 data/Slicer-Heart-CT/download_and_convert.py create mode 100644 data/test/README.md diff --git a/.gitignore b/.gitignore index c117323..1e9db68 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,7 @@ network_weights # Data files *.gz *.mat -*.mhd +*.mha *.raw *.zip *.nii diff --git a/data/CHOP-Valve4D/README.md b/data/CHOP-Valve4D/README.md new file mode 100644 index 0000000..bc1ace4 --- /dev/null +++ b/data/CHOP-Valve4D/README.md @@ -0,0 +1,95 @@ +# CHOP-Valve4D + +## Download + +Download this dataset automatically with: + +```bash +physiotwin4d-download-data CHOP-Valve4D --directory data/CHOP-Valve4D +``` + +or from Python: + +```python +from physiotwin4d import DataDownloadTools + +DataDownloadTools.DownloadCHOPValve4DData("data/CHOP-Valve4D") +assert DataDownloadTools.VerifyCHOPValve4DData("data/CHOP-Valve4D") +``` + +This downloads and extracts three zip archives attached to the +[PhysioTwin4D 2026.07.1 GitHub release](https://github.com/Project-MONAI/physiotwin4d/releases/tag/2026.07.1) +into matching subdirectories: + +| Release asset | Extracted into | Contents | Size | +| ----------------------------- | --------------- | ------------------------------------------- | ------- | +| `CHOP-Valve4D-Alterra.zip` | `Alterra/` | Alterra valve mesh time series (`.vtk`) | >1 GB | +| `CHOP-Valve4D-TPV25.zip` | `TPV25/` | TPV25 valve mesh time series (`.vtk`) | >1 GB | +| `CHOP-Valve4D-CT.zip` | `CT/` | Source CT volume + Simpleware segmentation | — | + +A subdirectory that already contains files is left alone, so re-running the +command resumes an interrupted download. + +**Directory structure after download:** +``` +data/CHOP-Valve4D/ +├── Alterra/ +│ ├── frame_0000.vtk +│ ├── frame_0001.vtk +│ └── ... +├── TPV25/ +│ ├── frame_0000.vtk +│ ├── frame_0001.vtk +│ └── ... +├── CT/ +│ ├── RVOT28-Dias.nii.gz (or .mha) +│ └── Simpleware/ +│ └── parts/ +└── README.md (this file) +``` + +## Overview + +Time-varying 4D valve reconstruction models showing transcatheter +pulmonary valve motion over the cardiac cycle, derived from an FEBio +finite-element simulation of an Alterra valve deployed in a native right +ventricular outflow tract (RVOT). + +### Original Data + +The source data is an FEBio finite-element model (`.feb`) published by the +Jolley Lab at CHOP (Children's Hospital of Philadelphia). This repository +does not redistribute the source `.feb` file — it is available from the +FEBio website: + +- Project page: https://repo.febio.org/permalink/project/136 +- Direct download: https://repo.febio.org/modelRepo/api/v1.05/files/0/136 + +### Converted Data (PhysioTwin4D Convenience Release) + +As a convenience, PhysioTwin4D converts the original FEBio geometry to VTK +(surface and volumetric meshes) and ITK (image) formats, and also provides +a Simpleware segmentation derived from the model. These converted files are +what the `physiotwin4d-download-data CHOP-Valve4D` command above fetches; +they are not tracked in this git repository. The original citation and +license terms below still apply to this converted data. + +### Citation + +Zelonis, C. N., Maheshwari, J., Wu, W., Maas, S. A., Aslan, S., Sunderland, K., +... & Jolley, M. A. (2025). Integrated Open-Source Framework for Simulation of +Transcatheter Pulmonary Valves in Native Right Ventricular Outflow Tracts. arXiv +preprint arXiv:2507.06337. + +### Acknowledgement + +Data provided by the Jolley Lab at CHOP (Children's Hospital of +Philadelphia): +- https://www.linkedin.com/company/jolleylab + +## Using This Dataset + +- Time-series VTK to USD conversion (`experiments/Convert_VTK_To_USD/`) +- 4D valve motion visualization in NVIDIA Omniverse +- Temporal cardiac mechanics analysis +- Valve dynamics studies and surgical planning diff --git a/data/DirLab-4DCT/Case10Pack_T00.mhd b/data/DirLab-4DCT/Case10Pack_T00.mhd new file mode 100644 index 0000000..0efb536 --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T00.img diff --git a/data/DirLab-4DCT/Case10Pack_T10.mhd b/data/DirLab-4DCT/Case10Pack_T10.mhd new file mode 100644 index 0000000..69c1c0f --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T10.img diff --git a/data/DirLab-4DCT/Case10Pack_T20.mhd b/data/DirLab-4DCT/Case10Pack_T20.mhd new file mode 100644 index 0000000..8f1b290 --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T20.img diff --git a/data/DirLab-4DCT/Case10Pack_T30.mhd b/data/DirLab-4DCT/Case10Pack_T30.mhd new file mode 100644 index 0000000..c5f5fbe --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T30.img diff --git a/data/DirLab-4DCT/Case10Pack_T40.mhd b/data/DirLab-4DCT/Case10Pack_T40.mhd new file mode 100644 index 0000000..0b0e9ea --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T40.img diff --git a/data/DirLab-4DCT/Case10Pack_T50.mhd b/data/DirLab-4DCT/Case10Pack_T50.mhd new file mode 100644 index 0000000..21b934f --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T50.img diff --git a/data/DirLab-4DCT/Case10Pack_T60.mhd b/data/DirLab-4DCT/Case10Pack_T60.mhd new file mode 100644 index 0000000..c976c0c --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T60.img diff --git a/data/DirLab-4DCT/Case10Pack_T70.mhd b/data/DirLab-4DCT/Case10Pack_T70.mhd new file mode 100644 index 0000000..7a5c45f --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T70.img diff --git a/data/DirLab-4DCT/Case10Pack_T80.mhd b/data/DirLab-4DCT/Case10Pack_T80.mhd new file mode 100644 index 0000000..748d712 --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T80.img diff --git a/data/DirLab-4DCT/Case10Pack_T90.mhd b/data/DirLab-4DCT/Case10Pack_T90.mhd new file mode 100644 index 0000000..78db654 --- /dev/null +++ b/data/DirLab-4DCT/Case10Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 120 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case10Pack/Images/case10_T90.img diff --git a/data/DirLab-4DCT/Case1Pack_T00.mhd b/data/DirLab-4DCT/Case1Pack_T00.mhd new file mode 100644 index 0000000..a48edee --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T00_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T10.mhd b/data/DirLab-4DCT/Case1Pack_T10.mhd new file mode 100644 index 0000000..1857d54 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T10_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T20.mhd b/data/DirLab-4DCT/Case1Pack_T20.mhd new file mode 100644 index 0000000..a15568e --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T20_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T30.mhd b/data/DirLab-4DCT/Case1Pack_T30.mhd new file mode 100644 index 0000000..6cfbfd1 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T30_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T40.mhd b/data/DirLab-4DCT/Case1Pack_T40.mhd new file mode 100644 index 0000000..f7147c1 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T40_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T50.mhd b/data/DirLab-4DCT/Case1Pack_T50.mhd new file mode 100644 index 0000000..6483a11 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T50_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T60.mhd b/data/DirLab-4DCT/Case1Pack_T60.mhd new file mode 100644 index 0000000..37cdc80 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T60_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T70.mhd b/data/DirLab-4DCT/Case1Pack_T70.mhd new file mode 100644 index 0000000..b0d21ca --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T70_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T80.mhd b/data/DirLab-4DCT/Case1Pack_T80.mhd new file mode 100644 index 0000000..93967ed --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T80_s.img diff --git a/data/DirLab-4DCT/Case1Pack_T90.mhd b/data/DirLab-4DCT/Case1Pack_T90.mhd new file mode 100644 index 0000000..1d12555 --- /dev/null +++ b/data/DirLab-4DCT/Case1Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case1Pack/Images/case1_T90_s.img diff --git a/data/DirLab-4DCT/Case2Pack_T00.mhd b/data/DirLab-4DCT/Case2Pack_T00.mhd new file mode 100644 index 0000000..96f1e89 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T00-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T10.mhd b/data/DirLab-4DCT/Case2Pack_T10.mhd new file mode 100644 index 0000000..e0bae55 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T10-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T20.mhd b/data/DirLab-4DCT/Case2Pack_T20.mhd new file mode 100644 index 0000000..bfc2b96 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T20-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T30.mhd b/data/DirLab-4DCT/Case2Pack_T30.mhd new file mode 100644 index 0000000..2011d3f --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T30-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T40.mhd b/data/DirLab-4DCT/Case2Pack_T40.mhd new file mode 100644 index 0000000..6f9ced3 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T40-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T50.mhd b/data/DirLab-4DCT/Case2Pack_T50.mhd new file mode 100644 index 0000000..af12b3b --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T50-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T60.mhd b/data/DirLab-4DCT/Case2Pack_T60.mhd new file mode 100644 index 0000000..9fab5c7 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T60-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T70.mhd b/data/DirLab-4DCT/Case2Pack_T70.mhd new file mode 100644 index 0000000..a1d6501 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T70-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T80.mhd b/data/DirLab-4DCT/Case2Pack_T80.mhd new file mode 100644 index 0000000..6275a6d --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T80-ssm.img diff --git a/data/DirLab-4DCT/Case2Pack_T90.mhd b/data/DirLab-4DCT/Case2Pack_T90.mhd new file mode 100644 index 0000000..53cf919 --- /dev/null +++ b/data/DirLab-4DCT/Case2Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 112 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.16 1.16 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case2Pack/Images/case2_T90-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T00.mhd b/data/DirLab-4DCT/Case3Pack_T00.mhd new file mode 100644 index 0000000..117a63b --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T00-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T10.mhd b/data/DirLab-4DCT/Case3Pack_T10.mhd new file mode 100644 index 0000000..a296f9a --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T10-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T20.mhd b/data/DirLab-4DCT/Case3Pack_T20.mhd new file mode 100644 index 0000000..798e073 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T20-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T30.mhd b/data/DirLab-4DCT/Case3Pack_T30.mhd new file mode 100644 index 0000000..eda0884 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T30-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T40.mhd b/data/DirLab-4DCT/Case3Pack_T40.mhd new file mode 100644 index 0000000..d3f7507 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T40-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T50.mhd b/data/DirLab-4DCT/Case3Pack_T50.mhd new file mode 100644 index 0000000..e78bc8e --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T50-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T60.mhd b/data/DirLab-4DCT/Case3Pack_T60.mhd new file mode 100644 index 0000000..5faede2 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T60-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T70.mhd b/data/DirLab-4DCT/Case3Pack_T70.mhd new file mode 100644 index 0000000..9a42415 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T70-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T80.mhd b/data/DirLab-4DCT/Case3Pack_T80.mhd new file mode 100644 index 0000000..5ecec92 --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T80-ssm.img diff --git a/data/DirLab-4DCT/Case3Pack_T90.mhd b/data/DirLab-4DCT/Case3Pack_T90.mhd new file mode 100644 index 0000000..976315d --- /dev/null +++ b/data/DirLab-4DCT/Case3Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 104 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.15 1.15 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case3Pack/Images/case3_T90-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T00.mhd b/data/DirLab-4DCT/Case4Pack_T00.mhd new file mode 100644 index 0000000..267078f --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T00-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T10.mhd b/data/DirLab-4DCT/Case4Pack_T10.mhd new file mode 100644 index 0000000..27abe64 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T10-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T20.mhd b/data/DirLab-4DCT/Case4Pack_T20.mhd new file mode 100644 index 0000000..cfa443e --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T20-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T30.mhd b/data/DirLab-4DCT/Case4Pack_T30.mhd new file mode 100644 index 0000000..26524b2 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T30-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T40.mhd b/data/DirLab-4DCT/Case4Pack_T40.mhd new file mode 100644 index 0000000..5519737 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T40-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T50.mhd b/data/DirLab-4DCT/Case4Pack_T50.mhd new file mode 100644 index 0000000..3fb3cb1 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T50-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T60.mhd b/data/DirLab-4DCT/Case4Pack_T60.mhd new file mode 100644 index 0000000..654797a --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T60-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T70.mhd b/data/DirLab-4DCT/Case4Pack_T70.mhd new file mode 100644 index 0000000..d0d6968 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T70-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T80.mhd b/data/DirLab-4DCT/Case4Pack_T80.mhd new file mode 100644 index 0000000..c07af58 --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T80-ssm.img diff --git a/data/DirLab-4DCT/Case4Pack_T90.mhd b/data/DirLab-4DCT/Case4Pack_T90.mhd new file mode 100644 index 0000000..7142cdd --- /dev/null +++ b/data/DirLab-4DCT/Case4Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 99 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.13 1.13 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case4Pack/Images/case4_T90-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T00.mhd b/data/DirLab-4DCT/Case5Pack_T00.mhd new file mode 100644 index 0000000..6b8b3be --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T00-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T10.mhd b/data/DirLab-4DCT/Case5Pack_T10.mhd new file mode 100644 index 0000000..2e89bbc --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T10-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T20.mhd b/data/DirLab-4DCT/Case5Pack_T20.mhd new file mode 100644 index 0000000..d7a3e80 --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T20-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T30.mhd b/data/DirLab-4DCT/Case5Pack_T30.mhd new file mode 100644 index 0000000..78abcb6 --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T30-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T40.mhd b/data/DirLab-4DCT/Case5Pack_T40.mhd new file mode 100644 index 0000000..1796e17 --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T40-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T50.mhd b/data/DirLab-4DCT/Case5Pack_T50.mhd new file mode 100644 index 0000000..d5900de --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T50-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T60.mhd b/data/DirLab-4DCT/Case5Pack_T60.mhd new file mode 100644 index 0000000..aeccb3b --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T60-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T70.mhd b/data/DirLab-4DCT/Case5Pack_T70.mhd new file mode 100644 index 0000000..cf14add --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T70-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T80.mhd b/data/DirLab-4DCT/Case5Pack_T80.mhd new file mode 100644 index 0000000..4e90974 --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T80-ssm.img diff --git a/data/DirLab-4DCT/Case5Pack_T90.mhd b/data/DirLab-4DCT/Case5Pack_T90.mhd new file mode 100644 index 0000000..b8f18fb --- /dev/null +++ b/data/DirLab-4DCT/Case5Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 256 256 106 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 1.1 1.1 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case5Pack/Images/case5_T90-ssm.img diff --git a/data/DirLab-4DCT/Case6Pack_T00.mhd b/data/DirLab-4DCT/Case6Pack_T00.mhd new file mode 100644 index 0000000..4f95634 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T00.img diff --git a/data/DirLab-4DCT/Case6Pack_T10.mhd b/data/DirLab-4DCT/Case6Pack_T10.mhd new file mode 100644 index 0000000..1cd17e6 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T10.img diff --git a/data/DirLab-4DCT/Case6Pack_T20.mhd b/data/DirLab-4DCT/Case6Pack_T20.mhd new file mode 100644 index 0000000..b5d7076 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T20.img diff --git a/data/DirLab-4DCT/Case6Pack_T30.mhd b/data/DirLab-4DCT/Case6Pack_T30.mhd new file mode 100644 index 0000000..7cb1347 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T30.img diff --git a/data/DirLab-4DCT/Case6Pack_T40.mhd b/data/DirLab-4DCT/Case6Pack_T40.mhd new file mode 100644 index 0000000..7b3f2dc --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T40.img diff --git a/data/DirLab-4DCT/Case6Pack_T50.mhd b/data/DirLab-4DCT/Case6Pack_T50.mhd new file mode 100644 index 0000000..58626d7 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T50.img diff --git a/data/DirLab-4DCT/Case6Pack_T60.mhd b/data/DirLab-4DCT/Case6Pack_T60.mhd new file mode 100644 index 0000000..da884bc --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T60.img diff --git a/data/DirLab-4DCT/Case6Pack_T70.mhd b/data/DirLab-4DCT/Case6Pack_T70.mhd new file mode 100644 index 0000000..3f40968 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T70.img diff --git a/data/DirLab-4DCT/Case6Pack_T80.mhd b/data/DirLab-4DCT/Case6Pack_T80.mhd new file mode 100644 index 0000000..8928248 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T80.img diff --git a/data/DirLab-4DCT/Case6Pack_T90.mhd b/data/DirLab-4DCT/Case6Pack_T90.mhd new file mode 100644 index 0000000..7b7fb20 --- /dev/null +++ b/data/DirLab-4DCT/Case6Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case6Pack/Images/case6_T90.img diff --git a/data/DirLab-4DCT/Case7Pack_T00.mhd b/data/DirLab-4DCT/Case7Pack_T00.mhd new file mode 100644 index 0000000..7e9a4ca --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T00.img diff --git a/data/DirLab-4DCT/Case7Pack_T10.mhd b/data/DirLab-4DCT/Case7Pack_T10.mhd new file mode 100644 index 0000000..6a37ac5 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T10.img diff --git a/data/DirLab-4DCT/Case7Pack_T20.mhd b/data/DirLab-4DCT/Case7Pack_T20.mhd new file mode 100644 index 0000000..c3ebe13 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T20.img diff --git a/data/DirLab-4DCT/Case7Pack_T30.mhd b/data/DirLab-4DCT/Case7Pack_T30.mhd new file mode 100644 index 0000000..fdb6e81 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T30.img diff --git a/data/DirLab-4DCT/Case7Pack_T40.mhd b/data/DirLab-4DCT/Case7Pack_T40.mhd new file mode 100644 index 0000000..3da5551 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T40.img diff --git a/data/DirLab-4DCT/Case7Pack_T50.mhd b/data/DirLab-4DCT/Case7Pack_T50.mhd new file mode 100644 index 0000000..258cfa3 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T50.img diff --git a/data/DirLab-4DCT/Case7Pack_T60.mhd b/data/DirLab-4DCT/Case7Pack_T60.mhd new file mode 100644 index 0000000..dbebb06 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T60.img diff --git a/data/DirLab-4DCT/Case7Pack_T70.mhd b/data/DirLab-4DCT/Case7Pack_T70.mhd new file mode 100644 index 0000000..8d2415e --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T70.img diff --git a/data/DirLab-4DCT/Case7Pack_T80.mhd b/data/DirLab-4DCT/Case7Pack_T80.mhd new file mode 100644 index 0000000..ff9b455 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T80.img diff --git a/data/DirLab-4DCT/Case7Pack_T90.mhd b/data/DirLab-4DCT/Case7Pack_T90.mhd new file mode 100644 index 0000000..605bc04 --- /dev/null +++ b/data/DirLab-4DCT/Case7Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 136 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case7Pack/Images/case7_T90.img diff --git a/data/DirLab-4DCT/Case8Deploy_T00.mhd b/data/DirLab-4DCT/Case8Deploy_T00.mhd new file mode 100644 index 0000000..9854edb --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T00.img diff --git a/data/DirLab-4DCT/Case8Deploy_T10.mhd b/data/DirLab-4DCT/Case8Deploy_T10.mhd new file mode 100644 index 0000000..c9cb21b --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T10.img diff --git a/data/DirLab-4DCT/Case8Deploy_T20.mhd b/data/DirLab-4DCT/Case8Deploy_T20.mhd new file mode 100644 index 0000000..9a742b8 --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T20.img diff --git a/data/DirLab-4DCT/Case8Deploy_T30.mhd b/data/DirLab-4DCT/Case8Deploy_T30.mhd new file mode 100644 index 0000000..be830ba --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T30.img diff --git a/data/DirLab-4DCT/Case8Deploy_T40.mhd b/data/DirLab-4DCT/Case8Deploy_T40.mhd new file mode 100644 index 0000000..bdc3cf0 --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T40.img diff --git a/data/DirLab-4DCT/Case8Deploy_T50.mhd b/data/DirLab-4DCT/Case8Deploy_T50.mhd new file mode 100644 index 0000000..6ece1d4 --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T50.img diff --git a/data/DirLab-4DCT/Case8Deploy_T60.mhd b/data/DirLab-4DCT/Case8Deploy_T60.mhd new file mode 100644 index 0000000..e4bd0e0 --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T60.img diff --git a/data/DirLab-4DCT/Case8Deploy_T70.mhd b/data/DirLab-4DCT/Case8Deploy_T70.mhd new file mode 100644 index 0000000..d75371a --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T70.img diff --git a/data/DirLab-4DCT/Case8Deploy_T80.mhd b/data/DirLab-4DCT/Case8Deploy_T80.mhd new file mode 100644 index 0000000..e156cf2 --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T80.img diff --git a/data/DirLab-4DCT/Case8Deploy_T90.mhd b/data/DirLab-4DCT/Case8Deploy_T90.mhd new file mode 100644 index 0000000..d48f37f --- /dev/null +++ b/data/DirLab-4DCT/Case8Deploy_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case8Deploy/Images/case8_T90.img diff --git a/data/DirLab-4DCT/Case9Pack_T00.mhd b/data/DirLab-4DCT/Case9Pack_T00.mhd new file mode 100644 index 0000000..6f09e31 --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T00.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T00.img diff --git a/data/DirLab-4DCT/Case9Pack_T10.mhd b/data/DirLab-4DCT/Case9Pack_T10.mhd new file mode 100644 index 0000000..1dd562d --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T10.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T10.img diff --git a/data/DirLab-4DCT/Case9Pack_T20.mhd b/data/DirLab-4DCT/Case9Pack_T20.mhd new file mode 100644 index 0000000..9972c4f --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T20.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T20.img diff --git a/data/DirLab-4DCT/Case9Pack_T30.mhd b/data/DirLab-4DCT/Case9Pack_T30.mhd new file mode 100644 index 0000000..437194a --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T30.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T30.img diff --git a/data/DirLab-4DCT/Case9Pack_T40.mhd b/data/DirLab-4DCT/Case9Pack_T40.mhd new file mode 100644 index 0000000..e36d6a3 --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T40.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T40.img diff --git a/data/DirLab-4DCT/Case9Pack_T50.mhd b/data/DirLab-4DCT/Case9Pack_T50.mhd new file mode 100644 index 0000000..e57002f --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T50.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T50.img diff --git a/data/DirLab-4DCT/Case9Pack_T60.mhd b/data/DirLab-4DCT/Case9Pack_T60.mhd new file mode 100644 index 0000000..45eef91 --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T60.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T60.img diff --git a/data/DirLab-4DCT/Case9Pack_T70.mhd b/data/DirLab-4DCT/Case9Pack_T70.mhd new file mode 100644 index 0000000..2f873dd --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T70.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T70.img diff --git a/data/DirLab-4DCT/Case9Pack_T80.mhd b/data/DirLab-4DCT/Case9Pack_T80.mhd new file mode 100644 index 0000000..8c36455 --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T80.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T80.img diff --git a/data/DirLab-4DCT/Case9Pack_T90.mhd b/data/DirLab-4DCT/Case9Pack_T90.mhd new file mode 100644 index 0000000..15a3d2d --- /dev/null +++ b/data/DirLab-4DCT/Case9Pack_T90.mhd @@ -0,0 +1,11 @@ +ObjectType = Image +NDims = 3 +DimSize = 512 512 128 +HeaderSize = -1 +BinaryData = True +BinaryDataByteOrderMSB = False +Orientation = 1 0 0 0 1 0 0 0 -1 +ElementSpacing = 0.97 0.97 2.5 +ElementType = MET_SHORT +ElementNumberOfChannels = 1 +ElementDataFile = Case9Pack/Images/case9_T90.img diff --git a/data/DirLab-4DCT/README.md b/data/DirLab-4DCT/README.md new file mode 100644 index 0000000..be5199d --- /dev/null +++ b/data/DirLab-4DCT/README.md @@ -0,0 +1,114 @@ +# DirLab-4DCT + +## Download + +**Manual download required** — this dataset is not fetched by +`physiotwin4d-download-data`; there is no automatic downloader because +DIR-Lab distributes each case individually and may require registration. + +1. Visit the DIR-Lab 4D-CT page and request/download the case archives: + https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/downloads-and-reference-data/4dct.html +2. Extract each case's raw images into `data/DirLab-4DCT/` (see layout + below). The `.mhd` headers already committed in this directory point at + those raw files, so no conversion step is required — see + "About the Committed `.mhd` Files" below. + +Once populated, check the layout with: + +```python +from physiotwin4d import DataDownloadTools + +assert DataDownloadTools.VerifyDirLab4DCTData("data/DirLab-4DCT") +``` + +**Directory structure after download:** +``` +data/DirLab-4DCT/ +├── Case1Pack/ +│ ├── Images/ # T00-T90 phase images +│ ├── ExtremePhases/ # T00 and T50 (max inhale/exhale) +│ └── Sampled4D/ # Sampled time points +├── Case1Pack_T00.mhd # Already-committed headers, per phase +├── Case1Pack_T10.mhd +... +├── Case10Pack/ +├── Convert4DCTToMHD.py # Documents how the .mhd headers were generated +└── README.md (this file) +``` + +### About the Committed `.mhd` Files + +The `Case*Pack_T*.mhd` files in this directory are already committed to the +repository, but they are only MetaImage **headers** (a few hundred bytes +each) — for example: + +``` +ObjectType = Image +NDims = 3 +DimSize = 256 256 94 +... +ElementDataFile = Case1Pack/Images/case1_T00_s.img +``` + +Each header's `ElementDataFile` points at the raw image data inside the +corresponding `Case*Pack/`/`Case*Deploy/` subdirectory (`.gitignore`d +because those raw volumes are large). These `.mhd` files will not load +until you complete the manual download above and the referenced +`Case*Pack/Images/*.img` files exist alongside them. + +`Convert4DCTToMHD.py` is what originally generated these headers from the +raw DIR-Lab archives. It is included for provenance/documentation only — +you do not need to run it; the `.mhd` files are already committed. + +## Overview + +Benchmark dataset for 4D CT respiratory motion analysis. Contains 10 cases +of lung CT scans at different respiratory phases with annotated landmark +points for registration validation. + +### Dataset Details + +- **Format**: `.mhd`/`.raw` (MetaImage format) +- **Cases**: 10 patient cases (Case 1-10) +- **Phases**: 10 respiratory phases per case (T00-T90) +- **Content**: Non-contrast lung CT +- **Anatomy**: Lungs, airways, thoracic structures +- **Landmarks**: 300+ annotated points per case for registration validation + +### Acknowledgement + +Data provided by the DIR-Lab at MD Anderson Cancer Center / Emory +University: +https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/ + +### Citation + +Dataset: https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/downloads-and-reference-data/4dct.html + +If you use this dataset, please cite: + +- Case numbers 4DCT1-4DCT5: Castillo R, Castillo E, Guerra R, Johnson VE, + McPhail T, Garg AK, Guerrero T. 2009. "A framework for evaluation of + deformable image registration spatial accuracy using large landmark + point sets." *Phys Med Biol* 54:1849-1870. +- Case numbers 4DCT6-4DCT10: Castillo E, Castillo R, Martinez J, Shenoy M, + Guerrero T. 2009. "Four-dimensional deformable image registration using + trajectory modeling." *Phys Med Biol* 55:305-327. + +## Using This Dataset + +- Primary dataset for `experiments/Lung-GatedCT_To_USD/` +- Registration algorithm validation +- Respiratory motion analysis +- Benchmark for deformable registration accuracy + +### Files in This Directory + +- `Convert4DCTToMHD.py` — documents how the committed `.mhd` headers were + generated from the raw DIR-Lab archives; not needed to run +- `Case*Pack_T*.mhd` — MetaImage headers for each case/phase (see above) + +### Additional Resources + +- Reference implementation for reading DIR-Lab's raw `.img` case files: + https://github.com/hsokooti/RegNet/blob/master/functions/preprocessing/dirlab.py diff --git a/data/DirLab-4DCT/citations.txt b/data/DirLab-4DCT/citations.txt deleted file mode 100644 index 9cd5543..0000000 --- a/data/DirLab-4DCT/citations.txt +++ /dev/null @@ -1,7 +0,0 @@ -https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/downloads-and-reference-data/4dct.html - -Case numbers 4DCT1-4DCT5 -Castillo R, Castillo E, Guerra R, Johnson VE, McPhail T, Garg AK, Guerrero T. 2009. A framework for evaluation of deformable image registration spatial accuracy using large landmark point sets. Phys Med Biol 54 1849-1870. - -Case numbers 4DCT6-4DCT10 -Castillo E, Castillo R, Martinez J, Shenoy M, Guerrero T. 2009. Four-dimensional deformable image registration using trajectory modeling. Phys Med Biol 55 305-327. diff --git a/data/DirLab-4DCT/reading_img_files.url b/data/DirLab-4DCT/reading_img_files.url deleted file mode 100644 index d3ebb0d..0000000 --- a/data/DirLab-4DCT/reading_img_files.url +++ /dev/null @@ -1,2 +0,0 @@ -[InternetShortcut] -URL=https://github.com/hsokooti/RegNet/blob/master/functions/preprocessing/dirlab.py diff --git a/data/KCL-Heart-Model/README.md b/data/KCL-Heart-Model/README.md index bf77556..17e1bd6 100644 --- a/data/KCL-Heart-Model/README.md +++ b/data/KCL-Heart-Model/README.md @@ -1,17 +1,19 @@ # KCL Heart Model Dataset -## Manual Download Required +## Download -**This data is NOT automatically downloaded.** Users must manually download the dataset from Zenodo (see below). +Download this dataset automatically with: -### Required Files - -Download the following files from the Zenodo repository and place them in this directory: +```bash +physiotwin4d-download-data KCL-Heart-Model --directory data/KCL-Heart-Model +``` -1. **`average_mesh.vtk`** - Mean heart volume mesh (place in this directory) -2. **20 individual heart meshes** - Files numbered `01.vtk` through `20.vtk` - - Create an `input_meshes/` subdirectory in this folder - - Place all 20 numbered vtk files into `input_meshes/` +This downloads and extracts the 20 individual heart meshes from +[zenodo.org/records/4590294](https://zenodo.org/records/4590294) (`01.tar.gz` +through `20.tar.gz`, ~70-100 MB each, ~1.8 GB total) and the average heart +mesh from [zenodo.org/records/4593739](https://zenodo.org/records/4593739) +(`average.tar.gz`, ~55 MB). Already-extracted files are reused, so +re-running the command resumes an interrupted download. **Directory structure after download:** ``` @@ -44,6 +46,8 @@ The KCL heart model data is publicly available through Zenodo: ### Citation +Dataset: https://zenodo.org/records/4590294 + If you use this 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) @@ -67,7 +71,6 @@ This experiment demonstrates the complete workflow for: ### Files in This Directory -- `citation.txt`: Citation information for the dataset - Other files: Generated by the `Heart-Create_Statistical_Model` experiment workflow ## Additional Resources diff --git a/data/KCL-Heart-Model/citation.txt b/data/KCL-Heart-Model/citation.txt deleted file mode 100644 index 76684f8..0000000 --- a/data/KCL-Heart-Model/citation.txt +++ /dev/null @@ -1 +0,0 @@ -https://zenodo.org/records/4590294 \ No newline at end of file diff --git a/data/README.md b/data/README.md index 4e5028c..dcfa81e 100644 --- a/data/README.md +++ b/data/README.md @@ -1,75 +1,44 @@ # PhysioTwin4D Data Directory -This directory contains sample datasets used for experiments, testing, and development of the PhysioTwin4D library. Each subdirectory contains a specific medical imaging dataset. +This directory holds the example datasets used by PhysioTwin4D's tutorials, +experiments, and tests. Each subdirectory is one dataset and has its own +`README.md` with the download URL(s), directory layout, data sizes, and the +exact `physiotwin4d-download-data` command for that dataset. This file is +just the map — see the subdirectory README before downloading anything. -## Directory Structure +## Datasets -``` -data/ -├── Slicer-Heart-CT/ # 4D cardiac CT with gated cardiac phases (AUTO-DOWNLOAD) -├── DirLab-4DCT/ # 4D lung CT benchmark dataset (MANUAL) -├── KCL-Heart-Model/ # Statistical shape model of the heart (MANUAL) -├── CHOP-Valve4D/ # 4D valve models (MANUAL) -``` - -## Data Download Methods - -### Automatic Download (Only Slicer-Heart-CT) -Only the **Slicer-Heart-CT** dataset can be automatically downloaded by running the appropriate script. - -### Manual Download (All Others) -The following datasets must be **manually downloaded and preprocessed** by the user: -- **DirLab-4DCT**: Respiratory motion benchmark data -- **KCL-Heart-Model**: Statistical cardiac shape models -- **CHOP-Valve4D**: Time-varying valve reconstructions +| Directory | Data Type | Provided By | Auto-Download | +| ----------------------------------- | ------------------------------------------------------- | --------------------------------------- | ------------- | +| [`Slicer-Heart-CT/`](Slicer-Heart-CT/README.md) | 4D gated cardiac CT (`.seq.nrrd`) | Jolley Lab (CHOP) / SlicerHeart on GitHub | Yes | +| [`DirLab-4DCT/`](DirLab-4DCT/README.md) | 4D lung CT respiratory benchmark + landmarks (`.mhd/.raw`) | DIR-Lab (Emory / MD Anderson) | No — manual, may require registration | +| [`KCL-Heart-Model/`](KCL-Heart-Model/README.md) | Statistical heart shape model meshes (`.vtk`) | King's College London, via Zenodo | Yes | +| [`CHOP-Valve4D/`](CHOP-Valve4D/README.md) | FEBio valve FE model, converted to VTK/ITK + Simpleware segmentation | Jolley Lab (CHOP), converted by PhysioTwin4D | Yes | -See individual dataset sections below for download instructions and preprocessing requirements. +[`data/test/`](test/README.md) is not a dataset — it is a cache +automatically managed by the pytest infrastructure (mainly from +`Slicer-Heart-CT`) and is not used by the workflows, tutorials, or CLIs. ---- +## Downloading Data -## Slicer-Heart-CT AUTO-DOWNLOAD +Datasets marked **Yes** above can be fetched with the +`physiotwin4d-download-data` CLI (or the corresponding +`DataDownloadTools.Download*` method), for example: -### Description -4D cardiac CT dataset with temporal gating showing complete cardiac cycle motion. Pediatric cardiac CT with truncal valve visualization. - -### Specifications -- **Format**: `.seq.nrrd` (4D NRRD sequence file) -- **Phases**: 21 temporal cardiac phases -- **Size**: ~1.2 GB -- **Content**: Contrast-enhanced cardiac CT -- **Anatomy**: Heart, great vessels, thoracic structures - -### Acknowledgement -Data provided by Jolley Lab at CHOP (Children's Hospital of Philadelphia): -- https://www.linkedin.com/company/jolleylab -- https://github.com/Slicer-Heart-CT/Slicer-Heart-CT - -### Downloading the Data - -**Automatic download** (recommended): -```python -from physiotwin4d import DataDownloadTools - -DataDownloadTools.DownloadSlicerHeartCTData("data/Slicer-Heart-CT") -assert DataDownloadTools.VerifySlicerHeartCTData("data/Slicer-Heart-CT") -``` - -**Manual download** (alternative): ```bash -# Direct download link: -wget https://github.com/Slicer-Heart-CT/Slicer-Heart-CT/releases/download/TestingData/TruncalValve_4DCT.seq.nrrd -P data/Slicer-Heart-CT/ +physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT +physiotwin4d-download-data KCL-Heart-Model --directory data/KCL-Heart-Model +physiotwin4d-download-data CHOP-Valve4D --directory data/CHOP-Valve4D ``` -### Usage -- Primary dataset for tutorials -- Primary dataset for `Heart-GatedCT_To_USD` experiments -- Used in test suite (`tests/test_download_heart_data.py`) -- Example data for cardiac motion visualization in Omniverse +Datasets marked **No** must be downloaded by hand following the +instructions in that dataset's own README (`DirLab-4DCT/README.md`). ### Verification Helpers -PhysioTwin4D exposes a small public utility for checking optional dataset -layouts: +Each dataset also has a `DataDownloadTools.Verify*Data()` helper that checks +whether the expected files are already present, so scripts can skip +downloading when the data is cached: ```python from physiotwin4d import DataDownloadTools @@ -80,243 +49,13 @@ DataDownloadTools.VerifyKCLHeartModelData("data/KCL-Heart-Model") DataDownloadTools.VerifyCHOPValve4DData("data/CHOP-Valve4D") ``` ---- - -## DirLab-4DCT MANUAL DOWNLOAD - -### Description -Benchmark dataset for 4D CT respiratory motion analysis. Contains 10 cases of lung CT scans at different respiratory phases with annotated landmark points for registration validation. - -### Specifications -- **Format**: `.mhd/.raw` (MetaImage format) -- **Cases**: 10 patient cases (Case 1-10) -- **Phases**: 10 respiratory phases per case (T00-T90) -- **Content**: Non-contrast lung CT -- **Anatomy**: Lungs, airways, thoracic structures -- **Landmarks**: 300+ annotated points per case for validation - -### Acknowledgement -Data provided by the DIR-Lab at MD Anderson Cancer Center: -- **Project**: COPDGene 4D-CT dataset -- **Publication**: Castillo et al., "A reference dataset for deformable image registration spatial accuracy evaluation using the COPDGene study archive" -- **Website**: https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/index.html - -### Downloading the Data - -**MANUAL DOWNLOAD REQUIRED** - -Users must manually download and preprocess this dataset. Follow these steps: - -**Step 1: Manual Download** -```python -# Using provided utilities in experiment scripts. -# See: experiments/Lung-GatedCT_To_USD/0-register_dirlab_4dct.py -# The script includes download utilities but requires manual execution. -``` - -**Step 2: User Preprocessing** -Users are responsible for: -- Downloading data from DIR-Lab website -- Extracting and organizing files in the proper directory structure -- Running preprocessing scripts if needed - -### Directory Structure -``` -DirLab-4DCT/ -├── Case1Pack/ -│ ├── Images/ # T00-T50 phase images -│ ├── ExtremePhases/ # T00 and T50 (max inhale/exhale) -│ └── Sampled4D/ # Sampled time points -├── Case1Pack_T00.mhd # Extracted phase files -├── Case1Pack_T10.mhd -... -├── Case10Pack/ -└── dirlab_password.txt # Password for access (if needed) -``` - -### Usage -- Primary dataset for `Lung-GatedCT_To_USD` experiments -- Registration algorithm validation -- Respiratory motion analysis -- Benchmark for deformable registration accuracy - ---- - -## KCL-Heart-Model MANUAL DOWNLOAD - -### Description -Statistical shape model (SSM) of the human heart derived from cardiac imaging data. Includes principal component analysis (PCA) modes of shape variation. - -### Specifications -- **Format**: `.vtk`, `.vtp` (VTK PolyData formats) -- **Content**: - - Average heart surface and mesh - - Individual heart models - - PCA eigenvectors - - Mode standard deviations - - Variance explained by each mode -- **Components**: Full heart mesh with chambers and vessels - -### Files -- `average_surface.vtp` - Mean heart surface (PolyData) -- `average_mesh.vtk` - Mean heart volume mesh (UnstructuredGrid) -- `Full_Heart_Mesh_1.vtk` - Example individual heart -- `Eigenvectors.csv` - PCA eigenvectors (shape modes) -- `Mode_standard_deviations.csv` - Standard deviation for each mode -- `Normalized_explained_variance.csv` - Variance explained by each PCA mode -- `data_description.pdf` - Detailed model documentation -- `publication.pdf` - Associated research publication -- `citation.txt` - Citation information - -### Acknowledgement -Data from King's College London (KCL): -- **Repository**: Cardiac imaging research group -- **License**: Check `citation.txt` for proper attribution - -### Downloading the Data - -**MANUAL DOWNLOAD REQUIRED** - -Users must manually obtain and place this data: -1. Obtain data from published research repositories or contact authors -2. Place files in `data/KCL-Heart-Model/` directory -3. Required files: `average_surface.vtp`, `average_mesh.vtk` - -Check the included PDFs (if available) for source information and proper citation. - -### Usage -- **Statistical shape model creation** (`experiments/Heart-Create_Statistical_Model/`) ⭐ **Primary use case** -- **Model-to-patient registration** (`experiments/Heart-Statistical_Model_To_Patient/`) -- VTK to USD conversion experiments (`experiments/Convert_VTK_To_USD/`) -- Shape-based cardiac analysis -- Atlas-based segmentation initialization -- Population-based statistical analysis - ---- - -## CHOP-Valve4D MANUAL DOWNLOAD - -### Description -Time-varying 4D valve reconstruction models showing valve motion over the cardiac cycle. These datasets represent dynamic valve geometries reconstructed from medical imaging data. - -### Specifications -- **Format**: `.vtk` (VTK PolyData files) -- **Content**: Time series of valve surface meshes -- **Valves**: Alterra, TPV25, and other valve types -- **Phases**: Multiple time points per cardiac cycle (200+ frames) -- **Resolution**: High-resolution surface meshes with anatomical features - -### Directory Structure -``` -CHOP-Valve4D/ -├── Alterra/ -│ ├── frame_0000.vtk -│ ├── frame_0001.vtk -│ └── ... (232 frames) -├── TPV25/ -│ ├── frame_0000.vtk -│ ├── frame_0001.vtk -│ └── ... (265 frames) -``` - -### Acknowledgement -Data provided by Jolley Lab at CHOP (Children's Hospital of Philadelphia): -- https://www.linkedin.com/company/jolleylab - -### Downloading the Data - -**MANUAL DOWNLOAD REQUIRED** - -**Availability**: This dataset will soon be publicly available for download from the **FEBio website** under the **Creative Commons Attribution (CC-BY) license**. - -- **Source**: https://febio.org/ (coming soon) -- **License**: CC-BY (Creative Commons Attribution) -- **Citation**: Please cite the Jolley Lab and FEBio when using this data - -**Setup Instructions**: -1. Download valve reconstruction data from FEBio website when available -2. Place files in `data/CHOP-Valve4D/` with proper subdirectory structure -3. Ensure files are named sequentially for time-series processing (e.g., `frame_0000.vtk`, `frame_0001.vtk`, ...) -4. Organize by valve type in subdirectories (e.g., `Alterra/`, `TPV25/`) - -### Usage -- Time-series VTK to USD conversion (`experiments/Convert_VTK_To_USD/`) -- 4D valve motion visualization in NVIDIA Omniverse -- Temporal cardiac mechanics analysis -- Valve dynamics studies and surgical planning - -### Related Resources -- **FEBio**: Finite Element Biomechanics software suite (https://febio.org/) -- **Jolley Lab**: Cardiac imaging and computational modeling research - ---- - -## Data Usage Guidelines - -### For Testing -- Tests automatically use cached data when available -- Download occurs only if data is missing -- Tests use subsets (e.g., first 2 time points) for speed - -### For Experiments -- Full datasets used for complete analysis -- Results saved to respective `experiments/*/results/` directories -- Original data remains unmodified - -### For Development -- Use small subsets for rapid iteration -- Full datasets for validation and benchmarking -- Cache intermediate results to avoid reprocessing - ---- - -## Data Access and Licensing - -- **Slicer-Heart-CT** : Public release from GitHub (auto-download available) -- **DirLab-4DCT** : Public benchmark dataset (manual download required, may require registration) -- **KCL-Heart-Model** : Requires manual download from research repositories -- **CHOP-Valve4D** : Soon available from FEBio website under CC-BY license (manual download) - -**Important**: Always cite the original data sources in publications and respect any usage restrictions. - -### Summary of Download Methods - -| Dataset | Auto-Download | Manual Required | License | Source | Used in Tests | -| --------------- | ------------- | --------------- | --------------- | ------------------- | ------------------------ | -| Slicer-Heart-CT | Yes | No | Public | GitHub | Yes | -| DirLab-4DCT | No | Yes | Public/Academic | DIR-Lab | No | -| KCL-Heart-Model | No | Yes | Check citation | Zenodo/KCL | Yes (skipped if missing) | -| CHOP-Valve4D | No | Yes | CC-BY | FEBio (coming soon) | No | - ---- - -## References - -### Slicer-Heart-CT -- Jolley Lab: https://www.linkedin.com/company/jolleylab -- GitHub: https://github.com/Slicer-Heart-CT/Slicer-Heart-CT - -### DirLab-4DCT -- Castillo et al., "A reference dataset for deformable image registration spatial accuracy evaluation using the COPDGene study archive" -- DIR-Lab: https://med.emory.edu/departments/radiation-oncology/research-laboratories/deformable-image-registration/ - -### KCL-Heart-Model -- 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) -- Zenodo: https://zenodo.org/records/4590294 - -### CHOP-Valve4D -- Jolley Lab (CHOP): https://www.linkedin.com/company/jolleylab -- FEBio Project: https://febio.org/ (dataset coming soon) -- License: Creative Commons Attribution (CC-BY) -- Citation: Please acknowledge Jolley Lab at CHOP and the FEBio Project - ---- - -## Tips +## Guidelines -1. **Storage**: Ensure adequate disk space (~10-20GB for all datasets) -2. **Download Time**: Initial downloads can be slow; be patient -3. **Organization**: Keep data organized; don't modify original files -4. **Backups**: Consider backing up processed results separately -5. **Documentation**: Update this README when adding new datasets +- Keep original downloaded data unmodified; write experiment/tutorial + results under `experiments/*/results/` or `tutorials/*/results/`, not + back into `data/`. +- Always respect the license and citation requirements of each dataset — + see the "Citation" / "Acknowledgement" section in the relevant + subdirectory README. +- If you add a new dataset, add both a row above and a subdirectory + `README.md` following the existing datasets as a template. diff --git a/data/Slicer-Heart-CT/README.md b/data/Slicer-Heart-CT/README.md new file mode 100644 index 0000000..e184120 --- /dev/null +++ b/data/Slicer-Heart-CT/README.md @@ -0,0 +1,64 @@ +# Slicer-Heart-CT + +## Download + +Download this dataset automatically with: + +```bash +physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT +``` + +or from Python: + +```python +from physiotwin4d import DataDownloadTools + +data_file = DataDownloadTools.DownloadSlicerHeartCTData("data/Slicer-Heart-CT") +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. + +**Directory structure after download:** +``` +data/Slicer-Heart-CT/ +├── TruncalValve_4DCT.seq.nrrd +└── README.md (this file) +``` + +## Overview + +4D cardiac CT dataset with temporal gating showing a complete cardiac +cycle. Pediatric cardiac CT with truncal valve visualization. + +### Dataset Details + +- **Format**: `.seq.nrrd` (4D NRRD sequence file) +- **Phases**: 21 temporal cardiac phases +- **Size**: ~1.2 GB +- **Content**: Contrast-enhanced cardiac CT +- **Anatomy**: Heart, great vessels, thoracic structures + +### Acknowledgement + +Data provided by the Jolley Lab at CHOP (Children's Hospital of +Philadelphia): +- https://www.linkedin.com/company/jolleylab +- https://github.com/SlicerHeart/SlicerHeart + +## Using This Dataset + +- 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` +- 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 diff --git a/data/Slicer-Heart-CT/download_and_convert.py b/data/Slicer-Heart-CT/download_and_convert.py deleted file mode 100644 index 535ba19..0000000 --- a/data/Slicer-Heart-CT/download_and_convert.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python -# %% -import os -import shutil - -from physiotwin4d.convert_image_4d_to_3d import ConvertImage4DTo3D -from physiotwin4d.data_download_tools import DataDownloadTools - -# %% -data_dir = "." -output_dir = "." - -if not os.path.exists(data_dir): - os.makedirs(data_dir, exist_ok=True) - -if not os.path.exists(output_dir): - os.makedirs(output_dir, exist_ok=True) - -input_image_filename = DataDownloadTools.DownloadSlicerHeartCTData(data_dir) - -# %% -conv = ConvertImage4DTo3D() -conv.load_image_4d(str(input_image_filename)) -conv.save_3d_images(output_dir, "slice") - -# Save the mid-stroke slice as the fixed/reference image -shutil.copyfile(f"{output_dir}/slice_007.mha", f"{output_dir}/slice_fixed.mha") diff --git a/data/test/README.md b/data/test/README.md new file mode 100644 index 0000000..45eab90 --- /dev/null +++ b/data/test/README.md @@ -0,0 +1,32 @@ +# data/test + +This directory is **automatically managed by the pytest infrastructure** +(`tests/conftest.py`) — it is a cache, not a dataset you download or +maintain by hand. It holds data used to run the unit test suite. + +This data is **not** used by the workflows, tutorials, or CLIs of the +PhysioTwin4D library; those consume the datasets documented in +[`data/README.md`](../README.md) instead. + +## What Lives Here + +- `slicer_heart/` — a cached copy of the `Slicer-Heart-CT` 4D CT sequence + (see the `download_test_data` fixture), split into per-phase `.mha` + slices by `test_download_heart_data.py`. +- `slicer_heart_small/` — the same phases downsampled to 1.5x1.5x1.5 mm, + used by tests that need a smaller/faster image (labelmaps and + transforms computed from this data are cached here too). + +Both subdirectories are created on demand by `tests/conftest.py` fixtures +the first time a test needs them, and are `.gitignore`d — do not commit +their contents. + +## Regenerating + +If this directory is deleted or corrupted, simply re-run the test suite; +the fixtures in `tests/conftest.py` will re-download and rebuild everything +here automatically: + +```bash +py -m pytest tests/ -v +``` diff --git a/docs/cli_scripts/download_data.rst b/docs/cli_scripts/download_data.rst index 862fad9..36d233f 100644 --- a/docs/cli_scripts/download_data.rst +++ b/docs/cli_scripts/download_data.rst @@ -15,8 +15,15 @@ Supported Datasets * - Data name - Description * - ``Slicer-Heart-CT`` - - Public 4D cardiac CT sample from SlicerHeart. This is currently the - only dataset downloaded automatically by PhysioTwin4D. + - Public 4D cardiac CT sample from SlicerHeart. + * - ``KCL-Heart-Model`` + - King's College London four-chamber heart model dataset: 20 + individual heart meshes plus an average mesh, from Zenodo. + * - ``CHOP-Valve4D`` + - CHOP Jolley Lab transcatheter pulmonary valve model, converted from + the original FEBio model to VTK/ITK and segmented with Simpleware, + from the PhysioTwin4D GitHub release. See + ``data/CHOP-Valve4D/README.md``. Basic Usage =========== @@ -39,14 +46,14 @@ Options .. code-block:: bash - physiotwin4d-download-data [Slicer-Heart-CT] [--directory DIRECTORY] + physiotwin4d-download-data [Slicer-Heart-CT|KCL-Heart-Model|CHOP-Valve4D] [--directory DIRECTORY] ``data_name`` - Dataset to download. The only accepted value is ``Slicer-Heart-CT``. + Dataset to download. One of ``Slicer-Heart-CT``, ``KCL-Heart-Model``, or + ``CHOP-Valve4D``. ``--directory`` - Directory where the dataset is stored. Defaults to - ``data/Slicer-Heart-CT``. + Directory where the dataset is stored. Defaults to ``data/``. Output ====== @@ -61,6 +68,31 @@ The command uses :meth:`physiotwin4d.data_download_tools.DataDownloadTools.DownloadSlicerHeartCTData`, so repeated runs reuse the existing non-empty file. +For ``KCL-Heart-Model``, the command downloads, extracts, and reuses: + +.. code-block:: text + + data/KCL-Heart-Model/average_mesh.vtk + data/KCL-Heart-Model/input_meshes/01.vtk ... 20.vtk + +The command uses +:meth:`physiotwin4d.data_download_tools.DataDownloadTools.DownloadKCLHeartModelData`, +which fetches each per-model ``.tar.gz`` archive from Zenodo, extracts its +mesh, and skips archives whose target ``.vtk`` file is already present. + +For ``CHOP-Valve4D``, the command downloads, extracts, and reuses: + +.. code-block:: text + + data/CHOP-Valve4D/Alterra/ (valve mesh time series, >1 GB) + data/CHOP-Valve4D/TPV25/ (valve mesh time series, >1 GB) + data/CHOP-Valve4D/CT/ (source CT volume and Simpleware segmentation) + +The command uses +:meth:`physiotwin4d.data_download_tools.DataDownloadTools.DownloadCHOPValve4DData`, +which fetches each subdirectory's zip archive from the PhysioTwin4D GitHub +release and skips any subdirectory that already contains files. + See Also ======== diff --git a/docs/tutorials.rst b/docs/tutorials.rst index d41936e..bc63d43 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -214,7 +214,7 @@ Workflow ``WorkflowCreateStatisticalModel`` Dataset - KCL-Heart-Model, downloaded manually. + KCL-Heart-Model, downloaded via ``physiotwin4d-download-data``. Preview .. figure:: assets/example.gif @@ -260,7 +260,7 @@ Workflow ``WorkflowFitStatisticalModelToPatient`` Dataset - KCL-Heart-Model, downloaded manually. + KCL-Heart-Model, downloaded via ``physiotwin4d-download-data``. Preview .. figure:: assets/example.gif diff --git a/experiments/Heart-Create_Statistical_Model/README.md b/experiments/Heart-Create_Statistical_Model/README.md index 4e2c7bf..1ea3a03 100644 --- a/experiments/Heart-Create_Statistical_Model/README.md +++ b/experiments/Heart-Create_Statistical_Model/README.md @@ -17,13 +17,17 @@ This experiment uses the **King's College London (KCL) four-chamber heart model The experiment requires the **KCL-Heart-Model** dataset located in `data/KCL-Heart-Model/`. -### Manual Download Required +### Download -The KCL dataset is NOT automatically downloaded. You must manually download it from: +Download it automatically with: -**[Virtual cohort of adult healthy four-chamber heart meshes from CT images](https://zenodo.org/records/4590294)** +```bash +physiotwin4d-download-data KCL-Heart-Model --directory data/KCL-Heart-Model +``` -See `data/KCL-Heart-Model/README.md` for complete download and setup instructions. +The data comes from +**[Virtual cohort of adult healthy four-chamber heart meshes from CT images](https://zenodo.org/records/4590294)**. +See `data/KCL-Heart-Model/README.md` for complete details. ### Required Files diff --git a/src/physiotwin4d/cli/download_data.py b/src/physiotwin4d/cli/download_data.py index 952c82f..116d74c 100644 --- a/src/physiotwin4d/cli/download_data.py +++ b/src/physiotwin4d/cli/download_data.py @@ -11,6 +11,8 @@ from ..data_download_tools import DataDownloadTools SLICER_HEART_CT = "Slicer-Heart-CT" +KCL_HEART_MODEL = "KCL-Heart-Model" +CHOP_VALVE4D = "CHOP-Valve4D" def main(argv: Optional[list[str]] = None) -> int: @@ -22,29 +24,42 @@ def main(argv: Optional[list[str]] = None) -> int: Examples: %(prog)s %(prog)s {SLICER_HEART_CT} --directory data/Slicer-Heart-CT + %(prog)s {KCL_HEART_MODEL} --directory data/KCL-Heart-Model + %(prog)s {CHOP_VALVE4D} --directory data/CHOP-Valve4D """, ) parser.add_argument( "data_name", nargs="?", - choices=[SLICER_HEART_CT], + choices=[SLICER_HEART_CT, KCL_HEART_MODEL, CHOP_VALVE4D], default=SLICER_HEART_CT, help=f"Dataset to download (default: {SLICER_HEART_CT})", ) parser.add_argument( "--directory", - default=f"data/{SLICER_HEART_CT}", - help=f"Directory where data will be stored (default: data/{SLICER_HEART_CT})", + default=None, + help="Directory where data will be stored (default: data/)", ) args = parser.parse_args(argv) - output_dir = Path(args.directory) + directory = args.directory or f"data/{args.data_name}" + output_dir = Path(directory) if args.data_name == SLICER_HEART_CT: data_file = DataDownloadTools.DownloadSlicerHeartCTData(output_dir) print(f"Downloaded {SLICER_HEART_CT} to: {data_file}") return 0 + if args.data_name == KCL_HEART_MODEL: + data_dir = DataDownloadTools.DownloadKCLHeartModelData(output_dir) + print(f"Downloaded {KCL_HEART_MODEL} to: {data_dir}") + return 0 + + if args.data_name == CHOP_VALVE4D: + data_dir = DataDownloadTools.DownloadCHOPValve4DData(output_dir) + print(f"Downloaded {CHOP_VALVE4D} to: {data_dir}") + return 0 + parser.error(f"Unsupported dataset: {args.data_name}") diff --git a/src/physiotwin4d/data_download_tools.py b/src/physiotwin4d/data_download_tools.py index 07f5cb0..4fc20e2 100644 --- a/src/physiotwin4d/data_download_tools.py +++ b/src/physiotwin4d/data_download_tools.py @@ -1,16 +1,19 @@ """ Dataset download and verification helpers. -Only Slicer-Heart-CT is downloaded automatically. Other datasets require -manual download, and the verification helpers check the file layouts used by -the repository tutorials, experiments, and tests. +Slicer-Heart-CT, KCL-Heart-Model, and CHOP-Valve4D are downloaded +automatically. Other datasets require manual download, and the verification +helpers check the file layouts used by the repository tutorials, +experiments, and tests. """ from __future__ import annotations import shutil +import tarfile import tempfile import urllib.request +import zipfile from pathlib import Path from typing import Union @@ -81,6 +84,149 @@ def VerifySlicerHeartCTData(dirname: Union[str, Path]) -> bool: # noqa: N802 """Return True when Slicer-Heart-CT has the expected 4-D CT file.""" return (Path(dirname) / DataDownloadTools.SLICER_HEART_CT_FILENAME).is_file() + KCL_HEART_MODEL_MESH_COUNT = 20 + KCL_HEART_MODEL_INDIVIDUAL_URL_TEMPLATE = ( + "https://zenodo.org/records/4590294/files/{index:02d}.tar.gz?download=1" + ) + KCL_HEART_MODEL_AVERAGE_URL = ( + "https://zenodo.org/records/4593739/files/average.tar.gz?download=1" + ) + + @staticmethod + def DownloadKCLHeartModelData(dirname: Union[str, Path]) -> Path: # noqa: N802 + """Download the KCL-Heart-Model dataset into ``dirname``. + + Downloads and extracts the 20 individual four-chamber heart meshes + from https://zenodo.org/records/4590294 into + ``dirname/input_meshes/01.vtk`` through ``20.vtk``, and the average + heart mesh from https://zenodo.org/records/4593739 into + ``dirname/average_mesh.vtk``. Already-extracted files are reused. + + Args: + dirname: Directory where the KCL-Heart-Model dataset should live. + + Returns: + Path to ``dirname``. + """ + data_dir = Path(dirname) + input_meshes_dir = data_dir / "input_meshes" + input_meshes_dir.mkdir(parents=True, exist_ok=True) + + for index in range(1, DataDownloadTools.KCL_HEART_MODEL_MESH_COUNT + 1): + target_file = input_meshes_dir / f"{index:02d}.vtk" + if target_file.exists() and target_file.stat().st_size > 0: + continue + url = DataDownloadTools.KCL_HEART_MODEL_INDIVIDUAL_URL_TEMPLATE.format( + index=index + ) + DataDownloadTools._DownloadAndExtractTarMember( + url, member_name=f"{index:02d}.vtk", target_file=target_file + ) + + average_file = data_dir / "average_mesh.vtk" + if not (average_file.exists() and average_file.stat().st_size > 0): + DataDownloadTools._DownloadAndExtractTarMember( + DataDownloadTools.KCL_HEART_MODEL_AVERAGE_URL, + member_name="average.vtk", + target_file=average_file, + ) + + return data_dir + + @staticmethod + def _DownloadAndExtractTarMember( # noqa: N802 + url: str, member_name: str, target_file: Path + ) -> None: + """Download a ``.tar.gz`` archive and extract one member to ``target_file``.""" + target_file.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(dir=str(target_file.parent)) as tmp_dir_name: + tmp_dir = Path(tmp_dir_name) + archive_file = tmp_dir / "archive.tar.gz" + with ( + urllib.request.urlopen( # noqa: S310 + url, timeout=_DOWNLOAD_TIMEOUT_SECONDS + ) as response, + open(archive_file, "wb") as out, + ): + shutil.copyfileobj(response, out) + if archive_file.stat().st_size == 0: + raise RuntimeError(f"Downloaded archive is empty: {url}") + + with tarfile.open(archive_file) as tar: + try: + tar.extract(member_name, path=tmp_dir, filter="data") + except TypeError: + # Python < 3.12 (without the PEP 706 backport) does not + # accept the ``filter`` keyword argument. + tar.extract(member_name, path=tmp_dir) + extracted_file = tmp_dir / member_name + if not extracted_file.is_file(): + raise RuntimeError( + f"Expected member {member_name!r} not found in archive: {url}" + ) + extracted_file.replace(target_file) + + CHOP_VALVE4D_RELEASE_URL = ( + "https://github.com/Project-MONAI/physiotwin4d/releases/download/2026.07.1/" + ) + # subdirectory name -> release asset filename. Alterra and TPV25 are + # each >1 GB (per-frame valve mesh time series); CT is the smaller + # image/segmentation bundle. See data/CHOP-Valve4D/README.md. + CHOP_VALVE4D_ASSETS = { + "Alterra": "CHOP-Valve4D-Alterra.zip", + "CT": "CHOP-Valve4D-CT.zip", + "TPV25": "CHOP-Valve4D-TPV25.zip", + } + + @staticmethod + def DownloadCHOPValve4DData(dirname: Union[str, Path]) -> Path: # noqa: N802 + """Download the CHOP-Valve4D convenience release into ``dirname``. + + Downloads the three zip archives attached to the PhysioTwin4D + 2026.07.1 GitHub release + (https://github.com/Project-MONAI/physiotwin4d/releases/tag/2026.07.1) + and extracts each into its matching subdirectory: ``Alterra/`` and + ``TPV25/`` (valve mesh time series, >1 GB each) and ``CT/`` (source + CT volume and Simpleware segmentation). A subdirectory that already + has any files is left alone, so re-running resumes an interrupted + download. See ``data/CHOP-Valve4D/README.md`` for what this + converted data contains and how it relates to the original FEBio + source model. + + Args: + dirname: Directory where the CHOP-Valve4D dataset should live. + + Returns: + Path to ``dirname``. + """ + data_dir = Path(dirname) + for subdir_name, asset_name in DataDownloadTools.CHOP_VALVE4D_ASSETS.items(): + target_dir = data_dir / subdir_name + if target_dir.is_dir() and any(target_dir.iterdir()): + continue + url = DataDownloadTools.CHOP_VALVE4D_RELEASE_URL + asset_name + DataDownloadTools._DownloadAndExtractZip(url, target_dir) + return data_dir + + @staticmethod + def _DownloadAndExtractZip(url: str, target_dir: Path) -> None: # noqa: N802 + """Stream-download a ``.zip`` archive and extract it into ``target_dir``.""" + target_dir.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(dir=str(target_dir.parent)) as tmp_dir_name: + archive_file = Path(tmp_dir_name) / "archive.zip" + with ( + urllib.request.urlopen( # noqa: S310 + url, timeout=_DOWNLOAD_TIMEOUT_SECONDS + ) as response, + open(archive_file, "wb") as out, + ): + shutil.copyfileobj(response, out, length=1024 * 1024) + if archive_file.stat().st_size == 0: + raise RuntimeError(f"Downloaded archive is empty: {url}") + + with zipfile.ZipFile(archive_file) as archive: + archive.extractall(target_dir) + @staticmethod def VerifyCHOPValve4DData(dirname: Union[str, Path]) -> bool: # noqa: N802 """Return True when CHOP-Valve4D files referenced by the repo exist. diff --git a/tests/test_download_data_cli.py b/tests/test_download_data_cli.py index 8c05e7b..d6d6333 100644 --- a/tests/test_download_data_cli.py +++ b/tests/test_download_data_cli.py @@ -48,3 +48,43 @@ def fake_download(dirname: Union[str, Path]) -> Path: assert result == 0 assert calls == [tmp_path] + + +def test_download_data_cli_routes_kcl_heart_model( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """KCL-Heart-Model 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(DataDownloadTools, "DownloadKCLHeartModelData", fake_download) + + result = download_data.main(["KCL-Heart-Model"]) + + assert result == 0 + assert calls == [Path("data/KCL-Heart-Model")] + assert "Downloaded KCL-Heart-Model" in capsys.readouterr().out + + +def test_download_data_cli_routes_chop_valve4d( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + """CHOP-Valve4D 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(DataDownloadTools, "DownloadCHOPValve4DData", fake_download) + + result = download_data.main(["CHOP-Valve4D"]) + + assert result == 0 + assert calls == [Path("data/CHOP-Valve4D")] + assert "Downloaded CHOP-Valve4D" in capsys.readouterr().out diff --git a/tests/test_download_heart_data.py b/tests/test_download_heart_data.py index 2893ddc..3405992 100644 --- a/tests/test_download_heart_data.py +++ b/tests/test_download_heart_data.py @@ -6,6 +6,9 @@ Heart-GatedCT_To_USD/0-download_and_convert_4d_to_3d.ipynb. """ +import io +import tarfile +import zipfile from pathlib import Path import pytest @@ -110,6 +113,110 @@ def test_data_downloaded( print(f"\nData file downloaded successfully: {data_file}") print(f" File size: {file_size / 1_000_000:.2f} MB") + def test_download_kcl_heart_model_data( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Each per-model and average archive is downloaded and unpacked.""" + archives_dir = tmp_path / "archives" + archives_dir.mkdir() + + def make_archive(member_name: str, content: bytes) -> Path: + archive_path = archives_dir / f"{member_name}.tar.gz" + with tarfile.open(archive_path, "w:gz") as tar: + info = tarfile.TarInfo(name=member_name) + info.size = len(content) + tar.addfile(info, io.BytesIO(content)) + return archive_path + + urls_to_archives = {} + for index in range(1, DataDownloadTools.KCL_HEART_MODEL_MESH_COUNT + 1): + url = DataDownloadTools.KCL_HEART_MODEL_INDIVIDUAL_URL_TEMPLATE.format( + index=index + ) + urls_to_archives[url] = make_archive( + f"{index:02d}.vtk", f"# vtk {index}\n".encode() + ) + urls_to_archives[DataDownloadTools.KCL_HEART_MODEL_AVERAGE_URL] = make_archive( + "average.vtk", b"# vtk average\n" + ) + + def fake_urlopen(url: str, timeout: float) -> object: + return open(urls_to_archives[url], "rb") + + monkeypatch.setattr( + "physiotwin4d.data_download_tools.urllib.request.urlopen", fake_urlopen + ) + + output_dir = tmp_path / "KCL-Heart-Model" + result_dir = DataDownloadTools.DownloadKCLHeartModelData(output_dir) + + assert result_dir == output_dir + assert (output_dir / "average_mesh.vtk").read_text() == "# vtk average\n" + for index in range(1, DataDownloadTools.KCL_HEART_MODEL_MESH_COUNT + 1): + mesh_file = output_dir / "input_meshes" / f"{index:02d}.vtk" + assert mesh_file.read_text() == f"# vtk {index}\n" + assert DataDownloadTools.VerifyKCLHeartModelData(output_dir) + + def test_download_chop_valve4d_data( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Each subdirectory's zip archive is downloaded and extracted.""" + archives_dir = tmp_path / "archives" + archives_dir.mkdir() + + def make_archive(subdir_name: str, member_name: str, content: bytes) -> Path: + archive_path = archives_dir / f"{subdir_name}.zip" + with zipfile.ZipFile(archive_path, "w") as zf: + zf.writestr(member_name, content) + return archive_path + + urls_to_archives = {} + for subdir_name, asset_name in DataDownloadTools.CHOP_VALVE4D_ASSETS.items(): + url = DataDownloadTools.CHOP_VALVE4D_RELEASE_URL + asset_name + urls_to_archives[url] = make_archive( + subdir_name, f"{subdir_name}.txt", f"# {subdir_name}\n".encode() + ) + + def fake_urlopen(url: str, timeout: float) -> object: + return open(urls_to_archives[url], "rb") + + monkeypatch.setattr( + "physiotwin4d.data_download_tools.urllib.request.urlopen", fake_urlopen + ) + + output_dir = tmp_path / "CHOP-Valve4D" + result_dir = DataDownloadTools.DownloadCHOPValve4DData(output_dir) + + assert result_dir == output_dir + for subdir_name in DataDownloadTools.CHOP_VALVE4D_ASSETS: + extracted_file = output_dir / subdir_name / f"{subdir_name}.txt" + assert extracted_file.read_text() == f"# {subdir_name}\n" + + def test_download_chop_valve4d_data_skips_populated_subdirs( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """Subdirectories that already have files are not re-downloaded.""" + output_dir = tmp_path / "CHOP-Valve4D" + for subdir_name in DataDownloadTools.CHOP_VALVE4D_ASSETS: + existing_file = output_dir / subdir_name / "already_here.txt" + existing_file.parent.mkdir(parents=True) + existing_file.write_text("cached\n") + + def fake_urlopen(url: str, timeout: float) -> object: + raise AssertionError(f"Should not download populated subdir: {url}") + + monkeypatch.setattr( + "physiotwin4d.data_download_tools.urllib.request.urlopen", fake_urlopen + ) + + result_dir = DataDownloadTools.DownloadCHOPValve4DData(output_dir) + + assert result_dir == output_dir + for subdir_name in DataDownloadTools.CHOP_VALVE4D_ASSETS: + assert (output_dir / subdir_name / "already_here.txt").read_text() == ( + "cached\n" + ) + if __name__ == "__main__": pytest.main([__file__, "-v", "-s"]) diff --git a/tutorials/README.md b/tutorials/README.md index 2e77ec0..3e4446a 100644 --- a/tutorials/README.md +++ b/tutorials/README.md @@ -15,7 +15,7 @@ dataset licensing, and expected directory layout. |---|--------|-------------|---------| | 1 | [tutorial_01_heart_gated_ct_to_usd.py](tutorial_01_heart_gated_ct_to_usd.py) | `WorkflowConvertImageToUSD` | Slicer-Heart-CT (prepare first) | | 2 | [tutorial_02_ct_to_vtk.py](tutorial_02_ct_to_vtk.py) | `WorkflowConvertImageToVTK` | Slicer-Heart-CT (prepare first) | -| 3 | [tutorial_03_create_statistical_model.py](tutorial_03_create_statistical_model.py) | `WorkflowCreateStatisticalModel` | KCL-Heart-Model (manual) | +| 3 | [tutorial_03_create_statistical_model.py](tutorial_03_create_statistical_model.py) | `WorkflowCreateStatisticalModel` | KCL-Heart-Model | | 4 | [tutorial_04_fit_statistical_model_to_patient.py](tutorial_04_fit_statistical_model_to_patient.py) | `WorkflowFitStatisticalModelToPatient` | KCL-Heart-Model plus Tutorial 3 output | | 5 | [tutorial_05_vtk_to_usd.py](tutorial_05_vtk_to_usd.py) | `WorkflowConvertVTKToUSD` | Output of tutorial 2 | | 6 | [tutorial_06_reconstruct_highres_4d_ct.py](tutorial_06_reconstruct_highres_4d_ct.py) | `WorkflowReconstructHighres4DCT` | DirLab-4DCT (manual) | From 0ccd5f15864f2fb06db17bad96e8ea3486c044d6 Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Wed, 8 Jul 2026 14:33:26 -0400 Subject: [PATCH 2/3] ENH: Code Rabbit --- src/physiotwin4d/data_download_tools.py | 87 ++++++++++++++++++----- tests/test_download_heart_data.py | 94 ++++++++++++++++++++++--- 2 files changed, 154 insertions(+), 27 deletions(-) diff --git a/src/physiotwin4d/data_download_tools.py b/src/physiotwin4d/data_download_tools.py index 4fc20e2..511c1c5 100644 --- a/src/physiotwin4d/data_download_tools.py +++ b/src/physiotwin4d/data_download_tools.py @@ -188,10 +188,11 @@ def DownloadCHOPValve4DData(dirname: Union[str, Path]) -> Path: # noqa: N802 and extracts each into its matching subdirectory: ``Alterra/`` and ``TPV25/`` (valve mesh time series, >1 GB each) and ``CT/`` (source CT volume and Simpleware segmentation). A subdirectory that already - has any files is left alone, so re-running resumes an interrupted - download. See ``data/CHOP-Valve4D/README.md`` for what this - converted data contains and how it relates to the original FEBio - source model. + has its expected files (see ``_CHOPValve4DSubdirIsPopulated``) is + left alone, so re-running resumes an interrupted download without + re-fetching a subdirectory a partial extraction left behind. See + ``data/CHOP-Valve4D/README.md`` for what this converted data + contains and how it relates to the original FEBio source model. Args: dirname: Directory where the CHOP-Valve4D dataset should live. @@ -202,12 +203,36 @@ def DownloadCHOPValve4DData(dirname: Union[str, Path]) -> Path: # noqa: N802 data_dir = Path(dirname) for subdir_name, asset_name in DataDownloadTools.CHOP_VALVE4D_ASSETS.items(): target_dir = data_dir / subdir_name - if target_dir.is_dir() and any(target_dir.iterdir()): + if DataDownloadTools._CHOPValve4DSubdirIsPopulated(subdir_name, target_dir): continue url = DataDownloadTools.CHOP_VALVE4D_RELEASE_URL + asset_name DataDownloadTools._DownloadAndExtractZip(url, target_dir) return data_dir + @staticmethod + def _CHOPValve4DSubdirIsPopulated( # noqa: N802 + subdir_name: str, target_dir: Path + ) -> bool: + """Return True when ``target_dir`` already has subdir_name's expected files. + + Used to decide whether a subdirectory can be skipped on a re-run. + Checking for *any* file is not enough — an interrupted extraction + can leave a partially-populated directory that would then be + skipped forever — so this checks for the specific files each + subdirectory is expected to contain once fully extracted. + """ + if not target_dir.is_dir(): + return False + if subdir_name == "CT": + has_ct_volume = any( + (target_dir / filename).is_file() + for filename in ("RVOT28-Dias.nii.gz", "RVOT28-Dias.mha") + ) + has_simpleware_parts = (target_dir / "Simpleware" / "parts").is_dir() + return has_ct_volume or has_simpleware_parts + # Alterra and TPV25 are valve mesh time series. + return any(target_dir.glob("*.vtk")) + @staticmethod def _DownloadAndExtractZip(url: str, target_dir: Path) -> None: # noqa: N802 """Stream-download a ``.zip`` archive and extract it into ``target_dir``.""" @@ -236,18 +261,39 @@ def VerifyCHOPValve4DData(dirname: Union[str, Path]) -> bool: # noqa: N802 experiments. """ data_dir = Path(dirname) - has_ct_volume = any( - (data_dir / "CT" / filename).is_file() - for filename in ("RVOT28-Dias.nii.gz", "RVOT28-Dias.mha") - ) - has_simpleware_parts = (data_dir / "CT" / "Simpleware" / "parts").is_dir() - has_alterra = (data_dir / "Alterra").is_dir() and any( - (data_dir / "Alterra").glob("*.vtk") + has_ct = DataDownloadTools._CHOPValve4DSubdirIsPopulated("CT", data_dir / "CT") + has_alterra = DataDownloadTools._CHOPValve4DSubdirIsPopulated( + "Alterra", data_dir / "Alterra" ) - has_tpv25 = (data_dir / "TPV25").is_dir() and any( - (data_dir / "TPV25").glob("*.vtk") + has_tpv25 = DataDownloadTools._CHOPValve4DSubdirIsPopulated( + "TPV25", data_dir / "TPV25" ) - return has_ct_volume or has_simpleware_parts or (has_alterra and has_tpv25) + return has_ct or (has_alterra and has_tpv25) + + @staticmethod + def _MetaImageHeaderHasBackingData(mhd_file: Path) -> bool: # noqa: N802 + """Return True when a MetaImage ``.mhd`` header's pixel data exists. + + Committed ``.mhd`` headers are tiny text files (a few hundred + bytes) that ship with the repository as documentation of the + expected DirLab-4DCT layout; their presence alone does not mean + the (large, gitignored) raw pixel data has been downloaded. This + checks that the file the header's ``ElementDataFile`` line points + to actually exists, resolved relative to the header's directory. + """ + try: + lines = mhd_file.read_text(errors="ignore").splitlines() + except OSError: + return False + for line in lines: + key, sep, value = line.partition("=") + if sep and key.strip() == "ElementDataFile": + data_file = value.strip() + if not data_file or data_file.upper() == "LOCAL": + # Pixel data is embedded in this same file. + return True + return (mhd_file.parent / data_file).is_file() + return False @staticmethod def VerifyDirLab4DCTData(dirname: Union[str, Path]) -> bool: # noqa: N802 @@ -256,11 +302,16 @@ def VerifyDirLab4DCTData(dirname: Union[str, Path]) -> bool: # noqa: N802 case1_dir = data_dir / "Case1" has_case_dir_layout = case1_dir.is_dir() and any(case1_dir.glob("*.mha")) has_case_dir_layout = has_case_dir_layout or ( - case1_dir.is_dir() and any(case1_dir.glob("*.mhd")) + case1_dir.is_dir() + and any( + DataDownloadTools._MetaImageHeaderHasBackingData(mhd_file) + for mhd_file in case1_dir.glob("*.mhd") + ) ) - has_pack_layout = any(data_dir.glob("Case1Pack_T*.mhd")) or any( - data_dir.glob("Case1Pack_T*.mha") + has_pack_layout = any(data_dir.glob("Case1Pack_T*.mha")) or any( + DataDownloadTools._MetaImageHeaderHasBackingData(mhd_file) + for mhd_file in data_dir.glob("Case1Pack_T*.mhd") ) return has_case_dir_layout or has_pack_layout diff --git a/tests/test_download_heart_data.py b/tests/test_download_heart_data.py index 3405992..9d15e8f 100644 --- a/tests/test_download_heart_data.py +++ b/tests/test_download_heart_data.py @@ -42,14 +42,41 @@ def test_verify_kcl_heart_model_data(self, tmp_path: Path) -> None: assert DataDownloadTools.VerifyKCLHeartModelData(tmp_path) def test_verify_dirlab_4dct_data(self, tmp_path: Path) -> None: - """Verify DirLab data by supported Case1 phase image layouts.""" + """Verify DirLab data requires both the header and its backing data.""" case1_dir = tmp_path / "Case1" case1_dir.mkdir() assert not DataDownloadTools.VerifyDirLab4DCTData(tmp_path) - (case1_dir / "case1_T00.mhd").write_text("ObjectType = Image\n") + # A header alone (as committed to the repo) must not verify -- + # the raw pixel data it points to has not been downloaded yet. + (case1_dir / "case1_T00.mhd").write_text( + "ObjectType = Image\nElementDataFile = case1_T00.img\n" + ) + assert not DataDownloadTools.VerifyDirLab4DCTData(tmp_path) + + # Once the backing pixel data the header points to exists, it verifies. + (case1_dir / "case1_T00.img").write_bytes(b"raw") + assert DataDownloadTools.VerifyDirLab4DCTData(tmp_path) + + def test_verify_dirlab_4dct_pack_layout_requires_backing_data( + self, tmp_path: Path + ) -> None: + """A committed Case1Pack_T*.mhd header alone must not verify as downloaded. + + Regression test: these headers are committed to the repo to + document the expected layout, so glob-matching the header filename + alone would always report the dataset as present. + """ + mhd_file = tmp_path / "Case1Pack_T00.mhd" + mhd_file.write_text( + "ObjectType = Image\nElementDataFile = Case1Pack/Images/case1_T00_s.img\n" + ) + assert not DataDownloadTools.VerifyDirLab4DCTData(tmp_path) + backing_file = tmp_path / "Case1Pack" / "Images" / "case1_T00_s.img" + backing_file.parent.mkdir(parents=True) + backing_file.write_bytes(b"raw") assert DataDownloadTools.VerifyDirLab4DCTData(tmp_path) def test_verify_chop_valve_4d_data(self, tmp_path: Path) -> None: @@ -192,15 +219,24 @@ def fake_urlopen(url: str, timeout: float) -> object: extracted_file = output_dir / subdir_name / f"{subdir_name}.txt" assert extracted_file.read_text() == f"# {subdir_name}\n" + @staticmethod + def _write_chop_valve4d_expected_marker(target_dir: Path, subdir_name: str) -> None: + """Create the marker file/dir _CHOPValve4DSubdirIsPopulated looks for.""" + target_dir.mkdir(parents=True, exist_ok=True) + if subdir_name == "CT": + (target_dir / "RVOT28-Dias.mha").write_bytes(b"mha") + else: + (target_dir / "frame_0000.vtk").write_text("# vtk\n") + def test_download_chop_valve4d_data_skips_populated_subdirs( self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - """Subdirectories that already have files are not re-downloaded.""" + """Subdirectories with their expected marker files are not re-downloaded.""" output_dir = tmp_path / "CHOP-Valve4D" for subdir_name in DataDownloadTools.CHOP_VALVE4D_ASSETS: - existing_file = output_dir / subdir_name / "already_here.txt" - existing_file.parent.mkdir(parents=True) - existing_file.write_text("cached\n") + self._write_chop_valve4d_expected_marker( + output_dir / subdir_name, subdir_name + ) def fake_urlopen(url: str, timeout: float) -> object: raise AssertionError(f"Should not download populated subdir: {url}") @@ -212,11 +248,51 @@ def fake_urlopen(url: str, timeout: float) -> object: result_dir = DataDownloadTools.DownloadCHOPValve4DData(output_dir) assert result_dir == output_dir - for subdir_name in DataDownloadTools.CHOP_VALVE4D_ASSETS: - assert (output_dir / subdir_name / "already_here.txt").read_text() == ( - "cached\n" + + def test_download_chop_valve4d_data_redownloads_partial_extraction( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + """A subdir left behind by an interrupted extraction is re-downloaded. + + Regression test: an earlier version skipped any subdirectory that + merely had *some* file in it, so an interrupted extraction that only + wrote a partial/temp file would be treated as complete forever. + """ + output_dir = tmp_path / "CHOP-Valve4D" + partial_dir = output_dir / "Alterra" + partial_dir.mkdir(parents=True) + (partial_dir / "partial_download.tmp").write_text("incomplete\n") + + archives_dir = tmp_path / "archives" + archives_dir.mkdir() + + def make_archive(subdir_name: str, member_name: str, content: bytes) -> Path: + archive_path = archives_dir / f"{subdir_name}.zip" + with zipfile.ZipFile(archive_path, "w") as zf: + zf.writestr(member_name, content) + return archive_path + + urls_to_archives = {} + for subdir_name, asset_name in DataDownloadTools.CHOP_VALVE4D_ASSETS.items(): + url = DataDownloadTools.CHOP_VALVE4D_RELEASE_URL + asset_name + member_name = "RVOT28-Dias.mha" if subdir_name == "CT" else "frame_0000.vtk" + urls_to_archives[url] = make_archive( + subdir_name, member_name, f"# {subdir_name}\n".encode() ) + def fake_urlopen(url: str, timeout: float) -> object: + return open(urls_to_archives[url], "rb") + + monkeypatch.setattr( + "physiotwin4d.data_download_tools.urllib.request.urlopen", fake_urlopen + ) + + DataDownloadTools.DownloadCHOPValve4DData(output_dir) + + # The stray leftover file did not prevent Alterra from re-downloading. + assert (partial_dir / "frame_0000.vtk").read_text() == "# Alterra\n" + assert (partial_dir / "partial_download.tmp").exists() + if __name__ == "__main__": pytest.main([__file__, "-v", "-s"]) From 3cc11649d05945c28ab9648b05dcaa74a000c4bb Mon Sep 17 00:00:00 2001 From: Stephen Aylward Date: Wed, 8 Jul 2026 15:18:55 -0400 Subject: [PATCH 3/3] ENH: Updated github.io docs to reflect data access updates --- docs/cli_scripts/4dct_reconstruction.rst | 3 +- docs/cli_scripts/download_data.rst | 8 ++++- docs/quickstart.rst | 46 ++++++++++++++++++------ docs/tutorials.rst | 34 ++++++++++++++---- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/docs/cli_scripts/4dct_reconstruction.rst b/docs/cli_scripts/4dct_reconstruction.rst index 5daa30a..90f4087 100644 --- a/docs/cli_scripts/4dct_reconstruction.rst +++ b/docs/cli_scripts/4dct_reconstruction.rst @@ -15,7 +15,8 @@ Input Requirements * Optional fixed and moving masks for registration focus. DirLab-4DCT data cannot be downloaded automatically by PhysioTwin4D. Prepare -it manually before using the DirLab tutorial or examples. +it manually before using the DirLab tutorial or examples — see +``data/DirLab-4DCT/README.md`` for the download and layout instructions. Basic Usage =========== diff --git a/docs/cli_scripts/download_data.rst b/docs/cli_scripts/download_data.rst index 36d233f..2426eac 100644 --- a/docs/cli_scripts/download_data.rst +++ b/docs/cli_scripts/download_data.rst @@ -91,11 +91,17 @@ For ``CHOP-Valve4D``, the command downloads, extracts, and reuses: The command uses :meth:`physiotwin4d.data_download_tools.DataDownloadTools.DownloadCHOPValve4DData`, which fetches each subdirectory's zip archive from the PhysioTwin4D GitHub -release and skips any subdirectory that already contains files. +release and skips a subdirectory once it has its expected extracted files +(the CT volume or Simpleware segmentation for ``CT/``, ``.vtk`` meshes for +``Alterra/`` and ``TPV25/``) — a subdirectory left behind by an interrupted +extraction is re-downloaded rather than treated as complete. See Also ======== +* :doc:`../tutorials` — Tutorials 1-4 use ``Slicer-Heart-CT`` and + ``KCL-Heart-Model``; ``DirLab-4DCT`` (Tutorial 6) is manual-only, see + ``data/DirLab-4DCT/README.md``. * :doc:`byod_tutorials` * :doc:`heart_gated_ct` * :doc:`overview` diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 741d722..6413afb 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -25,7 +25,19 @@ the workflow classes directly. Run as a regular file See :doc:`tutorials` for the NVIDIA-styled tutorial card index, dataset requirements, script paths, and workflow details. -After preparing the Slicer-Heart-CT data, run the first two tutorials: +Fetch the sample datasets with the download CLI before running a tutorial: + +.. code-block:: bash + + physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT + physiotwin4d-download-data KCL-Heart-Model --directory data/KCL-Heart-Model + +See :doc:`cli_scripts/download_data` for all supported datasets, sizes, and +source URLs. ``DirLab-4DCT`` (Tutorial 6) is manual-only; see +``data/DirLab-4DCT/README.md``. + +After downloading the Slicer-Heart-CT data (above), run the first two +tutorials: .. code-block:: bash @@ -40,11 +52,11 @@ recommended run order. Recommended run order: -1. Tutorials 1 and 2 first, after preparing Slicer-Heart-CT data. +1. Tutorials 1 and 2 first, after downloading Slicer-Heart-CT data. 2. Tutorial 5 after Tutorial 2 (consumes Tutorial 2 output). 3. Tutorial 3 after downloading KCL-Heart-Model. 4. Tutorial 4 after Tutorial 3 because it can consume Tutorial 3 output. -5. Tutorial 6 after downloading DirLab-4DCT. +5. Tutorial 6 after downloading DirLab-4DCT (manual). 6. Tutorial 8 after preparing your own cardiac gated CT, labelmaps, KCL volume PCA model, and ICON weights (bring-your-own-data). 7. Tutorial 9a and/or 9b after Tutorial 8 because they train from its fitted @@ -73,9 +85,9 @@ CUDA-capable GPU are required for practical runtime. .. code-block:: bash - python -c "from physiotwin4d import DataDownloadTools; DataDownloadTools.DownloadSlicerHeartCTData('data/test')" + physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT - physiotwin4d-convert-image-to-usd data/test/TruncalValve_4DCT.seq.nrrd \ + physiotwin4d-convert-image-to-usd data/Slicer-Heart-CT/TruncalValve_4DCT.seq.nrrd \ --registration-method ICON \ --output-dir output/quickstart \ --project-name slicer_heart_quickstart @@ -237,18 +249,30 @@ Convert VTK time series to USD: Sample Data =========== -Download Sample Cardiac CT Data --------------------------------- +Download Sample Datasets +------------------------- + +``Slicer-Heart-CT``, ``KCL-Heart-Model``, and ``CHOP-Valve4D`` are all +auto-downloadable, via the CLI: + +.. code-block:: bash + + physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT + +or from Python: .. code-block:: python from physiotwin4d import DataDownloadTools - data_file = DataDownloadTools.DownloadSlicerHeartCTData("sample_data") - assert DataDownloadTools.VerifySlicerHeartCTData("sample_data") + data_file = DataDownloadTools.DownloadSlicerHeartCTData("data/Slicer-Heart-CT") + assert DataDownloadTools.VerifySlicerHeartCTData("data/Slicer-Heart-CT") + +See :doc:`cli_scripts/download_data` for sizes, source URLs, and directory +layouts for every dataset. -DirLab-4DCT data is manual-only; see ``data/README.md`` before running the -high-resolution 4D CT reconstruction tutorial. Tutorials 8-10 are +DirLab-4DCT data is manual-only; see ``data/DirLab-4DCT/README.md`` before +running the high-resolution 4D CT reconstruction tutorial. Tutorials 8-10 are bring-your-own-data cardiac tutorials; see :doc:`tutorials` for their dataset layout. Tutorials 9a/9b/10a/10b additionally require the optional ``physicsnemo`` extra (``pip install "physiotwin4d[physicsnemo]"``); diff --git a/docs/tutorials.rst b/docs/tutorials.rst index bc63d43..cb5ed45 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -75,6 +75,28 @@ Tutorials +Get the Data First +=================== + +Before running any tutorial below, fetch its sample dataset with the +``physiotwin4d-download-data`` CLI: + +.. code-block:: bash + + physiotwin4d-download-data Slicer-Heart-CT --directory data/Slicer-Heart-CT + physiotwin4d-download-data KCL-Heart-Model --directory data/KCL-Heart-Model + +This covers the data used by Tutorials 1-5. Run +``physiotwin4d-download-data --help`` for all options, and see +:doc:`cli_scripts/download_data` for dataset sizes, source URLs, and +directory layouts. + +``DirLab-4DCT`` (used by Tutorial 6) is **not** auto-downloaded — DIR-Lab +distributes it manually and may require registration; see +``data/DirLab-4DCT/README.md`` for instructions. Tutorials 8-10 are +bring-your-own-data (see the note before Tutorial 8) and do not use a +downloadable sample. + Recommended Run Order ===================== @@ -84,11 +106,11 @@ directories by default. Edit those constants for tutorial exploration, or use the installed ``physiotwin4d-*`` CLI commands when you need command-line path arguments. -1. Run Tutorials 1 and 2 after preparing Slicer-Heart-CT data. +1. Run Tutorials 1 and 2 after downloading Slicer-Heart-CT data (above). 2. Run Tutorial 5 after Tutorial 2 because it consumes Tutorial 2 output. -3. Run Tutorial 3 after downloading KCL-Heart-Model. +3. Run Tutorial 3 after downloading KCL-Heart-Model (above). 4. Run Tutorial 4 after Tutorial 3 because it can consume the PCA model output. -5. Run Tutorial 6 after downloading DirLab-4DCT. +5. Run Tutorial 6 after downloading DirLab-4DCT (manual — see above). 6. Run Tutorial 8 after preparing your own cardiac gated CT, labelmaps, KCL volume PCA model, and ICON weights (bring-your-own-data; see the note below). 7. Run Tutorial 9a and/or 9b after Tutorial 8 because they train from its @@ -106,7 +128,7 @@ Workflow ``WorkflowConvertImageToUSD`` Dataset - Slicer-Heart-CT, prepared before running the tutorial. + Slicer-Heart-CT, downloaded via ``physiotwin4d-download-data`` (see above). Preview .. figure:: assets/example.gif @@ -162,7 +184,7 @@ Workflow ``WorkflowConvertImageToVTK`` Dataset - Slicer-Heart-CT, prepared before running the tutorial. + Slicer-Heart-CT, downloaded via ``physiotwin4d-download-data`` (see above). Preview .. figure:: assets/example.gif @@ -361,7 +383,7 @@ Workflow ``WorkflowReconstructHighres4DCT`` Dataset - DirLab-4DCT, downloaded manually. + DirLab-4DCT, downloaded manually — see ``data/DirLab-4DCT/README.md``. Preview .. figure:: assets/example.gif