seqseg 2.1.7__tar.gz → 2.1.9__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.7 → seqseg-2.1.9}/PKG-INFO +28 -51
- seqseg-2.1.9/README.md +114 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/pyproject.toml +1 -1
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/cli.py +84 -39
- seqseg-2.1.9/seqseg/pipeline/nnunet_cli.py +108 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/train.py +629 -64
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_cli_helpers.py +87 -14
- seqseg-2.1.9/seqseg/tests/test_nnunet_cli.py +139 -0
- seqseg-2.1.9/seqseg/tests/test_train_data.py +97 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_train_pipeline.py +439 -73
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/PKG-INFO +28 -51
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/SOURCES.txt +3 -0
- seqseg-2.1.7/README.md +0 -137
- {seqseg-2.1.7 → seqseg-2.1.9}/LICENSE +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/__init__.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/api.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/citations.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/__init__.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/aorta_tutorial.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_aorta.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_cereb.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_coro.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_debug.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_default.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_pulm.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_seg.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config/global_test.yaml +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/config_models.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/__init__.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/assembly.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/capping.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/centerline.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/datasets.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/evaluation.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/initialization.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/local_assembly.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/nnunet.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/params.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/prediction.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/simvascular.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/simvascular_contour.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/sitk_functions.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/sweep.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/tracing.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/tracing_functions.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/vmtk_functions.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/modules/vtk_functions.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/parallel/old_auto_centerline.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/parallel/parallel_auto_centerline.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/parallel/parallel_auto_centerline2.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/parallel/parallel_auto_centerline3.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/parallel/parallel_auto_centerline4.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/__init__.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/classic.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/config.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/directories.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/plus.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/post.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/pipeline/single_trace.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/scripts/__init__.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/scripts/prepare_casx_dataset.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/scripts/taubin_smooth.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/seqseg.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/seqseg_plus.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/conftest.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_api_entry.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_centerline_get_end_points.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_centerline_gradient.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_datasets_paths.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_nnunet2.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_nnunet_device.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_prepare_casx_dataset.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_resample_spacing.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_segmentation.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_single_trace_staging.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_start_seg.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_user_paths.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/tests/test_vtk.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg/user_paths.py +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/dependency_links.txt +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/entry_points.txt +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/requires.txt +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/seqseg.egg-info/top_level.txt +0 -0
- {seqseg-2.1.7 → seqseg-2.1.9}/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.9
|
|
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
|
|
@@ -30,8 +30,8 @@ Provides-Extra: viz
|
|
|
30
30
|
Requires-Dist: matplotlib>=3.5; extra == "viz"
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
33
|
-

|
|
34
|
+

