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.
- {seqseg-2.1.6 → seqseg-2.1.7}/PKG-INFO +59 -67
- seqseg-2.1.7/README.md +137 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/pyproject.toml +2 -4
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/cli.py +69 -28
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global.yaml +1 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_cereb.yaml +1 -1
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_debug.yaml +1 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/assembly.py +7 -2
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/nnunet.py +18 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/post.py +53 -34
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/train.py +282 -43
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_cli_helpers.py +139 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_nnunet_device.py +16 -1
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_train_pipeline.py +366 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/PKG-INFO +59 -67
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/requires.txt +1 -3
- seqseg-2.1.6/README.md +0 -144
- {seqseg-2.1.6 → seqseg-2.1.7}/LICENSE +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/__init__.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/api.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/citations.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/__init__.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/aorta_tutorial.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_aorta.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_coro.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_default.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_pulm.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_seg.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config/global_test.yaml +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/config_models.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/__init__.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/capping.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/centerline.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/datasets.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/evaluation.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/initialization.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/local_assembly.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/params.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/prediction.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/simvascular.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/simvascular_contour.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/sitk_functions.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/sweep.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/tracing.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/tracing_functions.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/vmtk_functions.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/modules/vtk_functions.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/old_auto_centerline.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline2.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline3.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/parallel/parallel_auto_centerline4.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/__init__.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/classic.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/config.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/directories.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/plus.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/pipeline/single_trace.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/__init__.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/prepare_casx_dataset.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/scripts/taubin_smooth.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/seqseg.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/seqseg_plus.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/conftest.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_api_entry.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_centerline_get_end_points.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_centerline_gradient.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_datasets_paths.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_nnunet2.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_prepare_casx_dataset.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_resample_spacing.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_segmentation.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_single_trace_staging.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_start_seg.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_user_paths.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/tests/test_vtk.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg/user_paths.py +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/SOURCES.txt +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/dependency_links.txt +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/entry_points.txt +0 -0
- {seqseg-2.1.6 → seqseg-2.1.7}/seqseg.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|

|
|
@@ -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.
|
|
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
|
|
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** —
|
|
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**
|
|
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
|
-
###
|
|
77
|
+
### Commands
|
|
79
78
|
|
|
80
|
-
|
|
|
79
|
+
| Command | Description |
|
|
81
80
|
| -------- | ----------- |
|
|
82
|
-
| **`seqseg run batch`** |
|
|
83
|
-
| **`seqseg run single`** | One volume
|
|
84
|
-
| **`seqseg run plus batch`** | Global nnU-Net sweep, then SeqSeg
|
|
85
|
-
| **`seqseg init dataset`** |
|
|
86
|
-
| **`seqseg paths init` / `set` / `show`** | Save default nnU-Net
|
|
87
|
-
| **`seqseg train prepare`** | Extract patches or whole volumes
|
|
88
|
-
| **`seqseg train nnunet`** | Run nnU-Net
|
|
89
|
-
| **`seqseg doctor`** | Check imports (SimpleITK, vtk, nnunetv2, scipy,
|
|
90
|
-
| **`seqseg config dump` / `fingerprint`** |
|
|
91
|
-
| **`seqseg post global-centerline`** |
|
|
92
|
-
| **`seqseg simvascular init`** | Create or refresh SimVascular project
|
|
93
|
-
| **`seqseg --version`** | Print installed
|
|
94
|
-
|
|
95
|
-
|
|
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.
|
|
131
|
-
2.
|
|
132
|
-
3.
|
|
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
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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
|
-
| [
|
|
154
|
-
| [
|
|
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
|
|
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
|
-
| [
|
|
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
|
+

|
|
2
|
+

|
|
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.
|
|
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 --
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
697
|
-
|
|
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(
|
|
925
|
-
|
|
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(
|
|
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="
|
|
1018
|
-
"
|
|
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
|
|
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
|
|
1233
|
+
help="Update saved paths (requires --nnunet-root)",
|
|
1198
1234
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
1199
1235
|
)
|
|
1200
|
-
p_set.add_argument(
|
|
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
|
|
@@ -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
|
-
|
|
982
|
-
|
|
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
|
|