seqseg 2.1.8__tar.gz → 2.2.0__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 (97) hide show
  1. {seqseg-2.1.8 → seqseg-2.2.0}/PKG-INFO +1 -1
  2. {seqseg-2.1.8 → seqseg-2.2.0}/pyproject.toml +2 -2
  3. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/__init__.py +10 -0
  4. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/api.py +34 -0
  5. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/cli.py +288 -28
  6. seqseg-2.2.0/seqseg/config/global_coro_retrace.yaml +59 -0
  7. seqseg-2.2.0/seqseg/config/sampler/default_cm.yaml +6 -0
  8. seqseg-2.2.0/seqseg/config/sampler/default_mm.yaml +46 -0
  9. seqseg-2.2.0/seqseg/config/sampler/test.yml +13 -0
  10. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config_models.py +42 -0
  11. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/assembly.py +14 -0
  12. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/centerline.py +369 -92
  13. seqseg-2.2.0/seqseg/modules/checkpoint.py +333 -0
  14. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/initialization.py +5 -3
  15. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/nnunet.py +13 -2
  16. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/sitk_functions.py +109 -0
  17. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/sweep.py +33 -12
  18. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/tracing.py +157 -19
  19. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/__init__.py +2 -0
  20. seqseg-2.2.0/seqseg/pipeline/classic.py +423 -0
  21. seqseg-2.2.0/seqseg/pipeline/continue_run.py +211 -0
  22. seqseg-2.2.0/seqseg/pipeline/nnunet_cli.py +108 -0
  23. seqseg-2.2.0/seqseg/pipeline/plus.py +432 -0
  24. seqseg-2.2.0/seqseg/pipeline/predict.py +110 -0
  25. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/train.py +381 -62
  26. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_api_entry.py +3 -0
  27. seqseg-2.2.0/seqseg/tests/test_centerline_backtracking.py +167 -0
  28. seqseg-2.2.0/seqseg/tests/test_checkpoint.py +390 -0
  29. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_cli_helpers.py +190 -2
  30. seqseg-2.2.0/seqseg/tests/test_global_prediction.py +104 -0
  31. seqseg-2.2.0/seqseg/tests/test_nnunet_cli.py +139 -0
  32. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_nnunet_device.py +8 -2
  33. seqseg-2.2.0/seqseg/tests/test_start_seg.py +228 -0
  34. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_train_data.py +4 -2
  35. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_train_pipeline.py +309 -10
  36. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/PKG-INFO +1 -1
  37. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/SOURCES.txt +12 -0
  38. seqseg-2.1.8/seqseg/pipeline/classic.py +0 -351
  39. seqseg-2.1.8/seqseg/pipeline/plus.py +0 -345
  40. seqseg-2.1.8/seqseg/tests/test_start_seg.py +0 -100
  41. {seqseg-2.1.8 → seqseg-2.2.0}/LICENSE +0 -0
  42. {seqseg-2.1.8 → seqseg-2.2.0}/README.md +0 -0
  43. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/citations.py +0 -0
  44. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/__init__.py +0 -0
  45. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/aorta_tutorial.yaml +0 -0
  46. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global.yaml +0 -0
  47. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_aorta.yaml +0 -0
  48. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_cereb.yaml +0 -0
  49. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_coro.yaml +0 -0
  50. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_debug.yaml +0 -0
  51. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_default.yaml +0 -0
  52. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_pulm.yaml +0 -0
  53. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_seg.yaml +0 -0
  54. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/config/global_test.yaml +0 -0
  55. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/__init__.py +0 -0
  56. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/capping.py +0 -0
  57. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/datasets.py +0 -0
  58. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/evaluation.py +0 -0
  59. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/local_assembly.py +0 -0
  60. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/params.py +0 -0
  61. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/prediction.py +0 -0
  62. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/simvascular.py +0 -0
  63. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/simvascular_contour.py +0 -0
  64. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/tracing_functions.py +0 -0
  65. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/vmtk_functions.py +0 -0
  66. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/modules/vtk_functions.py +0 -0
  67. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/parallel/old_auto_centerline.py +0 -0
  68. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/parallel/parallel_auto_centerline.py +0 -0
  69. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/parallel/parallel_auto_centerline2.py +0 -0
  70. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/parallel/parallel_auto_centerline3.py +0 -0
  71. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/parallel/parallel_auto_centerline4.py +0 -0
  72. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/config.py +0 -0
  73. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/directories.py +0 -0
  74. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/post.py +0 -0
  75. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/pipeline/single_trace.py +0 -0
  76. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/scripts/__init__.py +0 -0
  77. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/scripts/prepare_casx_dataset.py +0 -0
  78. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/scripts/taubin_smooth.py +0 -0
  79. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/seqseg.py +0 -0
  80. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/seqseg_plus.py +0 -0
  81. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/conftest.py +0 -0
  82. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_centerline_get_end_points.py +0 -0
  83. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_centerline_gradient.py +0 -0
  84. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_datasets_paths.py +0 -0
  85. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_nnunet2.py +0 -0
  86. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_prepare_casx_dataset.py +0 -0
  87. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_resample_spacing.py +0 -0
  88. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_segmentation.py +0 -0
  89. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_single_trace_staging.py +0 -0
  90. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_user_paths.py +0 -0
  91. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/tests/test_vtk.py +0 -0
  92. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg/user_paths.py +0 -0
  93. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/dependency_links.txt +0 -0
  94. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/entry_points.txt +0 -0
  95. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/requires.txt +0 -0
  96. {seqseg-2.1.8 → seqseg-2.2.0}/seqseg.egg-info/top_level.txt +0 -0
  97. {seqseg-2.1.8 → seqseg-2.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqseg
3
- Version: 2.1.8
3
+ Version: 2.2.0
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "seqseg"
7
- version = "2.1.8"
7
+ version = "2.2.0"
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"
@@ -54,7 +54,7 @@ homepage = "https://github.com/numisveinsson/SeqSeg"
54
54
  repository = "https://github.com/numisveinsson/SeqSeg"
55
55
 
56
56
  [tool.setuptools]
57
- package-data = { "seqseg" = ["config/*.yaml"] }
57
+ package-data = { "seqseg" = ["config/*.yaml", "config/sampler/*.yaml", "config/sampler/*.yml"] }
58
58
 
59
59
  [project.optional-dependencies]
60
60
  dev = [
@@ -12,6 +12,7 @@ __all__ = (
12
12
  "load_yaml_config",
13
13
  "run_classic_batch",
14
14
  "run_plus_batch",
15
+ "run_global_prediction",
15
16
  "bootstrap_simvascular_project",
16
17
  "bootstrap_simvascular_project_batch",
17
18
  "run_global_centerline_single",
@@ -21,6 +22,7 @@ __all__ = (
21
22
  "seeds_to_potential_branches",
22
23
  "TracingOptions",
23
24
  "run_tracing",
25
+ "continue_tracing",
24
26
  )
25
27
 
26
28
 
@@ -69,6 +71,10 @@ def __getattr__(name: str):
69
71
  from seqseg.pipeline.plus import run_plus_batch
70
72
 
71
73
  return run_plus_batch
74
+ if name == "run_global_prediction":
75
+ from seqseg.pipeline.predict import run_global_prediction
76
+
77
+ return run_global_prediction
72
78
  if name == "bootstrap_simvascular_project":
73
79
  from seqseg.pipeline.post import bootstrap_simvascular_project
74
80
 
@@ -105,4 +111,8 @@ def __getattr__(name: str):
105
111
  from seqseg.api import run_tracing
106
112
 
107
113
  return run_tracing
114
+ if name == "continue_tracing":
115
+ from seqseg.api import continue_tracing
116
+
117
+ return continue_tracing
108
118
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -107,6 +107,7 @@ def seeds_to_potential_branches(
107
107
  raise ValueError("old_point and new_point must have length 3")
108
108
  step = create_step_dict(old, r, new, r, None)
109
109
  step["connection"] = [0, 0]
110
+ step["init_seed"] = True
110
111
  out.append(step)
111
112
  return out
112
113
 
@@ -203,3 +204,36 @@ def run_tracing(
203
204
  force_cpu=opts.force_cpu,
204
205
  )
205
206
  return trace_centerline_from_context(ctx)
207
+
208
+
209
+ def continue_tracing(
210
+ case_dir: str,
211
+ extra_steps: int,
212
+ seeds: Optional[Sequence[Union[BranchSeed, Mapping[str, Any], Sequence[Any]]]] = None,
213
+ *,
214
+ max_n_branches: Optional[int] = None,
215
+ max_n_steps_per_branch: Optional[int] = None,
216
+ force_cpu: Optional[bool] = None,
217
+ ) -> TracingResult:
218
+ """
219
+ Continue a CLI/batch run from ``{case_dir}/checkpoint`` for ``extra_steps`` steps.
220
+
221
+ ``seeds`` accept the same forms as :func:`seeds_to_potential_branches`;
222
+ they are traced as new initialization seeds once the open branch ends.
223
+ Outputs are rewritten with the new step count and the checkpoint is
224
+ replaced, so the run can be continued again.
225
+ """
226
+ from seqseg.pipeline.continue_run import run_continue
227
+
228
+ triples = [
229
+ [s["old point"], s["point"], s["radius"]]
230
+ for s in seeds_to_potential_branches(seeds or [])
231
+ ]
232
+ return run_continue(
233
+ case_dir,
234
+ extra_steps,
235
+ seeds=triples,
236
+ max_n_branches=max_n_branches,
237
+ max_n_steps_per_branch=max_n_steps_per_branch,
238
+ force_cpu=force_cpu,
239
+ )
@@ -16,11 +16,19 @@ import faulthandler
16
16
  import numpy as np
17
17
 
18
18
  from seqseg.citations import print_citation_banner
19
- from seqseg.config_models import AlgorithmConfig, NnUNetModelSpec, load_yaml_config
19
+ from seqseg.config_models import (
20
+ AlgorithmConfig,
21
+ NnUNetModelSpec,
22
+ load_yaml_config,
23
+ resolve_nnunet_model_folder,
24
+ )
20
25
  from seqseg.modules import sitk_functions as sf
21
26
  from seqseg.modules.datasets import get_testing_samples
27
+ from seqseg.modules.checkpoint import has_checkpoint
22
28
  from seqseg.pipeline.classic import run_classic_batch
29
+ from seqseg.pipeline.continue_run import run_continue, seeds_from_json, seeds_from_xyzr
23
30
  from seqseg.pipeline.plus import run_plus_batch
31
+ from seqseg.pipeline.predict import run_global_prediction
24
32
  from seqseg.pipeline.single_trace import prepare_single_image_dataset
25
33
  from seqseg.pipeline.post import (
26
34
  bootstrap_simvascular_project,
@@ -190,6 +198,20 @@ def _add_plus_trace_args(p: argparse.ArgumentParser) -> None:
190
198
  type=str,
191
199
  default="Dataset012_COROASOCACT",
192
200
  )
201
+ p.add_argument(
202
+ "-global_seg_dir",
203
+ "--global_seg_dir",
204
+ "--global-seg-dir",
205
+ dest="global_seg_dir",
206
+ default=None,
207
+ type=str,
208
+ help=(
209
+ "Folder of per-case global segmentations. Skips the global "
210
+ "nnU-Net prediction and does not use global weights. "
211
+ "Accepts {case}_segmentation.mha with optional "
212
+ "{case}_probability.mha, or {case}{img_ext}"
213
+ ),
214
+ )
193
215
 
194
216
 
195
217
  def _seqseg_version() -> str:
@@ -244,6 +266,15 @@ def _validate_trace_batch(ns: argparse.Namespace) -> None:
244
266
  ns.start_seg = start_seg
245
267
 
246
268
 
269
+ def _resolve_run_weights(spec: NnUNetModelSpec) -> str:
270
+ """Pick the trainer folder, or exit when the dataset has several."""
271
+ try:
272
+ return resolve_nnunet_model_folder(spec)
273
+ except ValueError as e:
274
+ print(f"seqseg: {e}", file=sys.stderr)
275
+ sys.exit(2)
276
+
277
+
247
278
  def _cmd_trace_batch(ns: argparse.Namespace) -> None:
248
279
  _validate_trace_batch(ns)
249
280
  faulthandler.enable()
@@ -265,7 +296,8 @@ def _cmd_trace_batch(ns: argparse.Namespace) -> None:
265
296
  fold=ns.fold,
266
297
  scale=ns.scale,
267
298
  )
268
- dir_model_weights = spec.model_folder()
299
+ dir_model_weights = _resolve_run_weights(spec)
300
+ print(f"Using nnU-Net weights from:\n {dir_model_weights}")
269
301
 
270
302
  testing_samples, directory_data = get_testing_samples(ns.train_dataset, data_dir)
271
303
  print("Testing samples about to run:")
@@ -308,6 +340,7 @@ def _cmd_trace_batch(ns: argparse.Namespace) -> None:
308
340
  simvascular=bool(ns.simvascular),
309
341
  force_cpu=bool(ns.force_cpu),
310
342
  start_seg_path=ns.start_seg,
343
+ config_name=ns.config_name,
311
344
  start_time_global=t0,
312
345
  )
313
346
  print("\nTotal calculation time for all cases is: ")
@@ -328,24 +361,44 @@ def _cmd_trace_plus_batch(ns: argparse.Namespace) -> None:
328
361
  except Exception as e: # noqa: BLE001
329
362
  print(e)
330
363
 
331
- dir_model_weights_seqseg = NnUNetModelSpec(
332
- train_dataset=ns.seqseg_train_dataset,
333
- nnunet_type=ns.seqseg_test_name,
334
- results_path=ns.nnunet_results_path,
335
- fold=ns.seqseg_fold,
336
- scale=ns.seqseg_scale,
337
- ).model_folder()
338
- dir_model_weights_global = NnUNetModelSpec(
339
- train_dataset=ns.global_train_dataset,
340
- nnunet_type=ns.global_test_name,
341
- results_path=ns.nnunet_results_path,
342
- fold=ns.global_fold,
343
- scale=ns.global_scale,
344
- ).model_folder()
364
+ dir_model_weights_seqseg = _resolve_run_weights(
365
+ NnUNetModelSpec(
366
+ train_dataset=ns.seqseg_train_dataset,
367
+ nnunet_type=ns.seqseg_test_name,
368
+ results_path=ns.nnunet_results_path,
369
+ fold=ns.seqseg_fold,
370
+ scale=ns.seqseg_scale,
371
+ )
372
+ )
373
+ global_seg_dir = getattr(ns, "global_seg_dir", None)
374
+ if global_seg_dir:
375
+ global_seg_dir = os.path.abspath(os.path.expanduser(global_seg_dir))
376
+ if not os.path.isdir(global_seg_dir):
377
+ print(
378
+ "seqseg run plus batch: global segmentation directory not found: "
379
+ f"{global_seg_dir}",
380
+ file=sys.stderr,
381
+ )
382
+ sys.exit(2)
383
+ dir_model_weights_global = None
384
+ else:
385
+ dir_model_weights_global = _resolve_run_weights(
386
+ NnUNetModelSpec(
387
+ train_dataset=ns.global_train_dataset,
388
+ nnunet_type=ns.global_test_name,
389
+ results_path=ns.nnunet_results_path,
390
+ fold=ns.global_fold,
391
+ scale=ns.global_scale,
392
+ )
393
+ )
345
394
  print("Using nnUNet model weights from:")
346
395
  print(dir_model_weights_seqseg)
347
- print("Using nnUNet model weights for global segmentation from:")
348
- print(dir_model_weights_global)
396
+ if global_seg_dir:
397
+ print("Using global segmentations from:")
398
+ print(global_seg_dir)
399
+ else:
400
+ print("Using nnUNet model weights for global segmentation from:")
401
+ print(dir_model_weights_global)
349
402
 
350
403
  testing_samples, directory_data = get_testing_samples(ns.seqseg_train_dataset, data_dir)
351
404
  print("Testing samples about to run:")
@@ -387,6 +440,8 @@ def _cmd_trace_plus_batch(ns: argparse.Namespace) -> None:
387
440
  simvascular=bool(ns.simvascular),
388
441
  force_cpu=bool(ns.force_cpu),
389
442
  start_seg_path=ns.start_seg,
443
+ global_seg_dir=global_seg_dir,
444
+ config_name=ns.config_name,
390
445
  start_time_global=t0,
391
446
  )
392
447
  print("\nTotal calculation time is: ")
@@ -529,12 +584,104 @@ def _cmd_trace_single(ns: argparse.Namespace) -> None:
529
584
  simvascular=bool(ns.simvascular),
530
585
  force_cpu=bool(ns.force_cpu),
531
586
  start_seg_path=getattr(ns, "start_seg", None),
587
+ config_name=ns.config_name,
532
588
  start_time_global=t0,
533
589
  )
