seqseg 2.1.6__tar.gz → 2.1.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. {seqseg-2.1.6 → seqseg-2.1.7}/PKG-INFO +59 -67
  2. seqseg-2.1.7/README.md +137 -0
  3. {seqseg-2.1.6 → seqseg-2.1.7}/pyproject.toml +2 -4
  4. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/cli.py +69 -28
  5. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global.yaml +1 -0
  6. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_cereb.yaml +1 -1
  7. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_debug.yaml +1 -0
  8. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/assembly.py +7 -2
  9. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/nnunet.py +18 -0
  10. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/post.py +53 -34
  11. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/train.py +282 -43
  12. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_cli_helpers.py +139 -0
  13. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_nnunet_device.py +16 -1
  14. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_train_pipeline.py +366 -0
  15. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/PKG-INFO +59 -67
  16. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/requires.txt +1 -3
  17. seqseg-2.1.6/README.md +0 -144
  18. {seqseg-2.1.6 → seqseg-2.1.7}/LICENSE +0 -0
  19. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/__init__.py +0 -0
  20. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/api.py +0 -0
  21. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/citations.py +0 -0
  22. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/__init__.py +0 -0
  23. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/aorta_tutorial.yaml +0 -0
  24. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_aorta.yaml +0 -0
  25. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_coro.yaml +0 -0
  26. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_default.yaml +0 -0
  27. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_pulm.yaml +0 -0
  28. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_seg.yaml +0 -0
  29. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_test.yaml +0 -0
  30. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config_models.py +0 -0
  31. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/__init__.py +0 -0
  32. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/capping.py +0 -0
  33. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/centerline.py +0 -0
  34. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/datasets.py +0 -0
  35. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/evaluation.py +0 -0
  36. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/initialization.py +0 -0
  37. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/local_assembly.py +0 -0
  38. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/params.py +0 -0
  39. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/prediction.py +0 -0
  40. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/simvascular.py +0 -0
  41. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/simvascular_contour.py +0 -0
  42. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/sitk_functions.py +0 -0
  43. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/sweep.py +0 -0
  44. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/tracing.py +0 -0
  45. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/tracing_functions.py +0 -0
  46. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/vmtk_functions.py +0 -0
  47. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/vtk_functions.py +0 -0
  48. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/old_auto_centerline.py +0 -0
  49. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline.py +0 -0
  50. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline2.py +0 -0
  51. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline3.py +0 -0
  52. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline4.py +0 -0
  53. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/__init__.py +0 -0
  54. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/classic.py +0 -0
  55. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/config.py +0 -0
  56. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/directories.py +0 -0
  57. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/plus.py +0 -0
  58. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/single_trace.py +0 -0
  59. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/__init__.py +0 -0
  60. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/prepare_casx_dataset.py +0 -0
  61. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/taubin_smooth.py +0 -0
  62. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/seqseg.py +0 -0
  63. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/seqseg_plus.py +0 -0
  64. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/conftest.py +0 -0
  65. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_api_entry.py +0 -0
  66. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_centerline_get_end_points.py +0 -0
  67. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_centerline_gradient.py +0 -0
  68. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_datasets_paths.py +0 -0
  69. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_nnunet2.py +0 -0
  70. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_prepare_casx_dataset.py +0 -0
  71. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_resample_spacing.py +0 -0
  72. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_segmentation.py +0 -0
  73. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_single_trace_staging.py +0 -0
  74. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_start_seg.py +0 -0
  75. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_user_paths.py +0 -0
  76. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_vtk.py +0 -0
  77. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/user_paths.py +0 -0
  78. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/SOURCES.txt +0 -0
  79. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/dependency_links.txt +0 -0
  80. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/entry_points.txt +0 -0
  81. {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/top_level.txt +0 -0
  82. {seqseg-2.1.6 → seqseg-2.1.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqseg
3
- Version: 2.1.6
3
+ Version: 2.1.7
4
4
  Summary: A deep learning-based medical image blood vessel tracking and segmentation tool.
5
5
  Author-email: Numi Sveinsson Cepero <numi@berkeley.com>
6
6
  License: Apache-2.0
@@ -23,12 +23,11 @@ Requires-Dist: SimpleITK>2
23
23
  Requires-Dist: PyYAML==6.0.3
24
24
  Requires-Dist: nnunetv2>=2.5.1
25
25
  Requires-Dist: scipy
26
+ Requires-Dist: vascular-segment-sampler>=0.6.0
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: pytest>=7; extra == "dev"
28
29
  Provides-Extra: viz
29
30
  Requires-Dist: matplotlib>=3.5; extra == "viz"
30
- Provides-Extra: train
31
- Requires-Dist: vascular-segment-sampler>=0.1.0; extra == "train"
32
31
  Dynamic: license-file
33
32
 
34
33
  ![example workflow](https://github.com/numisveinsson/SeqSeg/actions/workflows/python-app.yml/badge.svg)
@@ -48,10 +47,10 @@ Dynamic: license-file
48
47
  <p align="center">
49
48
  <a href="https://rdcu.be/dU0wy"><img src="https://img.shields.io/badge/Paper-Annals%20of%20BME-blue" alt="Paper"/></a>
50
49
  <a href="https://github.com/numisveinsson/SeqSeg/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-green.svg" alt="License"/></a>
51
- <a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.11-blue.svg" alt="Python"/></a>
50
+ <a href="https://www.python.org"><img src="https://img.shields.io/badge/Python-3.9%2B-blue.svg" alt="Python 3.9+"/></a>
52
51
  </p>
53
52
 
54
- > **News:** SeqSeg now outputs a full SimVascular project in the `simvascular/` subdirectory — open it directly in SimVascular with automatic pathlines and contours for every segmented branch.
53
+ > **News:** SeqSeg writes a full SimVascular project in the `simvascular/` subdirectory — open it directly in SimVascular with pathlines and contours for every segmented branch.
55
54
 
56
55
  ---
57
56
 
@@ -64,7 +63,7 @@ SeqSeg segments vessels **sequentially**, taking steps along vessel centerlines
64
63
  - 🩻 **Multi-modal** — works with CT and MR 3D medical images
65
64
  - 📏 **Scalable** — vessels from ~1mm coronaries to ~30mm aortas
66
65
  - ✅ **Clinically validated** — coronary, aortic, cerebral, and pulmonary anatomies (pre-trained weights for aorta CT/MR and coronary CT)
67
- - ⚡ **Fast** — ~2–10 min per case, Dice > 0.9 on validation, runs on CPU or GPU
66
+ - ⚡ **Fast** — on CPU, about 2–5 minutes for an aorta, 5–15 for a coronary tree, and 10–30 for a cerebral tree. GPU is faster. Details in [Benchmarks](docs/benchmarks.md).
68
67
 
69
68
  <p align="center">
70
69
  <img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/mr_model_tracing_fast_shorter.gif" alt="SeqSeg Demo"/><br/>
@@ -73,89 +72,82 @@ SeqSeg segments vessels **sequentially**, taking steps along vessel centerlines
73
72
 
74
73
  ## What's new in 2.x
75
74
 
76
- SeqSeg **2.0** refactors the package around a clearer CLI and a stable Python API. Existing batch workflows still work; legacy invocations without a subcommand (e.g. `seqseg -data_dir ...`) are rewritten to `seqseg run batch` automatically.
75
+ SeqSeg **2.0** uses subcommands. A command from 1.x that starts with a flag, such as `seqseg -data_dir ...`, still runs: it is rewritten to `seqseg run batch` automatically.
77
76
 
78
- ### Command-line interface
77
+ ### Commands
79
78
 
80
- | Feature | Description |
79
+ | Command | Description |
81
80
  | -------- | ----------- |
82
- | **`seqseg run batch`** | Classic dataset batch tracing (preferred entry point) |
83
- | **`seqseg run single`** | One volume + seeds: stages under `<outdir>/_seqseg_single_staging/`, then runs like batch |
84
- | **`seqseg run plus batch`** | Global nnU-Net sweep, then SeqSeg (replaces monolithic `seqseg_plus` script flow) |
85
- | **`seqseg init dataset`** | Scaffold `images/`, `centerlines/`, `truths/`, and template `seeds.json` |
86
- | **`seqseg paths init` / `set` / `show`** | Save default nnU-Net / data / out dirs in `~/.seqseg/paths.yaml` |
87
- | **`seqseg train prepare`** | Extract patches or whole volumes + build nnU-Net Dataset (needs `pip install "seqseg[train]"`) |
88
- | **`seqseg train nnunet`** | Run nnU-Net plan/preprocess and training |
89
- | **`seqseg doctor`** | Check imports (SimpleITK, vtk, nnunetv2, scipy, optional sampler) and paths |
90
- | **`seqseg config dump` / `fingerprint`** | Inspect or diff packaged YAML configs |
91
- | **`seqseg post global-centerline`** | Post-process segmentations into global centerlines |
92
- | **`seqseg simvascular init`** | Create or refresh SimVascular project layout under a case directory |
93
- | **`seqseg --version`** | Print installed package version |
94
-
95
- ### Python library API
96
-
97
- Embed tracing in other Python code without writing SeqSeg output files:
98
-
99
- - **`seqseg.api.run_tracing`** — pass a `sitk.Image`, seed definitions, and an nnU-Net trainer folder; get a `TracingResult` with global probability segmentation at `result.assembly.assembly`
100
- - **`TracingOptions(disk_io=False)`** — skip VTK/MHA debug trees on disk (nnU-Net weights still load from `model_folder`)
101
- - **`BranchSeed`**, **`branch_seed_at_point`**, **`seeds_to_potential_branches`** — simple seed formats instead of hand-built step dicts
102
- - **`TracingContext`** / **`trace_centerline_from_context`** — lower-level control with the same in-memory image support
103
- - Lazy re-exports from **`import seqseg`** (see `seqseg/__init__.py`)
104
-
105
- Quick example (seeds and config known):
106
-
107
- ```python
108
- from seqseg.api import TracingOptions, branch_seed_at_point, run_tracing
109
-
110
- result = run_tracing(
111
- my_sitk_image,
112
- [branch_seed_at_point([x, y, z], radius)],
113
- "/path/to/nnUNetTrainer__nnUNetPlans__3d_fullres",
114
- config="global",
115
- options=TracingOptions(disk_io=False),
116
- )
117
- prob_seg = result.assembly.assembly # sitk.Image; threshold for binary masks
118
- ```
119
-
120
- See [High-level API (`seqseg.api`)](#high-level-api-seqsegapi) for full detail.
121
-
122
- ### Internal structure (for contributors)
123
-
124
- - Pipeline modules: `seqseg.pipeline.classic`, `plus`, `post`, `single_trace`
125
- - Typed config helpers: `AlgorithmConfig`, `NnUNetModelSpec` in `seqseg.config_models`
126
- - Tracing core accepts **`sitk.Image`** or file paths for the reference volume and optional prior segmentation
81
+ | **`seqseg run batch`** | Dataset batch tracing |
82
+ | **`seqseg run single`** | One volume and seeds. Stages files under `<outdir>/_seqseg_single_staging/`, then traces |
83
+ | **`seqseg run plus batch`** | Global nnU-Net sweep, then SeqSeg |
84
+ | **`seqseg init dataset`** | Create `images/`, `centerlines/`, `truths/`, and a template `seeds.json` |
85
+ | **`seqseg paths init` / `set` / `show`** | Save default nnU-Net, data, and output directories in `~/.seqseg/paths.yaml` |
86
+ | **`seqseg train prepare`** | Extract patches or whole volumes and build an nnU-Net dataset |
87
+ | **`seqseg train nnunet`** | Run nnU-Net planning, preprocessing, and training |
88
+ | **`seqseg doctor`** | Check imports (SimpleITK, vtk, nnunetv2, scipy, vascular-segment-sampler) and paths |
89
+ | **`seqseg config dump` / `fingerprint`** | Print a packaged YAML config, or list keys that differ from another |
90
+ | **`seqseg post global-centerline`** | Build global centerlines from existing segmentations |
91
+ | **`seqseg simvascular init`** | Create or refresh a SimVascular project under a case directory |
92
+ | **`seqseg --version`** | Print the installed version |
93
+
94
+ `seqseg run batch` flags use underscores (`-outdir` / `--outdir`). The data folder is `-data_dir` or `--data_directory`. `seqseg run single`, `seqseg train`, and `seqseg paths` use hyphens (`--image`, `--data-dir`).
127
95
 
128
96
  ### Migrating from 1.x
129
97
 
130
- 1. **CLI:** Prefer `seqseg run batch` (or keep legacy flags — they still work).
131
- 2. **Plus workflow:** Use `seqseg run plus batch` instead of `python -m seqseg.seqseg_plus` with the same nnU-Net path flags.
132
- 3. **Library:** Use `run_tracing` or `TracingContext` rather than calling `trace_centerline` with only file paths.
133
- 4. **Version:** `pip install -U seqseg` and check with `seqseg --version` (expects **2.1.0**).
98
+ 1. Prefer `seqseg run batch`. The old flag-only form still works.
99
+ 2. Use `seqseg run plus batch` in place of `python -m seqseg.seqseg_plus`. The nnU-Net path flags are unchanged.
100
+ 3. Check the install with `seqseg --version` after `pip install -U seqseg`.
134
101
 
135
102
  ## Quick Start
136
103
 
104
+ Python 3.9 or newer. 3.11 is the version used in the tutorial and conda example.
105
+
137
106
  ```bash
138
- # Install
139
107
  pip install seqseg
140
108
 
141
- # Download pre-trained weights (see Installation docs for links)
142
- # Run segmentation
143
- seqseg -data_dir your_data/ -nnunet_results_path path/to/weights/ -config_name aorta_tutorial
109
+ # Aorta and femoral weights (MR and CT). Coronary weights are a separate zip; see Installation.
110
+ curl -L -o nnUNet_results.zip https://zenodo.org/records/15020477/files/nnUNet_results.zip
111
+ unzip nnUNet_results.zip
112
+ ```
113
+
114
+ `-nnunet_results_path` is the folder that contains `Dataset005_SEQAORTANDFEMOMR` (and the other dataset folders in that zip).
115
+
116
+ **First run:** follow the [step-by-step tutorial](seqseg/tutorial/tutorial.md). It includes an abdominal-aorta MR scan, seed points, and Windows notes. The tutorial caps the number of steps so the example finishes in a few minutes; a real case should use the defaults.
117
+
118
+ **Your own data** needs `images/` and `seeds.json` (see [Usage](docs/usage.md)). This command is an aorta MR case. Change `-train_dataset`, `-config_name`, and `-img_ext` to match your images and weights:
119
+
120
+ ```bash
121
+ seqseg run batch \
122
+ -data_dir your_data/ \
123
+ -nnunet_results_path nnUNet_results/ \
124
+ -train_dataset Dataset005_SEQAORTANDFEMOMR \
125
+ -config_name global_aorta \
126
+ -img_ext .mha \
127
+ -outdir results/
144
128
  ```
145
129
 
146
- **📖 New here? Follow the [step-by-step tutorial](seqseg/tutorial/tutorial.md)** with example data and detailed instructions.
130
+ | Anatomy | `-train_dataset` | `-config_name` |
131
+ |---------|------------------|----------------|
132
+ | Aorta / femoral MR | `Dataset005_SEQAORTANDFEMOMR` | `global_aorta` |
133
+ | Aorta / femoral CT | `Dataset006_SEQAORTANDFEMOCT` | `global_aorta` |
134
+ | Coronary CT | `Dataset010_SEQCOROASOCACT` | `global_coro` |
135
+
136
+ The tutorial uses `-config_name aorta_tutorial` for its sample scan. Download links and the trainer-folder layout are in [Installation](docs/installation.md).
147
137
 
148
138
  ## Documentation
149
139
 
150
140
  | Guide | Description |
151
141
  |-------|-------------|
152
142
  | [Installation](docs/installation.md) | Setup, dependencies, and pre-trained model weights |
153
- | [Usage](docs/usage.md) | Data preparation, CLI arguments, and output files |
154
- | [Configuration](docs/configuration.md) | YAML configs and key tracking parameters |
143
+ | [Tutorial](seqseg/tutorial/tutorial.md) | End-to-end aorta example with sample data |
144
+ | [Usage](docs/usage.md) | Data layout, seeds, CLI arguments, and output files |
145
+ | [Configuration](docs/configuration.md) | YAML configs and tracking parameters |
155
146
  | [Algorithm Overview](docs/algorithm.md) | Methodology, workflow, and training strategy |
156
- | [Training](docs/training.md) | Train nnU-Net models on a new dataset for SeqSeg |
147
+ | [Training](docs/training.md) | Train nnU-Net models on a new dataset |
157
148
  | [Performance & Benchmarks](docs/benchmarks.md) | Accuracy, timing, and qualitative comparisons |
158
- | [Research & Development](docs/development.md) | SimVascular / Slicer integrations and related tooling |
149
+ | [API](docs/api.md) | Call tracing from Python |
150
+ | [Research & Development](docs/development.md) | SimVascular, 3D Slicer, and repository layout |
159
151
 
160
152
  ## Citation
161
153
 
seqseg-2.1.7/README.md ADDED
@@ -0,0 +1,137 @@
1
+ ![example workflow](https://github.com/numisveinsson/SeqSeg/actions/workflows/python-app.yml/badge.svg)
2
+ ![example workflow](https://github.com/numisveinsson/SeqSeg/actions/workflows/test.yml/badge.svg)
3
+
4
+ <p align="center">
5
+ <img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/seqseg_logo.png" alt="SeqSeg — Sequential Vessel Segmentation" width="480"/><br/>
6
+ <img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/coronary.png" alt="Example coronary segmentation (SeqSeg)" width="260"/>
7
+ </p>
8
+
9
+ <h1 align="center">SeqSeg: Sequential Vessel Segmentation and Tracking</h1>
10
+
11
+ <p align="center">
12
+ <b>Automatic tracking and segmentation of blood vessels in CT and MR images using deep learning and geometric tracking.</b>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://rdcu.be/dU0wy"><img src="https://img.shields.io/badge/Paper-Annals%20of%20BME-blue" alt="Paper"/></a>
17
+ <a href="https://github.com/numisveinsson/SeqSeg/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-green.svg" alt="License"/></a>
18
+ <a href="https://www.python.org"><img src="https://img.shields.io/badge/Python-3.9%2B-blue.svg" alt="Python 3.9+"/></a>
19
+ </p>
20
+
21
+ > **News:** SeqSeg writes a full SimVascular project in the `simvascular/` subdirectory — open it directly in SimVascular with pathlines and contours for every segmented branch.
22
+
23
+ ---
24
+
25
+ ## Why SeqSeg?
26
+
27
+ SeqSeg segments vessels **sequentially**, taking steps along vessel centerlines and detecting bifurcations to grow complete vascular trees from just **1–2 seed points**. By combining local deep-learning predictions (nnU-Net) with geometric tracking, it stays robust across vessel scales — from small coronaries to large aortas.
28
+
29
+ - 🌱 **Minimal supervision** — only 1–2 seed points to initialize
30
+ - 🌿 **Robust bifurcation detection** — automatically follows every branch
31
+ - 🩻 **Multi-modal** — works with CT and MR 3D medical images
32
+ - 📏 **Scalable** — vessels from ~1mm coronaries to ~30mm aortas
33
+ - ✅ **Clinically validated** — coronary, aortic, cerebral, and pulmonary anatomies (pre-trained weights for aorta CT/MR and coronary CT)
34
+ - ⚡ **Fast** — on CPU, about 2–5 minutes for an aorta, 5–15 for a coronary tree, and 10–30 for a cerebral tree. GPU is faster. Details in [Benchmarks](docs/benchmarks.md).
35
+
36
+ <p align="center">
37
+ <img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/mr_model_tracing_fast_shorter.gif" alt="SeqSeg Demo"/><br/>
38
+ <i>Real-time demonstration: automatic segmentation of an abdominal aorta in a 3D MR scan.</i>
39
+ </p>
40
+
41
+ ## What's new in 2.x
42
+
43
+ SeqSeg **2.0** uses subcommands. A command from 1.x that starts with a flag, such as `seqseg -data_dir ...`, still runs: it is rewritten to `seqseg run batch` automatically.
44
+
45
+ ### Commands
46
+
47
+ | Command | Description |
48
+ | -------- | ----------- |
49
+ | **`seqseg run batch`** | Dataset batch tracing |
50
+ | **`seqseg run single`** | One volume and seeds. Stages files under `<outdir>/_seqseg_single_staging/`, then traces |
51
+ | **`seqseg run plus batch`** | Global nnU-Net sweep, then SeqSeg |
52
+ | **`seqseg init dataset`** | Create `images/`, `centerlines/`, `truths/`, and a template `seeds.json` |
53
+ | **`seqseg paths init` / `set` / `show`** | Save default nnU-Net, data, and output directories in `~/.seqseg/paths.yaml` |
54
+ | **`seqseg train prepare`** | Extract patches or whole volumes and build an nnU-Net dataset |
55
+ | **`seqseg train nnunet`** | Run nnU-Net planning, preprocessing, and training |
56
+ | **`seqseg doctor`** | Check imports (SimpleITK, vtk, nnunetv2, scipy, vascular-segment-sampler) and paths |
57
+ | **`seqseg config dump` / `fingerprint`** | Print a packaged YAML config, or list keys that differ from another |
58
+ | **`seqseg post global-centerline`** | Build global centerlines from existing segmentations |
59
+ | **`seqseg simvascular init`** | Create or refresh a SimVascular project under a case directory |
60
+ | **`seqseg --version`** | Print the installed version |
61
+
62
+ `seqseg run batch` flags use underscores (`-outdir` / `--outdir`). The data folder is `-data_dir` or `--data_directory`. `seqseg run single`, `seqseg train`, and `seqseg paths` use hyphens (`--image`, `--data-dir`).
63
+
64
+ ### Migrating from 1.x
65
+
66
+ 1. Prefer `seqseg run batch`. The old flag-only form still works.
67
+ 2. Use `seqseg run plus batch` in place of `python -m seqseg.seqseg_plus`. The nnU-Net path flags are unchanged.
68
+ 3. Check the install with `seqseg --version` after `pip install -U seqseg`.
69
+
70
+ ## Quick Start
71
+
72
+ Python 3.9 or newer. 3.11 is the version used in the tutorial and conda example.
73
+
74
+ ```bash
75
+ pip install seqseg
76
+
77
+ # Aorta and femoral weights (MR and CT). Coronary weights are a separate zip; see Installation.
78
+ curl -L -o nnUNet_results.zip https://zenodo.org/records/15020477/files/nnUNet_results.zip
79
+ unzip nnUNet_results.zip
80
+ ```
81
+
82
+ `-nnunet_results_path` is the folder that contains `Dataset005_SEQAORTANDFEMOMR` (and the other dataset folders in that zip).
83
+
84
+ **First run:** follow the [step-by-step tutorial](seqseg/tutorial/tutorial.md). It includes an abdominal-aorta MR scan, seed points, and Windows notes. The tutorial caps the number of steps so the example finishes in a few minutes; a real case should use the defaults.
85
+
86
+ **Your own data** needs `images/` and `seeds.json` (see [Usage](docs/usage.md)). This command is an aorta MR case. Change `-train_dataset`, `-config_name`, and `-img_ext` to match your images and weights:
87
+
88
+ ```bash
89
+ seqseg run batch \
90
+ -data_dir your_data/ \
91
+ -nnunet_results_path nnUNet_results/ \
92
+ -train_dataset Dataset005_SEQAORTANDFEMOMR \
93
+ -config_name global_aorta \
94
+ -img_ext .mha \
95
+ -outdir results/
96
+ ```
97
+
98
+ | Anatomy | `-train_dataset` | `-config_name` |
99
+ |---------|------------------|----------------|
100
+ | Aorta / femoral MR | `Dataset005_SEQAORTANDFEMOMR` | `global_aorta` |
101
+ | Aorta / femoral CT | `Dataset006_SEQAORTANDFEMOCT` | `global_aorta` |
102
+ | Coronary CT | `Dataset010_SEQCOROASOCACT` | `global_coro` |
103
+
104
+ The tutorial uses `-config_name aorta_tutorial` for its sample scan. Download links and the trainer-folder layout are in [Installation](docs/installation.md).
105
+
106
+ ## Documentation
107
+
108
+ | Guide | Description |
109
+ |-------|-------------|
110
+ | [Installation](docs/installation.md) | Setup, dependencies, and pre-trained model weights |
111
+ | [Tutorial](seqseg/tutorial/tutorial.md) | End-to-end aorta example with sample data |
112
+ | [Usage](docs/usage.md) | Data layout, seeds, CLI arguments, and output files |
113
+ | [Configuration](docs/configuration.md) | YAML configs and tracking parameters |
114
+ | [Algorithm Overview](docs/algorithm.md) | Methodology, workflow, and training strategy |
115
+ | [Training](docs/training.md) | Train nnU-Net models on a new dataset |
116
+ | [Performance & Benchmarks](docs/benchmarks.md) | Accuracy, timing, and qualitative comparisons |
117
+ | [API](docs/api.md) | Call tracing from Python |
118
+ | [Research & Development](docs/development.md) | SimVascular, 3D Slicer, and repository layout |
119
+
120
+ ## Citation
121
+
122
+ When using SeqSeg, please cite the following [paper](https://rdcu.be/dU0wy):
123
+
124
+ ```
125
+ @Article{SveinssonCepero2024,
126
+ author={Sveinsson Cepero, Numi
127
+ and Shadden, Shawn C.},
128
+ title={SeqSeg: Learning Local Segments for Automatic Vascular Model Construction},
129
+ journal={Annals of Biomedical Engineering},
130
+ year={2024},
131
+ month={Sep},
132
+ day={18},
133
+ issn={1573-9686},
134
+ doi={10.1007/s10439-024-03611-z},
135
+ url={https://doi.org/10.1007/s10439-024-03611-z},
136
+ }
137
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "seqseg"
7
- version = "2.1.6"
7
+ version = "2.1.7"
8
8
  description = "A deep learning-based medical image blood vessel tracking and segmentation tool."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -42,6 +42,7 @@ dependencies = [
42
42
  "PyYAML==6.0.3",
43
43
  "nnunetv2>=2.5.1",
44
44
  "scipy",
45
+ "vascular-segment-sampler>=0.6.0",
45
46
  ]
46
47
 
47
48
  [project.scripts]
@@ -62,9 +63,6 @@ dev = [
62
63
  viz = [
63
64
  "matplotlib>=3.5",
64
65
  ]
65
- train = [
66
- "vascular-segment-sampler>=0.1.0",
67
- ]
68
66
 
69
67
  [tool.pytest.ini_options]
70
68
  testpaths = ["seqseg/tests"]
@@ -225,7 +225,8 @@ def _validate_trace_batch(ns: argparse.Namespace) -> None:
225
225
  + "\n ".join(missing)
226
226
  + "\n\nMinimal example:\n"
227
227
  " seqseg paths init\n"
228
- " seqseg paths set --data-dir /path/to/dataset --outdir /path/to/out\n"
228
+ " seqseg paths set --nnunet-root ~/nnunet_data "
229
+ "--data-dir /path/to/dataset --outdir /path/to/out\n"
229
230
  " seqseg run batch -img_ext .nii.gz "
230
231
  "-train_dataset Dataset010_SEQCOROASOCACT\n",
231
232
  file=sys.stderr,
@@ -435,7 +436,6 @@ def _cmd_init_dataset(ns: argparse.Namespace) -> None:
435
436
  f" seqseg run batch -data_dir {root}/ -outdir ... -img_ext .nii.gz "
436
437
  "-nnunet_results_path ...\n"
437
438
  "\nNext (train a new model): also fill centerlines/ and truths/, then:\n"
438
- f' pip install "seqseg[train]"\n'
439
439
  f" seqseg paths init --data-dir {root}/ --outdir ~/seqseg_train\n"
440
440
  " seqseg train prepare --name MYDATA --dataset-number 999 --modality CT\n"
441
441
  )
@@ -550,10 +550,7 @@ def _cmd_doctor(ns: argparse.Namespace) -> None:
550
550
  try:
551
551
  importlib.import_module(modname)
552
552
  except ImportError as e:
553
- extra = ""
554
- if modname == "vascular_segment_sampler":
555
- extra = ' — optional; pip install "seqseg[train]"'
556
- print(f" {label}: MISSING ({e}){extra}")
553
+ print(f" {label}: MISSING ({e})")
557
554
  else:
558
555
  print(f" {label}: OK")
559
556
 
@@ -592,19 +589,9 @@ def _cmd_paths_init(ns: argparse.Namespace) -> None:
592
589
 
593
590
 
594
591
  def _cmd_paths_set(ns: argparse.Namespace) -> None:
595
- fields = (
596
- ns.nnunet_root,
597
- ns.nnunet_raw,
598
- ns.nnunet_preprocessed,
599
- ns.nnunet_results,
600
- ns.data_dir,
601
- ns.outdir,
602
- )
603
- if not any(f is not None for f in fields):
592
+ if not ns.nnunet_root:
604
593
  print(
605
- "seqseg paths set: provide at least one of "
606
- "--nnunet-root, --nnunet-raw, --nnunet-preprocessed, "
607
- "--nnunet-results, --data-dir, --outdir",
594
+ "seqseg paths set: --nnunet-root is required",
608
595
  file=sys.stderr,
609
596
  )
610
597
  sys.exit(2)
@@ -673,10 +660,21 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
673
660
  verbose=ns.verbose,
674
661
  img_ext=ns.img_ext,
675
662
  global_volumes=ns.global_volumes,
663
+ keep_extracted=ns.keep_extracted,
676
664
  )
677
665
  except TrainDependencyError as e:
678
666
  print(str(e), file=sys.stderr)
679
667
  sys.exit(1)
668
+ except FileNotFoundError as e:
669
+ message = str(e)
670
+ if message.startswith("Sampler config"):
671
+ print(message, file=sys.stderr)
672
+ sys.exit(2)
673
+ import traceback
674
+
675
+ traceback.print_exc()
676
+ print(f"seqseg train prepare failed: {e}", file=sys.stderr)
677
+ sys.exit(1)
680
678
  except Exception as e: # noqa: BLE001
681
679
  import traceback
682
680
 
@@ -693,8 +691,17 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
693
691
  sys.exit(1)
694
692
 
695
693
  print("\nPrepare complete.")
696
- kind = "Whole volumes" if ns.global_volumes else "Patches"
697
- print(f" {kind}: {result.extracted_dir}")
694
+ if result.removed_staging:
695
+ print(
696
+ " Extracted volumes were linked into nnU-Net raw and the "
697
+ "temporary folder was removed."
698
+ )
699
+ else:
700
+ kind = "Whole volumes" if ns.global_volumes else "Patches"
701
+ print(f" {kind}: {result.extracted_dir}")
702
+ results = resolve_path("nnunet_results")
703
+ if results:
704
+ print(f" Trained models will be written under: {results}")
698
705
  for name, path in zip(result.dataset_names, result.dataset_dirs):
699
706
  try:
700
707
  ds_id = dataset_id_from_name(name)
@@ -734,6 +741,7 @@ def _cmd_train_nnunet(ns: argparse.Namespace) -> None:
734
741
  np=ns.np,
735
742
  trainer=ns.trainer,
736
743
  plans=ns.plans,
744
+ cleanup=ns.cleanup,
737
745
  )
738
746
  except Exception as e: # noqa: BLE001
739
747
  print(f"seqseg train nnunet failed: {e}", file=sys.stderr)
@@ -921,8 +929,18 @@ def _build_parser() -> argparse.ArgumentParser:
921
929
  g1 = gcl_sub.add_parser("single", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
922
930
  g1.add_argument("--seg", required=True, type=str)
923
931
  g1.add_argument("--out", required=True, type=str)
924
- g1.add_argument("--seeds-json", required=True, type=str)
925
- g1.add_argument("--case-name", required=True, type=str)
932
+ g1.add_argument(
933
+ "--seeds-json",
934
+ default=None,
935
+ type=str,
936
+ help="Dataset seeds.json. Omit to place one seed in each disconnected body",
937
+ )
938
+ g1.add_argument(
939
+ "--case-name",
940
+ default=None,
941
+ type=str,
942
+ help="Case name in seeds.json. Required when --seeds-json is set",
943
+ )
926
944
  g1.add_argument("--config-name", default=None, type=str)
927
945
  g1.add_argument("--merge-method", default="clean", type=str)
928
946
  g1.add_argument("--directory-data", default=None, type=str)
@@ -932,7 +950,12 @@ def _build_parser() -> argparse.ArgumentParser:
932
950
  g2 = gcl_sub.add_parser("batch", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
933
951
  g2.add_argument("--seg-dir", required=True, type=str)
934
952
  g2.add_argument("--seg-glob", required=True, type=str)
935
- g2.add_argument("--seeds-json", required=True, type=str)
953
+ g2.add_argument(
954
+ "--seeds-json",
955
+ default=None,
956
+ type=str,
957
+ help="Dataset seeds.json. Omit to place one seed in each disconnected body",
958
+ )
936
959
  g2.add_argument("--out-dir", default=None, type=str)
937
960
  g2.add_argument("--config-name", default=None, type=str)
938
961
  g2.add_argument("--merge-method", default="clean", type=str)
@@ -1014,8 +1037,9 @@ def _build_parser() -> argparse.ArgumentParser:
1014
1037
  "-outdir",
1015
1038
  default=None,
1016
1039
  type=str,
1017
- help="Directory for extracted patches or whole volumes "
1018
- "(default: seqseg paths outdir, else ./extracted_data/)",
1040
+ help="Optional scratch directory for extracted patches or whole volumes. "
1041
+ "Default: <parent of nnUNet_raw>/_seqseg_extracted, removed after "
1042
+ "conversion. Pass this to keep extracts or resume with --skip-sample.",
1019
1043
  )
1020
1044
  p_prep.add_argument(
1021
1045
  "--name",
@@ -1050,7 +1074,7 @@ def _build_parser() -> argparse.ArgumentParser:
1050
1074
  default=None,
1051
1075
  type=str,
1052
1076
  help="Write DatasetXXX_* here "
1053
- "(default: seqseg paths nnunet_raw / $nnUNet_raw; else --outdir)",
1077
+ "(default: seqseg paths nnunet_raw / $nnUNet_raw)",
1054
1078
  )
1055
1079
  p_prep.add_argument(
1056
1080
  "--img-ext",
@@ -1103,6 +1127,11 @@ def _build_parser() -> argparse.ArgumentParser:
1103
1127
  )
1104
1128
  p_prep.add_argument("--yes", action="store_true", help="Non-interactive confirms")
1105
1129
  p_prep.add_argument("--verbose", action="store_true")
1130
+ p_prep.add_argument(
1131
+ "--keep-extracted",
1132
+ action="store_true",
1133
+ help="Keep <nnunet-root>/_seqseg_extracted after conversion into nnUNet_raw",
1134
+ )
1106
1135
  p_prep.set_defaults(_handler=_cmd_train_prepare, img_ext=None)
1107
1136
 
1108
1137
  p_nn = train_sub.add_parser(
@@ -1152,6 +1181,13 @@ def _build_parser() -> argparse.ArgumentParser:
1152
1181
  )
1153
1182
  p_nn.add_argument("--trainer", default="nnUNetTrainer", type=str)
1154
1183
  p_nn.add_argument("--plans", default="nnUNetPlans", type=str)
1184
+ p_nn.add_argument(
1185
+ "--cleanup",
1186
+ action="store_true",
1187
+ help="After training succeeds, delete this dataset's nnUNet_raw and "
1188
+ "nnUNet_preprocessed folders. Leaves nnUNet_results. "
1189
+ "Ignored with --plan-only.",
1190
+ )
1155
1191
  p_nn.set_defaults(_handler=_cmd_train_nnunet)
1156
1192
 
1157
1193
  paths_p = sub.add_parser(
@@ -1194,10 +1230,15 @@ def _build_parser() -> argparse.ArgumentParser:
1194
1230
 
1195
1231
  p_set = paths_sub.add_parser(
1196
1232
  "set",
1197
- help="Update one or more saved paths",
1233
+ help="Update saved paths (requires --nnunet-root)",
1198
1234
  formatter_class=argparse.ArgumentDefaultsHelpFormatter,
1199
1235
  )
1200
- p_set.add_argument("--nnunet-root", default=None, type=str)
1236
+ p_set.add_argument(
1237
+ "--nnunet-root",
1238
+ required=True,
1239
+ type=str,
1240
+ help="Parent directory for nnUNet_raw / preprocessed / results",
1241
+ )
1201
1242
  p_set.add_argument("--nnunet-raw", default=None, type=str)
1202
1243
  p_set.add_argument("--nnunet-preprocessed", default=None, type=str)
1203
1244
  p_set.add_argument("--nnunet-results", default=None, type=str)
@@ -14,6 +14,7 @@ TREE_ANALYSIS: False
14
14
 
15
15
  VOLUME_SIZE_RATIO: 5 # default 5, 5.5 for coronaries, 4 for segs
16
16
  MAGN_RADIUS: 1
17
+ ADD_RADIUS: 0 # mm (keep in mm)
17
18
  MAX_PERC_ENLARGE: 0.3 # if percent of blood in a given volume goes above this, we try enlarging volume, default 0.33 for coronaries
18
19
  MIN_RADIUS: 0.3 # mm (keep in mm)
19
20
  MIN_RES: 6
@@ -2,7 +2,7 @@
2
2
  # True if we have a segmentation, False if we are predicting
3
3
  SEGMENTATION: False # usually False
4
4
 
5
- DEBUG: True
5
+ DEBUG: False
6
6
  DEBUG_STEP: 200
7
7
 
8
8
  ANIMATION: True # to write out animation files
@@ -14,6 +14,7 @@ TREE_ANALYSIS: False
14
14
 
15
15
  VOLUME_SIZE_RATIO: 6 # default 5, 5.5 for coronaries, 4 for segs
16
16
  MAGN_RADIUS: 1
17
+ ADD_RADIUS: 0 # mm (keep in mm)
17
18
  MAX_PERC_ENLARGE: 0.5 # if percent of blood in a given volume goes above this, we try enlarging volume, default 0.33 for coronaries
18
19
  MIN_RADIUS: 0.3 # mm (keep in mm)
19
20
  MIN_RES: 6
@@ -978,8 +978,13 @@ def calc_centerline_global(predicted_vessels, initial_seeds,
978
978
  Centerline merge method for post-processing. Supported values are
979
979
  ``'clean'`` (default) and ``'tree'``.
980
980
  """
981
- print(f"""Calculating global centerline
982
- with {len(initial_seeds)} initial seeds""")
981
+ if nr_seeds is None:
982
+ print(
983
+ "Calculating global centerline; "
984
+ "identifying one seed per disconnected body"
985
+ )
986
+ else:
987
+ print(f"Calculating global centerline with {len(initial_seeds)} initial seeds")
983
988
  # Use multi-component centerline extraction so disconnected bodies are
984
989
  # handled in one unified workflow.
985
990
  centerline_poly, success_info = calc_multi_component_centerlines(
@@ -56,6 +56,24 @@ _CHECKPOINT_CANDIDATES = (
56
56
  )
57
57
 
58
58
 
59
+ def select_training_device():
60
+ """nnU-Net ``-device`` name: CUDA, then MPS, then CPU.
61
+
62
+ Returns ``cpu`` when torch is not installed. Training itself still needs
63
+ torch; this keeps command construction usable in tests that mock it.
64
+ """
65
+ try:
66
+ import torch
67
+ except ImportError:
68
+ return 'cpu'
69
+ if torch.cuda.is_available():
70
+ return 'cuda'
71
+ mps = getattr(getattr(torch, 'backends', None), 'mps', None)
72
+ if mps is not None and mps.is_available():
73
+ return 'mps'
74
+ return 'cpu'
75
+
76
+
59
77
  def select_inference_device(model_folder, force_cpu=False):
60
78
  """Pick torch device for nnU-Net inference.
61
79