|
|
35
35
|
|
|
36
36
|
<p align="center">
|
|
37
37
|
<img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/seqseg_logo.png" alt="SeqSeg — Sequential Vessel Segmentation" width="480"/><br/>
|
|
@@ -50,10 +50,6 @@ Dynamic: license-file
|
|
|
50
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>
|
|
51
51
|
</p>
|
|
52
52
|
|
|
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.
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
53
|
## Why SeqSeg?
|
|
58
54
|
|
|
59
55
|
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.
|
|
@@ -65,57 +61,38 @@ SeqSeg segments vessels **sequentially**, taking steps along vessel centerlines
|
|
|
65
61
|
- ✅ **Clinically validated** — coronary, aortic, cerebral, and pulmonary anatomies (pre-trained weights for aorta CT/MR and coronary CT)
|
|
66
62
|
- ⚡ **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).
|
|
67
63
|
|
|
64
|
+
A finished case includes a `simvascular/` project you can open directly in SimVascular, with pathlines and contours for every segmented branch.
|
|
65
|
+
|
|
68
66
|
<p align="center">
|
|
69
67
|
<img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/mr_model_tracing_fast_shorter.gif" alt="SeqSeg Demo"/><br/>
|
|
70
68
|
<i>Real-time demonstration: automatic segmentation of an abdominal aorta in a 3D MR scan.</i>
|
|
71
69
|
</p>
|
|
72
70
|
|
|
73
|
-
##
|
|
74
|
-
|
|
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.
|
|
76
|
-
|
|
77
|
-
### Commands
|
|
78
|
-
|
|
79
|
-
| Command | Description |
|
|
80
|
-
| -------- | ----------- |
|
|
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`).
|
|
71
|
+
## Start here
|
|
95
72
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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`.
|
|
73
|
+
- **First segmentation** — the [tutorial](seqseg/tutorial/tutorial.md) runs a sample abdominal-aorta MR scan.
|
|
74
|
+
- **Your own images** — [Usage](docs/usage.md) describes `images/` and `seeds.json`. The command is in [Quick Start](#quick-start).
|
|
75
|
+
- **Train a model** — [Training](docs/training.md).
|
|
101
76
|
|
|
102
77
|
## Quick Start
|
|
103
78
|
|
|
104
79
|
Python 3.9 or newer. 3.11 is the version used in the tutorial and conda example.
|
|
105
80
|
|
|
81
|
+
The [tutorial](seqseg/tutorial/tutorial.md) is the sample run (abdominal-aorta MR, seed points, and Windows notes). Plan on 15–20 minutes including setup. Tracing is capped, so that part finishes in a few minutes; a real case should use the defaults.
|
|
82
|
+
|
|
83
|
+
Install SeqSeg and the aorta weights below, then run `seqseg run batch` on your own data. The folder needs `images/` and `seeds.json` ([Usage](docs/usage.md)). This example is an aorta MR case. Change `-train_dataset`, `-config_name`, and `-img_ext` to match your images and weights.
|
|
84
|
+
|
|
106
85
|
```bash
|
|
107
86
|
pip install seqseg
|
|
108
87
|
|
|
109
|
-
# Aorta and femoral weights (MR and CT)
|
|
88
|
+
# Aorta and femoral weights (MR and CT)
|
|
110
89
|
curl -L -o nnUNet_results.zip https://zenodo.org/records/15020477/files/nnUNet_results.zip
|
|
111
90
|
unzip nnUNet_results.zip
|
|
112
91
|
```
|
|
113
92
|
|
|
114
|
-
|
|
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.
|
|
93
|
+
[Aorta and femoral weights](https://zenodo.org/records/15020477) (`nnUNet_results.zip`). [Coronary CT weights](https://zenodo.org/records/19547894) are a separate archive, `nnUNet_results_coronary.zip`.
|
|
117
94
|
|
|
118
|
-
|
|
95
|
+
`-nnunet_results_path` is the folder that contains `Dataset005_SEQAORTANDFEMOMR` (and the other dataset folders in that zip).
|
|
119
96
|
|
|
120
97
|
```bash
|
|
121
98
|
seqseg run batch \
|
|
@@ -133,21 +110,21 @@ seqseg run batch \
|
|
|
133
110
|
| Aorta / femoral CT | `Dataset006_SEQAORTANDFEMOCT` | `global_aorta` |
|
|
134
111
|
| Coronary CT | `Dataset010_SEQCOROASOCACT` | `global_coro` |
|
|
135
112
|
|
|
136
|
-
The tutorial uses `-config_name aorta_tutorial` for its sample scan.
|
|
113
|
+
The tutorial uses `-config_name aorta_tutorial` for its sample scan. The trainer-folder layout is in [Installation](docs/installation.md). The full command list and flag spelling are in [Usage](docs/usage.md).
|
|
137
114
|
|
|
138
115
|
## Documentation
|
|
139
116
|
|
|
140
|
-
| Guide | Description |
|
|
141
|
-
|
|
142
|
-
| [Installation](docs/installation.md) | Setup, dependencies, and pre-trained model weights |
|
|
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 |
|
|
146
|
-
| [
|
|
147
|
-
| [
|
|
148
|
-
| [Performance & Benchmarks](docs/benchmarks.md) | Accuracy, timing, and qualitative comparisons |
|
|
149
|
-
| [API](docs/api.md) | Call tracing from Python |
|
|
150
|
-
| [Research & Development](docs/development.md) | SimVascular, 3D Slicer, and repository layout |
|
|
117
|
+
| Job | Guide | Description |
|
|
118
|
+
|-----|-------|-------------|
|
|
119
|
+
| Run | [Installation](docs/installation.md) | Setup, dependencies, and pre-trained model weights |
|
|
120
|
+
| Run | [Tutorial](seqseg/tutorial/tutorial.md) | End-to-end aorta example with sample data |
|
|
121
|
+
| Run | [Usage](docs/usage.md) | Data layout, seeds, CLI arguments, and output files |
|
|
122
|
+
| Run | [Configuration](docs/configuration.md) | YAML configs and tracking parameters |
|
|
123
|
+
| Train | [Training](docs/training.md) | Train nnU-Net models on a new dataset |
|
|
124
|
+
| Understand | [Algorithm Overview](docs/algorithm.md) | Methodology, workflow, and training strategy |
|
|
125
|
+
| Understand | [Performance & Benchmarks](docs/benchmarks.md) | Accuracy, timing, and qualitative comparisons |
|
|
126
|
+
| Integrate | [API](docs/api.md) | Call tracing from Python |
|
|
127
|
+
| Integrate | [Research & Development](docs/development.md) | SimVascular, 3D Slicer, and repository layout |
|
|
151
128
|
|
|
152
129
|
## Citation
|
|
153
130
|
|
seqseg-2.1.9/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
## Why SeqSeg?
|
|
22
|
+
|
|
23
|
+
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.
|
|
24
|
+
|
|
25
|
+
- 🌱 **Minimal supervision** — only 1–2 seed points to initialize
|
|
26
|
+
- 🌿 **Robust bifurcation detection** — automatically follows every branch
|
|
27
|
+
- 🩻 **Multi-modal** — works with CT and MR 3D medical images
|
|
28
|
+
- 📏 **Scalable** — vessels from ~1mm coronaries to ~30mm aortas
|
|
29
|
+
- ✅ **Clinically validated** — coronary, aortic, cerebral, and pulmonary anatomies (pre-trained weights for aorta CT/MR and coronary CT)
|
|
30
|
+
- ⚡ **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).
|
|
31
|
+
|
|
32
|
+
A finished case includes a `simvascular/` project you can open directly in SimVascular, with pathlines and contours for every segmented branch.
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<img src="https://raw.githubusercontent.com/numisveinsson/SeqSeg/main/seqseg/assets/mr_model_tracing_fast_shorter.gif" alt="SeqSeg Demo"/><br/>
|
|
36
|
+
<i>Real-time demonstration: automatic segmentation of an abdominal aorta in a 3D MR scan.</i>
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
## Start here
|
|
40
|
+
|
|
41
|
+
- **First segmentation** — the [tutorial](seqseg/tutorial/tutorial.md) runs a sample abdominal-aorta MR scan.
|
|
42
|
+
- **Your own images** — [Usage](docs/usage.md) describes `images/` and `seeds.json`. The command is in [Quick Start](#quick-start).
|
|
43
|
+
- **Train a model** — [Training](docs/training.md).
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
Python 3.9 or newer. 3.11 is the version used in the tutorial and conda example.
|
|
48
|
+
|
|
49
|
+
The [tutorial](seqseg/tutorial/tutorial.md) is the sample run (abdominal-aorta MR, seed points, and Windows notes). Plan on 15–20 minutes including setup. Tracing is capped, so that part finishes in a few minutes; a real case should use the defaults.
|
|
50
|
+
|
|
51
|
+
Install SeqSeg and the aorta weights below, then run `seqseg run batch` on your own data. The folder needs `images/` and `seeds.json` ([Usage](docs/usage.md)). This example is an aorta MR case. Change `-train_dataset`, `-config_name`, and `-img_ext` to match your images and weights.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install seqseg
|
|
55
|
+
|
|
56
|
+
# Aorta and femoral weights (MR and CT)
|
|
57
|
+
curl -L -o nnUNet_results.zip https://zenodo.org/records/15020477/files/nnUNet_results.zip
|
|
58
|
+
unzip nnUNet_results.zip
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
[Aorta and femoral weights](https://zenodo.org/records/15020477) (`nnUNet_results.zip`). [Coronary CT weights](https://zenodo.org/records/19547894) are a separate archive, `nnUNet_results_coronary.zip`.
|
|
62
|
+
|
|
63
|
+
`-nnunet_results_path` is the folder that contains `Dataset005_SEQAORTANDFEMOMR` (and the other dataset folders in that zip).
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
seqseg run batch \
|
|
67
|
+
-data_dir your_data/ \
|
|
68
|
+
-nnunet_results_path nnUNet_results/ \
|
|
69
|
+
-train_dataset Dataset005_SEQAORTANDFEMOMR \
|
|
70
|
+
-config_name global_aorta \
|
|
71
|
+
-img_ext .mha \
|
|
72
|
+
-outdir results/
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
| Anatomy | `-train_dataset` | `-config_name` |
|
|
76
|
+
|---------|------------------|----------------|
|
|
77
|
+
| Aorta / femoral MR | `Dataset005_SEQAORTANDFEMOMR` | `global_aorta` |
|
|
78
|
+
| Aorta / femoral CT | `Dataset006_SEQAORTANDFEMOCT` | `global_aorta` |
|
|
79
|
+
| Coronary CT | `Dataset010_SEQCOROASOCACT` | `global_coro` |
|
|
80
|
+
|
|
81
|
+
The tutorial uses `-config_name aorta_tutorial` for its sample scan. The trainer-folder layout is in [Installation](docs/installation.md). The full command list and flag spelling are in [Usage](docs/usage.md).
|
|
82
|
+
|
|
83
|
+
## Documentation
|
|
84
|
+
|
|
85
|
+
| Job | Guide | Description |
|
|
86
|
+
|-----|-------|-------------|
|
|
87
|
+
| Run | [Installation](docs/installation.md) | Setup, dependencies, and pre-trained model weights |
|
|
88
|
+
| Run | [Tutorial](seqseg/tutorial/tutorial.md) | End-to-end aorta example with sample data |
|
|
89
|
+
| Run | [Usage](docs/usage.md) | Data layout, seeds, CLI arguments, and output files |
|
|
90
|
+
| Run | [Configuration](docs/configuration.md) | YAML configs and tracking parameters |
|
|
91
|
+
| Train | [Training](docs/training.md) | Train nnU-Net models on a new dataset |
|
|
92
|
+
| Understand | [Algorithm Overview](docs/algorithm.md) | Methodology, workflow, and training strategy |
|
|
93
|
+
| Understand | [Performance & Benchmarks](docs/benchmarks.md) | Accuracy, timing, and qualitative comparisons |
|
|
94
|
+
| Integrate | [API](docs/api.md) | Call tracing from Python |
|
|
95
|
+
| Integrate | [Research & Development](docs/development.md) | SimVascular, 3D Slicer, and repository layout |
|
|
96
|
+
|
|
97
|
+
## Citation
|
|
98
|
+
|
|
99
|
+
When using SeqSeg, please cite the following [paper](https://rdcu.be/dU0wy):
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
@Article{SveinssonCepero2024,
|
|
103
|
+
author={Sveinsson Cepero, Numi
|
|
104
|
+
and Shadden, Shawn C.},
|
|
105
|
+
title={SeqSeg: Learning Local Segments for Automatic Vascular Model Construction},
|
|
106
|
+
journal={Annals of Biomedical Engineering},
|
|
107
|
+
year={2024},
|
|
108
|
+
month={Sep},
|
|
109
|
+
day={18},
|
|
110
|
+
issn={1573-9686},
|
|
111
|
+
doi={10.1007/s10439-024-03611-z},
|
|
112
|
+
url={https://doi.org/10.1007/s10439-024-03611-z},
|
|
113
|
+
}
|
|
114
|
+
```
|
|
@@ -437,7 +437,7 @@ def _cmd_init_dataset(ns: argparse.Namespace) -> None:
|
|
|
437
437
|
"-nnunet_results_path ...\n"
|
|
438
438
|
"\nNext (train a new model): also fill centerlines/ and truths/, then:\n"
|
|
439
439
|
f" seqseg paths init --data-dir {root}/ --outdir ~/seqseg_train\n"
|
|
440
|
-
" seqseg train prepare --name MYDATA --dataset-
|
|
440
|
+
" seqseg train prepare --name MYDATA --dataset-id 999 --modality CT\n"
|
|
441
441
|
)
|
|
442
442
|
|
|
443
443
|
|
|
@@ -634,6 +634,12 @@ def _cmd_paths_export(_ns: argparse.Namespace) -> None:
|
|
|
634
634
|
|
|
635
635
|
|
|
636
636
|
def _cmd_train_prepare(ns: argparse.Namespace) -> None:
|
|
637
|
+
if not ns.preprocess_only and not ns.name:
|
|
638
|
+
print(
|
|
639
|
+
"seqseg train prepare: --name is required",
|
|
640
|
+
file=sys.stderr,
|
|
641
|
+
)
|
|
642
|
+
sys.exit(2)
|
|
637
643
|
try:
|
|
638
644
|
result = prepare_training_dataset(
|
|
639
645
|
data_dir=ns.data_dir,
|
|
@@ -656,11 +662,14 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
|
|
|
656
662
|
skip_sample=ns.skip_sample,
|
|
657
663
|
skip_convert=ns.skip_convert,
|
|
658
664
|
also_test=ns.also_test,
|
|
659
|
-
yes=ns.yes,
|
|
660
665
|
verbose=ns.verbose,
|
|
661
666
|
img_ext=ns.img_ext,
|
|
662
667
|
global_volumes=ns.global_volumes,
|
|
663
668
|
keep_extracted=ns.keep_extracted,
|
|
669
|
+
configuration=ns.configuration,
|
|
670
|
+
plans=ns.plans,
|
|
671
|
+
np=ns.np,
|
|
672
|
+
preprocess_only=ns.preprocess_only,
|
|
664
673
|
)
|
|
665
674
|
except TrainDependencyError as e:
|
|
666
675
|
print(str(e), file=sys.stderr)
|
|
@@ -670,6 +679,9 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
|
|
|
670
679
|
if message.startswith("Sampler config"):
|
|
671
680
|
print(message, file=sys.stderr)
|
|
672
681
|
sys.exit(2)
|
|
682
|
+
if "seqseg train prepare" in message:
|
|
683
|
+
print(message, file=sys.stderr)
|
|
684
|
+
sys.exit(1)
|
|
673
685
|
import traceback
|
|
674
686
|
|
|
675
687
|
traceback.print_exc()
|
|
@@ -691,17 +703,30 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
|
|
|
691
703
|
sys.exit(1)
|
|
692
704
|
|
|
693
705
|
print("\nPrepare complete.")
|
|
694
|
-
if
|
|
706
|
+
if ns.preprocess_only:
|
|
695
707
|
print(
|
|
696
|
-
"
|
|
697
|
-
|
|
708
|
+
f" Preprocessed {ns.configuration} from the existing raw dataset."
|
|
709
|
+
)
|
|
710
|
+
elif result.removed_staging:
|
|
711
|
+
print(
|
|
712
|
+
" Extracted subvolumes were removed after conversion. "
|
|
713
|
+
"done.txt and the .txt/.csv metadata were kept in\n"
|
|
714
|
+
f" {result.extracted_dir}"
|
|
698
715
|
)
|
|
699
716
|
else:
|
|
700
717
|
kind = "Whole volumes" if ns.global_volumes else "Patches"
|
|
701
718
|
print(f" {kind}: {result.extracted_dir}")
|
|
719
|
+
if not result.preprocessed:
|
|
720
|
+
print(
|
|
721
|
+
" nnU-Net preprocessing was skipped (--skip-convert). "
|
|
722
|
+
"Re-run prepare without --skip-convert before training."
|
|
723
|
+
)
|
|
702
724
|
results = resolve_path("nnunet_results")
|
|
703
725
|
if results:
|
|
704
726
|
print(f" Trained models will be written under: {results}")
|
|
727
|
+
config_flag = ""
|
|
728
|
+
if ns.configuration != "3d_fullres":
|
|
729
|
+
config_flag = f" --configuration {ns.configuration}"
|
|
705
730
|
for name, path in zip(result.dataset_names, result.dataset_dirs):
|
|
706
731
|
try:
|
|
707
732
|
ds_id = dataset_id_from_name(name)
|
|
@@ -709,11 +734,12 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
|
|
|
709
734
|
ds_id = "?"
|
|
710
735
|
print(f" nnU-Net dataset: {name}")
|
|
711
736
|
print(f" path: {path}")
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
737
|
+
if result.preprocessed:
|
|
738
|
+
print(
|
|
739
|
+
" next:\n"
|
|
740
|
+
f" seqseg train nnunet --dataset-id {ds_id}"
|
|
741
|
+
f"{config_flag} --fold 0"
|
|
742
|
+
)
|
|
717
743
|
|
|
718
744
|
|
|
719
745
|
def _cmd_train_nnunet(ns: argparse.Namespace) -> None:
|
|
@@ -736,12 +762,10 @@ def _cmd_train_nnunet(ns: argparse.Namespace) -> None:
|
|
|
736
762
|
dataset_id,
|
|
737
763
|
configuration=ns.configuration,
|
|
738
764
|
fold=ns.fold,
|
|
739
|
-
skip_plan=ns.skip_plan,
|
|
740
|
-
plan_only=ns.plan_only,
|
|
741
|
-
np=ns.np,
|
|
742
765
|
trainer=ns.trainer,
|
|
743
766
|
plans=ns.plans,
|
|
744
767
|
cleanup=ns.cleanup,
|
|
768
|
+
continue_training=ns.continue_training,
|
|
745
769
|
)
|
|
746
770
|
except Exception as e: # noqa: BLE001
|
|
747
771
|
print(f"seqseg train nnunet failed: {e}", file=sys.stderr)
|
|
@@ -1021,7 +1045,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1021
1045
|
|
|
1022
1046
|
p_prep = train_sub.add_parser(
|
|
1023
1047
|
"prepare",
|
|
1024
|
-
help="
|
|
1048
|
+
help="Extract volumes, write an nnU-Net dataset, and plan/preprocess",
|
|
1025
1049
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
1026
1050
|
)
|
|
1027
1051
|
p_prep.add_argument(
|
|
@@ -1038,22 +1062,27 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1038
1062
|
default=None,
|
|
1039
1063
|
type=str,
|
|
1040
1064
|
help="Optional scratch directory for extracted patches or whole volumes. "
|
|
1041
|
-
"Default:
|
|
1042
|
-
"
|
|
1065
|
+
"Default: nnUNet_raw/DatasetXXX_*/_seqseg_extracted. Subvolumes are "
|
|
1066
|
+
"removed after conversion; done.txt and .txt/.csv metadata are kept. "
|
|
1067
|
+
"Pass this to keep a copy elsewhere.",
|
|
1043
1068
|
)
|
|
1044
1069
|
p_prep.add_argument(
|
|
1045
1070
|
"--name",
|
|
1046
1071
|
"-name",
|
|
1047
|
-
|
|
1072
|
+
default=None,
|
|
1048
1073
|
type=str,
|
|
1049
|
-
help="Dataset name stem (e.g. MYDATA → Dataset0XX_MYDATACT)"
|
|
1074
|
+
help="Dataset name stem (e.g. MYDATA → Dataset0XX_MYDATACT). "
|
|
1075
|
+
"Required unless --preprocess-only",
|
|
1050
1076
|
)
|
|
1051
1077
|
p_prep.add_argument(
|
|
1078
|
+
"--dataset-id",
|
|
1052
1079
|
"--dataset-number",
|
|
1053
1080
|
"-dataset_number",
|
|
1081
|
+
dest="dataset_number",
|
|
1054
1082
|
required=True,
|
|
1055
1083
|
type=int,
|
|
1056
|
-
help="nnU-Net dataset id (e.g. 999).
|
|
1084
|
+
help="nnU-Net dataset id to create (e.g. 999). "
|
|
1085
|
+
"Incremented per modality if several",
|
|
1057
1086
|
)
|
|
1058
1087
|
p_prep.add_argument(
|
|
1059
1088
|
"--modality",
|
|
@@ -1065,7 +1094,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1065
1094
|
p_prep.add_argument(
|
|
1066
1095
|
"--config-name",
|
|
1067
1096
|
"-config_name",
|
|
1068
|
-
default="
|
|
1097
|
+
default="default_mm",
|
|
1069
1098
|
type=str,
|
|
1070
1099
|
help="Sampler YAML name/path (from vascular-segment-sampler)",
|
|
1071
1100
|
)
|
|
@@ -1125,18 +1154,43 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1125
1154
|
action="store_true",
|
|
1126
1155
|
help="Also convert modality_test folders when present",
|
|
1127
1156
|
)
|
|
1128
|
-
p_prep.add_argument("--yes", action="store_true", help="Non-interactive confirms")
|
|
1129
1157
|
p_prep.add_argument("--verbose", action="store_true")
|
|
1130
1158
|
p_prep.add_argument(
|
|
1131
1159
|
"--keep-extracted",
|
|
1132
1160
|
action="store_true",
|
|
1133
|
-
help="Keep
|
|
1161
|
+
help="Keep extracted subvolumes in nnUNet_raw/DatasetXXX_*/_seqseg_extracted "
|
|
1162
|
+
"after conversion into nnUNet_raw",
|
|
1163
|
+
)
|
|
1164
|
+
p_prep.add_argument(
|
|
1165
|
+
"--configuration",
|
|
1166
|
+
default="3d_fullres",
|
|
1167
|
+
choices=["3d_fullres", "2d", "3d_lowres", "3d_cascade_fullres"],
|
|
1168
|
+
help="nnU-Net configuration to preprocess. Default 3d_fullres",
|
|
1169
|
+
)
|
|
1170
|
+
p_prep.add_argument(
|
|
1171
|
+
"--plans",
|
|
1172
|
+
default="nnUNetPlans",
|
|
1173
|
+
type=str,
|
|
1174
|
+
help="nnU-Net plans name (training -p). Default nnUNetPlans",
|
|
1175
|
+
)
|
|
1176
|
+
p_prep.add_argument(
|
|
1177
|
+
"--np",
|
|
1178
|
+
type=int,
|
|
1179
|
+
default=None,
|
|
1180
|
+
help="Processes for nnU-Net preprocessing. Default: --num-cores",
|
|
1181
|
+
)
|
|
1182
|
+
p_prep.add_argument(
|
|
1183
|
+
"--preprocess-only",
|
|
1184
|
+
action="store_true",
|
|
1185
|
+
help="Use an existing nnUNet_raw dataset. Plans and preprocesses it "
|
|
1186
|
+
"when plans are missing; otherwise resamples --configuration. "
|
|
1187
|
+
"Does not extract or convert subvolumes",
|
|
1134
1188
|
)
|
|
1135
1189
|
p_prep.set_defaults(_handler=_cmd_train_prepare, img_ext=None)
|
|
1136
1190
|
|
|
1137
1191
|
p_nn = train_sub.add_parser(
|
|
1138
1192
|
"nnunet",
|
|
1139
|
-
help="
|
|
1193
|
+
help="Train with nnUNetv2_train (--c continues from the latest checkpoint)",
|
|
1140
1194
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
1141
1195
|
)
|
|
1142
1196
|
p_nn.add_argument(
|
|
@@ -1149,13 +1203,13 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1149
1203
|
"--dataset-name",
|
|
1150
1204
|
type=str,
|
|
1151
1205
|
default=None,
|
|
1152
|
-
help="Alternative to --dataset-id (e.g.
|
|
1206
|
+
help="Alternative to --dataset-id (e.g. Dataset999_MYDATACT)",
|
|
1153
1207
|
)
|
|
1154
1208
|
p_nn.add_argument(
|
|
1155
1209
|
"--configuration",
|
|
1156
1210
|
default="3d_fullres",
|
|
1157
1211
|
choices=["3d_fullres", "2d", "3d_lowres", "3d_cascade_fullres"],
|
|
1158
|
-
help="nnU-Net configuration
|
|
1212
|
+
help="nnU-Net configuration to train. Default 3d_fullres",
|
|
1159
1213
|
)
|
|
1160
1214
|
p_nn.add_argument(
|
|
1161
1215
|
"--fold",
|
|
@@ -1164,20 +1218,11 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1164
1218
|
help="Fold to train (0-4 or all)",
|
|
1165
1219
|
)
|
|
1166
1220
|
p_nn.add_argument(
|
|
1167
|
-
"--
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
)
|
|
1171
|
-
p_nn.add_argument(
|
|
1172
|
-
"--plan-only",
|
|
1221
|
+
"--c",
|
|
1222
|
+
"--continue",
|
|
1223
|
+
dest="continue_training",
|
|
1173
1224
|
action="store_true",
|
|
1174
|
-
help="
|
|
1175
|
-
)
|
|
1176
|
-
p_nn.add_argument(
|
|
1177
|
-
"--np",
|
|
1178
|
-
type=int,
|
|
1179
|
-
default=None,
|
|
1180
|
-
help="Processes for plan_and_preprocess (-np)",
|
|
1225
|
+
help="Continue training from checkpoint_latest.pth (nnU-Net --c)",
|
|
1181
1226
|
)
|
|
1182
1227
|
p_nn.add_argument("--trainer", default="nnUNetTrainer", type=str)
|
|
1183
1228
|
p_nn.add_argument("--plans", default="nnUNetPlans", type=str)
|
|
@@ -1186,7 +1231,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1186
1231
|
action="store_true",
|
|
1187
1232
|
help="After training succeeds, delete this dataset's nnUNet_raw and "
|
|
1188
1233
|
"nnUNet_preprocessed folders. Leaves nnUNet_results. "
|
|
1189
|
-
"
|
|
1234
|
+
"A later --c run needs that preprocessed data.",
|
|
1190
1235
|
)
|
|
1191
1236
|
p_nn.set_defaults(_handler=_cmd_train_nnunet)
|
|
1192
1237
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Run an nnU-Net planning or preprocessing entry point with a faster file lookup.
|
|
2
|
+
|
|
3
|
+
``python -m seqseg.pipeline.nnunet_cli plan_and_preprocess -d 999 ...`` takes the
|
|
4
|
+
same arguments as ``nnUNetv2_plan_and_preprocess``.
|
|
5
|
+
|
|
6
|
+
nnU-Net finds a dataset's training files by matching every case id against
|
|
7
|
+
every name in ``imagesTr``. That is quadratic in the number of cases, and it
|
|
8
|
+
runs again in fingerprinting, planning, preprocessing, and the label copy:
|
|
9
|
+
about an hour each for several hundred thousand patches. Listing the files
|
|
10
|
+
under ``dataset`` in dataset.json avoids it, but nnU-Net sends dataset.json to
|
|
11
|
+
every preprocessing job, so a long list slows preprocessing far more.
|
|
12
|
+
|
|
13
|
+
This module swaps in a single-pass lookup before nnU-Net starts. It returns
|
|
14
|
+
the same mapping as nnU-Net's folder scan. nnU-Net's files are not modified.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
from typing import Callable, Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
_ENTRY_POINTS = {
|
|
25
|
+
"plan_and_preprocess": "plan_and_preprocess_entry",
|
|
26
|
+
"preprocess": "preprocess_entry",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_CHANNEL_SUFFIX = re.compile(r"_\d{4}$")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def training_files_from_folder(raw_dataset_folder: str, dataset_json: dict) -> Dict[str, dict]:
|
|
33
|
+
"""Same result as nnU-Net's ``imagesTr`` scan, in one directory listing."""
|
|
34
|
+
ending = dataset_json["file_ending"]
|
|
35
|
+
crop = len(ending) + 5
|
|
36
|
+
images_dir = os.path.join(raw_dataset_folder, "imagesTr")
|
|
37
|
+
labels_dir = os.path.join(raw_dataset_folder, "labelsTr")
|
|
38
|
+
|
|
39
|
+
images: Dict[str, List[str]] = {}
|
|
40
|
+
with os.scandir(images_dir) as entries:
|
|
41
|
+
for entry in entries:
|
|
42
|
+
name = entry.name
|
|
43
|
+
if not name.endswith(ending) or not entry.is_file():
|
|
44
|
+
continue
|
|
45
|
+
files = images.setdefault(name[:-crop], [])
|
|
46
|
+
if _CHANNEL_SUFFIX.search(name[: -len(ending)]):
|
|
47
|
+
files.append(name)
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
case: {
|
|
51
|
+
"images": [os.path.join(images_dir, name) for name in sorted(images[case])],
|
|
52
|
+
"label": os.path.join(labels_dir, case + ending),
|
|
53
|
+
}
|
|
54
|
+
for case in sorted(images)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _fast_lookup(original: Callable) -> Callable:
|
|
59
|
+
def get_filenames_of_train_images_and_targets(
|
|
60
|
+
raw_dataset_folder: str, dataset_json: Optional[dict] = None
|
|
61
|
+
):
|
|
62
|
+
if dataset_json is None:
|
|
63
|
+
from batchgenerators.utilities.file_and_folder_operations import load_json
|
|
64
|
+
|
|
65
|
+
dataset_json = load_json(os.path.join(raw_dataset_folder, "dataset.json"))
|
|
66
|
+
if "dataset" in dataset_json:
|
|
67
|
+
return original(raw_dataset_folder, dataset_json)
|
|
68
|
+
return training_files_from_folder(raw_dataset_folder, dataset_json)
|
|
69
|
+
|
|
70
|
+
return get_filenames_of_train_images_and_targets
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def install_fast_lookup() -> None:
|
|
74
|
+
"""Point every loaded nnU-Net module at the single-pass lookup.
|
|
75
|
+
|
|
76
|
+
Modules bind the function with ``from ... import``, so each one is patched.
|
|
77
|
+
Only the parent process looks up files; spawned workers do not need it.
|
|
78
|
+
"""
|
|
79
|
+
import nnunetv2.experiment_planning.plan_and_preprocess_entrypoints # noqa: F401
|
|
80
|
+
import nnunetv2.preprocessing.preprocessors.default_preprocessor # noqa: F401
|
|
81
|
+
import nnunetv2.utilities.utils as nnunet_utils
|
|
82
|
+
|
|
83
|
+
original = nnunet_utils.get_filenames_of_train_images_and_targets
|
|
84
|
+
fast = _fast_lookup(original)
|
|
85
|
+
for name, module in list(sys.modules.items()):
|
|
86
|
+
if not name.startswith("nnunetv2") or module is None:
|
|
87
|
+
continue
|
|
88
|
+
if getattr(module, "get_filenames_of_train_images_and_targets", None) is original:
|
|
89
|
+
module.get_filenames_of_train_images_and_targets = fast
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def main(argv: Optional[List[str]] = None) -> None:
|
|
93
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
94
|
+
if not args or args[0] not in _ENTRY_POINTS:
|
|
95
|
+
raise SystemExit(
|
|
96
|
+
"usage: python -m seqseg.pipeline.nnunet_cli "
|
|
97
|
+
f"{{{','.join(_ENTRY_POINTS)}}} [nnU-Net arguments]"
|
|
98
|
+
)
|
|
99
|
+
install_fast_lookup()
|
|
100
|
+
from nnunetv2.experiment_planning import plan_and_preprocess_entrypoints
|
|
101
|
+
|
|
102
|
+
command = args.pop(0)
|
|
103
|
+
sys.argv = [f"nnUNetv2_{command}", *args]
|
|
104
|
+
getattr(plan_and_preprocess_entrypoints, _ENTRY_POINTS[command])()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
main()
|