534
590
  print("\nTotal calculation time for run single: ")
535
591
  print(f"{((time.time() - t0) / 60):.2f} min\n")
536
592
 
537
593
 
594
+ def _cmd_trace_continue(ns: argparse.Namespace) -> None:
595
+ faulthandler.enable()
596
+ t0 = time.time()
597
+ case_dir = os.path.abspath(os.path.expanduser(ns.case_dir))
598
+ if not has_checkpoint(case_dir):
599
+ print(
600
+ f"seqseg run continue: no checkpoint in {case_dir}/checkpoint. "
601
+ "Only runs made with a SeqSeg version that writes checkpoints "
602
+ "can be continued.",
603
+ file=sys.stderr,
604
+ )
605
+ sys.exit(2)
606
+ if ns.extra_steps < 0:
607
+ print("seqseg run continue: --extra-steps must be >= 0", file=sys.stderr)
608
+ sys.exit(2)
609
+
610
+ seeds: List[list] = []
611
+ try:
612
+ if ns.seeds_json:
613
+ if not os.path.isfile(ns.seeds_json):
614
+ print(f"--seeds-json not found: {ns.seeds_json}", file=sys.stderr)
615
+ sys.exit(2)
616
+ with open(os.path.join(case_dir, "checkpoint", "manifest.json"),
617
+ encoding="utf-8") as f:
618
+ case = json.load(f).get("case")
619
+ seeds += seeds_from_json(ns.seeds_json, case)
620
+ if ns.seed:
621
+ seeds += seeds_from_xyzr(
622
+ [list(map(float, s)) for s in ns.seed],
623
+ tangent=tuple(ns.tangent),
624
+ seed_step=ns.seed_step,
625
+ )
626
+ except ValueError as e:
627
+ print(f"seqseg run continue: {e}", file=sys.stderr)
628
+ sys.exit(2)
629
+
630
+ run_continue(
631
+ case_dir,
632
+ ns.extra_steps,
633
+ seeds=seeds,
634
+ max_n_branches=ns.max_n_branches,
635
+ max_n_steps_per_branch=ns.max_n_steps_per_branch,
636
+ force_cpu=True if ns.force_cpu else None,
637
+ )
638
+ print("\nTotal calculation time for run continue: ")
639
+ print(f"{((time.time() - t0) / 60):.2f} min\n")
640
+
641
+
642
+ def _cmd_run_global(ns: argparse.Namespace) -> None:
643
+ model_folder = os.path.abspath(os.path.expanduser(ns.model_folder))
644
+ if not os.path.isdir(model_folder):
645
+ print(
646
+ f"seqseg run global: model folder not found: {model_folder}",
647
+ file=sys.stderr,
648
+ )
649
+ sys.exit(2)
650
+ outdir = os.path.abspath(os.path.expanduser(ns.outdir))
651
+ image = None
652
+ image_dir = None
653
+ if ns.image:
654
+ image = os.path.abspath(os.path.expanduser(ns.image))
655
+ if not os.path.isfile(image):
656
+ print(
657
+ f"seqseg run global: image not found: {image}",
658
+ file=sys.stderr,
659
+ )
660
+ sys.exit(2)
661
+ else:
662
+ image_dir = os.path.abspath(os.path.expanduser(ns.image_dir))
663
+ if not os.path.isdir(image_dir):
664
+ print(
665
+ f"seqseg run global: image folder not found: {image_dir}",
666
+ file=sys.stderr,
667
+ )
668
+ sys.exit(2)
669
+ try:
670
+ run_global_prediction(
671
+ model_folder=model_folder,
672
+ outdir=outdir,
673
+ image=image,
674
+ image_dir=image_dir,
675
+ img_ext=ns.img_ext,
676
+ fold=ns.fold,
677
+ scale=ns.scale,
678
+ force_cpu=bool(ns.force_cpu),
679
+ )
680
+ except FileNotFoundError as e:
681
+ print(f"seqseg run global: {e}", file=sys.stderr)
682
+ sys.exit(2)
683
+
684
+
538
685
  def _cmd_doctor(ns: argparse.Namespace) -> None:
539
686
  import importlib
540
687
 
@@ -709,8 +856,9 @@ def _cmd_train_prepare(ns: argparse.Namespace) -> None:
709
856
  )
710
857
  elif result.removed_staging:
711
858
  print(
712
- " Extracted volumes were linked into nnU-Net raw and the "
713
- "temporary folder was removed."
859
+ " Extracted subvolumes were removed after conversion. "
860
+ "done.txt and the .txt/.csv metadata were kept in\n"
861
+ f" {result.extracted_dir}"
714
862
  )
715
863
  else:
716
864
  kind = "Whole volumes" if ns.global_volumes else "Patches"
@@ -829,7 +977,7 @@ def _build_parser() -> argparse.ArgumentParser:
829
977
 
830
978
  run_grp = sub.add_parser(
831
979
  "run",
832
- help="Run segmentation and vessel tracing (batch, plus, or single volume)",
980
+ help="Run segmentation and vessel tracing (batch, plus, single, or global)",
833
981
  )
834
982
  run_sub = run_grp.add_subparsers(dest="run_cmd", required=True)
835
983
 
@@ -944,6 +1092,109 @@ def _build_parser() -> argparse.ArgumentParser:
944
1092
  )
945
1093
  p_single.set_defaults(_handler=_cmd_trace_single)
946
1094
 
1095
+ p_continue = run_sub.add_parser(
1096
+ "continue",
1097
+ help="Continue a traced case from its checkpoint for more steps",
1098
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
1099
+ )
1100
+ p_continue.add_argument(
1101
+ "--case-dir",
1102
+ required=True,
1103
+ type=str,
1104
+ help="Case output folder holding checkpoint/ (e.g. <outdir>/3d_fullres_case001)",
1105
+ )
1106
+ p_continue.add_argument(
1107
+ "--extra-steps",
1108
+ required=True,
1109
+ type=int,
1110
+ help="Steps to add on top of the steps already taken",
1111
+ )
1112
+ p_continue.add_argument(
1113
+ "--seed",
1114
+ action="append",
1115
+ nargs=4,
1116
+ type=float,
1117
+ metavar=("X", "Y", "Z", "R"),
1118
+ help="New seed traced after the open branch ends (repeat for multiple)",
1119
+ )
1120
+ p_continue.add_argument(
1121
+ "--tangent",
1122
+ nargs=3,
1123
+ type=float,
1124
+ default=[0.0, 0.0, 1.0],
1125
+ metavar=("TX", "TY", "TZ"),
1126
+ help="Used with --seed to place the paired old point",
1127
+ )
1128
+ p_continue.add_argument(
1129
+ "--seed-step",
1130
+ default=1.0,
1131
+ type=float,
1132
+ help="Physical step along --tangent for old-point when using --seed",
1133
+ )
1134
+ p_continue.add_argument(
1135
+ "--seeds-json",
1136
+ default=None,
1137
+ type=str,
1138
+ help="New seeds as [old_point, new_point, radius] triples: a seeds.json "
1139
+ "(entry matching the case), one case object, or a bare list",
1140
+ )
1141
+ p_continue.add_argument(
1142
+ "--max-n-branches",
1143
+ default=None,
1144
+ type=int,
1145
+ help="Raise the branch cap (default: value from the checkpoint)",
1146
+ )
1147
+ p_continue.add_argument(
1148
+ "--max-n-steps-per-branch",
1149
+ default=None,
1150
+ type=int,
1151
+ help="Change the per-branch cap (default: value from the checkpoint)",
1152
+ )
1153
+ p_continue.add_argument(
1154
+ "-cpu",
1155
+ "--cpu",
1156
+ dest="force_cpu",
1157
+ action="store_true",
1158
+ help="Force nnU-Net inference on CPU even if a GPU is available",
1159
+ )
1160
+ p_continue.set_defaults(_handler=_cmd_trace_continue)
1161
+
1162
+ p_global = run_sub.add_parser(
1163
+ "global",
1164
+ help="Whole-volume nnU-Net prediction (one image or a folder)",
1165
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
1166
+ )
1167
+ global_src = p_global.add_mutually_exclusive_group(required=True)
1168
+ global_src.add_argument("--image", type=str, help="Input volume path")
1169
+ global_src.add_argument(
1170
+ "--image-dir",
1171
+ type=str,
1172
+ help="Folder of input volumes (not a SeqSeg dataset)",
1173
+ )
1174
+ p_global.add_argument("--outdir", required=True, type=str)
1175
+ p_global.add_argument(
1176
+ "--model-folder",
1177
+ required=True,
1178
+ type=str,
1179
+ help="nnU-Net trainer folder (…/nnUNetTrainer__nnUNetPlans__3d_fullres)",
1180
+ )
1181
+ p_global.add_argument("--fold", default="all", type=str)
1182
+ p_global.add_argument("--scale", default=1.0, type=float)
1183
+ p_global.add_argument(
1184
+ "--img-ext",
1185
+ default=None,
1186
+ type=str,
1187
+ help="Volume extension when using --image-dir (for example .nii.gz)",
1188
+ )
1189
+ p_global.add_argument(
1190
+ "-cpu",
1191
+ "--cpu",
1192
+ dest="force_cpu",
1193
+ action="store_true",
1194
+ help="Force nnU-Net inference on CPU even if a GPU is available",
1195
+ )
1196
+ p_global.set_defaults(_handler=_cmd_run_global)
1197
+
947
1198
  post = sub.add_parser("post", help="Post-process existing outputs")
948
1199
  post_sub = post.add_subparsers(dest="post_cmd", required=True)
949
1200
  gcl = post_sub.add_parser("global-centerline")
@@ -1061,8 +1312,9 @@ def _build_parser() -> argparse.ArgumentParser:
1061
1312
  default=None,
1062
1313
  type=str,
1063
1314
  help="Optional scratch directory for extracted patches or whole volumes. "
1064
- "Default: <parent of nnUNet_raw>/_seqseg_extracted, removed after "
1065
- "conversion. Pass this to keep extracts or resume with --skip-sample.",
1315
+ "Default: nnUNet_raw/DatasetXXX_*/_seqseg_extracted. Subvolumes are "
1316
+ "removed after conversion; done.txt and .txt/.csv metadata are kept. "
1317
+ "Pass this to keep a copy elsewhere.",
1066
1318
  )
1067
1319
  p_prep.add_argument(
1068
1320
  "--name",
@@ -1094,7 +1346,7 @@ def _build_parser() -> argparse.ArgumentParser:
1094
1346
  "-config_name",
1095
1347
  default="default_mm",
1096
1348
  type=str,
1097
- help="Sampler YAML name/path (from vascular-segment-sampler)",
1349
+ help="Sampler YAML name/path (default_mm, default_cm, test, or a file)",
1098
1350
  )
1099
1351
  p_prep.add_argument(
1100
1352
  "--nnunet-raw",
@@ -1156,7 +1408,8 @@ def _build_parser() -> argparse.ArgumentParser:
1156
1408
  p_prep.add_argument(
1157
1409
  "--keep-extracted",
1158
1410
  action="store_true",
1159
- help="Keep <nnunet-root>/_seqseg_extracted after conversion into nnUNet_raw",
1411
+ help="Keep extracted subvolumes in nnUNet_raw/DatasetXXX_*/_seqseg_extracted "
1412
+ "after conversion into nnUNet_raw",
1160
1413
  )
1161
1414
  p_prep.add_argument(
1162
1415
  "--configuration",
@@ -1179,8 +1432,9 @@ def _build_parser() -> argparse.ArgumentParser:
1179
1432
  p_prep.add_argument(
1180
1433
  "--preprocess-only",
1181
1434
  action="store_true",
1182
- help="Preprocess another configuration from an existing raw dataset "
1183
- "and plans file. Does not extract or convert subvolumes",
1435
+ help="Use an existing nnUNet_raw dataset. Plans and preprocesses it "
1436
+ "when plans are missing; otherwise resamples --configuration. "
1437
+ "Does not extract or convert subvolumes",
1184
1438
  )
1185
1439
  p_prep.set_defaults(_handler=_cmd_train_prepare, img_ext=None)
1186
1440
 
@@ -1220,7 +1474,13 @@ def _build_parser() -> argparse.ArgumentParser:
1220
1474
  action="store_true",
1221
1475
  help="Continue training from checkpoint_latest.pth (nnU-Net --c)",
1222
1476
  )
1223
- p_nn.add_argument("--trainer", default="nnUNetTrainer", type=str)
1477
+ p_nn.add_argument(
1478
+ "--trainer",
1479
+ default=None,
1480
+ type=str,
1481
+ help="nnU-Net trainer class. Default: nnUNetTrainer, or "
1482
+ "nnUNetTrainerNoDeepSupervision when the plan downsamples at most once",
1483
+ )
1224
1484
  p_nn.add_argument("--plans", default="nnUNetPlans", type=str)
1225
1485
  p_nn.add_argument(
1226
1486
  "--cleanup",
@@ -0,0 +1,59 @@
1
+ # Coronary config with retracing prevention on.
2
+ # Same settings as global_coro.yaml except PREVENT_RETRACE.
3
+ # if we are tracing a segmentation or predicting a segmentation as we go
4
+ # True if we have a segmentation, False if we are predicting
5
+ SEGMENTATION: False # usually False
6
+
7
+ DEBUG: False
8
+ DEBUG_STEP: 100
9
+
10
+ ANIMATION: True # to write out animation files
11
+ ANIMATION_STEPS: 3 # write out animation every x steps
12
+
13
+ RETRACE: False # retrace strategry
14
+ TIME_ANALYSIS: False # if we want to write out how long each step take
15
+ TREE_ANALYSIS: False
16
+
17
+ VOLUME_SIZE_RATIO: 5.5 # default 5, 5.5 for coronaries, 4 for segs
18
+ MAGN_RADIUS: 1
19
+ ADD_RADIUS: 0.3
20
+ MAX_PERC_ENLARGE: 0.33 # if percent of blood in a given volume goes above this, we try enlarging volume, default 0.33 for coronaries
21
+ MIN_RADIUS: 0.3 # mm (keep in mm)
22
+ MIN_RES: 5
23
+ CENTERLINE_EXTRACTION_VMTK: False # if we want to extract centerline using VMTK
24
+ CENTERLINE_MERGE_METHOD: clean # clean (vtkCleanPolyData) or tree
25
+ TYPE_CENT: profileidlist #profileidlist # can also be profileidlist / pointlist
26
+
27
+ NR_CHANCES: 3
28
+ NR_ALLOW_RETRACE_STEPS: 10
29
+ PREVENT_RETRACE: True
30
+ SORT_NEXT: True
31
+ MERGE_NEXT: False
32
+
33
+ ASSEMBLY_EVERY_N: 20 # add to assembly every N steps
34
+ USE_BUFFER: True # buffer the assembly (so not to mess with retrace prevention)
35
+ BUFFER_N: 20 # add to assembly steps buff_N steps ago
36
+ WEIGHTED_ASSEMBLY: True # if assembly should be weighted
37
+ WEIGHT_TYPE: gaussian # type of weighting for assembly
38
+ CENT_MAX_SPACING: 0.3 # mm; max spacing per dim for final assembly (refine only)
39
+
40
+ FORCEFUL_SIDEBRANCH: False
41
+ FORCEFUL_SIDEBRANCH_MAGN_RADIUS: 1.1
42
+
43
+ MEGA_SUBVOLUME: False # extract centerline from previous N steps, instead of just 1
44
+ NR_MEGA_SUB: 3 # how many steps to assemble into mega subvolume
45
+
46
+ STOP_PRE: True # whether to stop tracing prematurely, if True then options below will be used
47
+ STOP_RADIUS: 0.4 # (mm) 0.48 if radius goes below, stop tracing
48
+ MAX_STEPS_BRANCH: 100 # max num of steps per branch, stop when reached
49
+
50
+ RESTART_BRANCH: True
51
+
52
+ # SimVascular: after each completed branch, write one .ctgr contour group (2D
53
+ # contours along that branch's .pth) using the live global probability assembly.
54
+ SIMVASCULAR_CONTOURS: True
55
+ SIMVASCULAR_CONTOUR_ISO: 0.5
56
+ SIMVASCULAR_CONTOUR_STRIDE: 2
57
+ SIMVASCULAR_CONTOUR_HALF_EXTENT_MM: 50.0
58
+ # closest_to_path | largest — see simvascular_contour.py
59
+ SIMVASCULAR_CONTOUR_COMPONENT: closest_to_path
@@ -0,0 +1,6 @@
1
+ # SeqSeg sampler config for centimeter data (VMR / SimVascular). Overlay on default_mm.yaml.
2
+ # Adapted from vascular-segment-sampler 0.7.0 config/default_cm.yaml.
3
+ # Image, truth, surface, and centerline must all be in cm.
4
+
5
+ RADIUS_ADD: 0.02 # absolute add to radius in cm (0.2 mm)
6
+ LARGE_RADIUS_THRESH: 0.4 # radius in cm at which MOVE_SLOWER_LARGE applies (4 mm)
@@ -0,0 +1,46 @@
1
+ # SeqSeg sampler default for millimeter data. Base for every named sampler config.
2
+ # Adapted from vascular-segment-sampler 0.7.0 config/default_mm.yaml, keeping only
3
+ # the keys the sampler requires for `seqseg train prepare`.
4
+ # Image, truth, surface, and centerline must all be in mm.
5
+ # For centimeter data (VMR), use --config-name default_cm.
6
+ #
7
+ # The optional outputs below are off. Turning one on in your own YAML also needs
8
+ # its companion keys, e.g. RESAMPLE_SIZE with RESAMPLE_VOLUMES, OUTLET_CLASSES
9
+ # with WRITE_SURFACE/WRITE_CENTERLINE, N_SLICES and RESAMPLE_CROSS_IMG with
10
+ # WRITE_TRAJECTORIES/WRITE_CROSS_SECTIONAL.
11
+
12
+ DATASET_NAME: 'other'
13
+
14
+ EXTRACT_VOLUMES: True
15
+ WRITE_SAMPLES: True
16
+ WRITE_IMG: True
17
+ BINARIZE: True
18
+ REMOVE_OTHER: True # Remove other unconnected vessels in gt volume, can cause problems where vessels are close
19
+ ROTATE_VOLUMES: False
20
+ RESAMPLE_VOLUMES: False
21
+ CAPFREE: False # whether to require a certain amount of cap-free volume
22
+ CAPFREE_PROP: 0.0 # how much of volume must be cap free, per sidelength
23
+
24
+ WRITE_SURFACE: False
25
+ WRITE_CENTERLINE: False
26
+ WRITE_VTK: False
27
+ WRITE_DISCRETE_CENTERLINE: False
28
+ WRITE_OUTLET_STATS: False
29
+ WRITE_CROSS_SECTIONAL: False
30
+ WRITE_TRAJECTORIES: False
31
+
32
+ # MOVE_DIST, MU_SIZE, SIGMA_SHIFT are multiples of local radius. RADIUS_ADD and
33
+ # LARGE_RADIUS_THRESH are absolute, in the centerline's unit.
34
+ MOVE_DIST: 1.5 # how far to move along centerline between sampling, in radii
35
+ MOVE_SLOWER_LARGE: 0.5 # how much slower for bigger vessels
36
+ MOVE_SLOWER_BIFURC: 0.5 # how much slower at bifurcations
37
+ LARGE_RADIUS_THRESH: 4.0 # radius in mm at which MOVE_SLOWER_LARGE applies
38
+ RADIUS_ADD: 0.2 # absolute add to radius in mm (before RADIUS_SCALE)
39
+ RADIUS_SCALE: 1 # keep 1 when image and centerline units match
40
+ MU_SIZE: 5 # mean for subvolume-radius ratio
41
+ SIGMA_SIZE: 0.8 # standard deviation for subvolume-radius ratio
42
+ MU_SHIFT: 0 # mean for subvolume shift off centerline
43
+ SIGMA_SHIFT: 0.8 # standard deviation for subvolume shift off centerline
44
+ NUMBER_SAMPLES: 3 # samples at each point along vessel
45
+ NUMBER_SAMPLES_BIFURC: 5 # samples at a bifurcation
46
+ NUMBER_SAMPLES_START: 3 # samples at start of vessel (first 5%)
@@ -0,0 +1,13 @@
1
+ # SeqSeg smoke-test sampler overlay on default_mm.yaml.
2
+ # Adapted from vascular-segment-sampler 0.7.0 config/test.yml.
3
+ # One extract per point, long steps, and a hard cap so a case finishes quickly.
4
+
5
+ MAX_SAMPLES: 12
6
+
7
+ MOVE_DIST: 8 # centerline step, in radii (default 1.5)
8
+ MOVE_SLOWER_LARGE: 1
9
+ MOVE_SLOWER_BIFURC: 1
10
+
11
+ NUMBER_SAMPLES: 1
12
+ NUMBER_SAMPLES_BIFURC: 1
13
+ NUMBER_SAMPLES_START: 1