seqseg 2.2.0__tar.gz → 2.2.1__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 (94) hide show
  1. {seqseg-2.2.0 → seqseg-2.2.1}/PKG-INFO +1 -1
  2. {seqseg-2.2.0 → seqseg-2.2.1}/pyproject.toml +1 -1
  3. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/assembly.py +0 -1
  4. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/centerline.py +118 -30
  5. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/initialization.py +0 -1
  6. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/tracing.py +0 -1
  7. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_centerline_backtracking.py +40 -1
  8. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/PKG-INFO +1 -1
  9. {seqseg-2.2.0 → seqseg-2.2.1}/LICENSE +0 -0
  10. {seqseg-2.2.0 → seqseg-2.2.1}/README.md +0 -0
  11. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/__init__.py +0 -0
  12. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/api.py +0 -0
  13. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/citations.py +0 -0
  14. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/cli.py +0 -0
  15. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/__init__.py +0 -0
  16. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/aorta_tutorial.yaml +0 -0
  17. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global.yaml +0 -0
  18. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_aorta.yaml +0 -0
  19. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_cereb.yaml +0 -0
  20. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_coro.yaml +0 -0
  21. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_coro_retrace.yaml +0 -0
  22. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_debug.yaml +0 -0
  23. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_default.yaml +0 -0
  24. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_pulm.yaml +0 -0
  25. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_seg.yaml +0 -0
  26. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/global_test.yaml +0 -0
  27. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/sampler/default_cm.yaml +0 -0
  28. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/sampler/default_mm.yaml +0 -0
  29. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config/sampler/test.yml +0 -0
  30. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/config_models.py +0 -0
  31. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/__init__.py +0 -0
  32. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/capping.py +0 -0
  33. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/checkpoint.py +0 -0
  34. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/datasets.py +0 -0
  35. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/evaluation.py +0 -0
  36. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/local_assembly.py +0 -0
  37. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/nnunet.py +0 -0
  38. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/params.py +0 -0
  39. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/prediction.py +0 -0
  40. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/simvascular.py +0 -0
  41. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/simvascular_contour.py +0 -0
  42. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/sitk_functions.py +0 -0
  43. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/sweep.py +0 -0
  44. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/tracing_functions.py +0 -0
  45. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/vmtk_functions.py +0 -0
  46. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/modules/vtk_functions.py +0 -0
  47. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/parallel/old_auto_centerline.py +0 -0
  48. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/parallel/parallel_auto_centerline.py +0 -0
  49. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/parallel/parallel_auto_centerline2.py +0 -0
  50. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/parallel/parallel_auto_centerline3.py +0 -0
  51. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/parallel/parallel_auto_centerline4.py +0 -0
  52. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/__init__.py +0 -0
  53. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/classic.py +0 -0
  54. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/config.py +0 -0
  55. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/continue_run.py +0 -0
  56. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/directories.py +0 -0
  57. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/nnunet_cli.py +0 -0
  58. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/plus.py +0 -0
  59. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/post.py +0 -0
  60. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/predict.py +0 -0
  61. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/single_trace.py +0 -0
  62. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/pipeline/train.py +0 -0
  63. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/scripts/__init__.py +0 -0
  64. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/scripts/prepare_casx_dataset.py +0 -0
  65. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/scripts/taubin_smooth.py +0 -0
  66. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/seqseg.py +0 -0
  67. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/seqseg_plus.py +0 -0
  68. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/conftest.py +0 -0
  69. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_api_entry.py +0 -0
  70. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_centerline_get_end_points.py +0 -0
  71. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_centerline_gradient.py +0 -0
  72. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_checkpoint.py +0 -0
  73. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_cli_helpers.py +0 -0
  74. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_datasets_paths.py +0 -0
  75. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_global_prediction.py +0 -0
  76. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_nnunet2.py +0 -0
  77. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_nnunet_cli.py +0 -0
  78. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_nnunet_device.py +0 -0
  79. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_prepare_casx_dataset.py +0 -0
  80. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_resample_spacing.py +0 -0
  81. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_segmentation.py +0 -0
  82. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_single_trace_staging.py +0 -0
  83. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_start_seg.py +0 -0
  84. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_train_data.py +0 -0
  85. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_train_pipeline.py +0 -0
  86. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_user_paths.py +0 -0
  87. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/tests/test_vtk.py +0 -0
  88. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg/user_paths.py +0 -0
  89. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/SOURCES.txt +0 -0
  90. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/dependency_links.txt +0 -0
  91. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/entry_points.txt +0 -0
  92. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/requires.txt +0 -0
  93. {seqseg-2.2.0 → seqseg-2.2.1}/seqseg.egg-info/top_level.txt +0 -0
  94. {seqseg-2.2.0 → seqseg-2.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqseg
3
- Version: 2.2.0
3
+ Version: 2.2.1
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.2.0"
7
+ version = "2.2.1"
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"
@@ -1004,7 +1004,6 @@ def calc_centerline_global(predicted_vessels, initial_seeds,
1004
1004
  centerline_poly, success_info = calc_multi_component_centerlines(
1005
1005
  predicted_vessels,
1006
1006
  nr_seeds=nr_seeds,
1007
- min_res=500,
1008
1007
  out_dir=None,
1009
1008
  write_files=False,
1010
1009
  move_target_if_fail=False,
@@ -1339,8 +1339,116 @@ def backtracking_gradient(gradient, distance_map_surf_np,
1339
1339
  return points, success
1340
1340
 
1341
1341
 
1342
+ # Centerline grids are isotropic. Three voxels across the inscribed radius
1343
+ # keeps the lumen wide enough for the finite-difference gradient. The
1344
+ # spacing clamp refuses both a one-voxel tube and a needlessly fine grid,
1345
+ # and the voxel budget caps the float64 gradient.
1346
+ _VOXELS_PER_RADIUS = 3.0
1347
+ _MIN_SPACING_MM = 0.35
1348
+ _MAX_SPACING_MM = 1.0
1349
+ _MAX_CENTERLINE_VOXELS = 20_000_000
1350
+ _GRID_SIZE_TOLERANCE = 0.10
1351
+
1352
+
1353
+ def centerline_grid_size(size, spacing, radius_mm):
1354
+ """Choose an isotropic centerline grid from the inscribed radius.
1355
+
1356
+ Parameters
1357
+ ----------
1358
+ size : sequence of int
1359
+ Current voxel counts, one per axis.
1360
+ spacing : sequence of float
1361
+ Current spacing in millimeters, one per axis.
1362
+ radius_mm : float
1363
+ Maximum inscribed radius in millimeters. A non-positive or
1364
+ non-finite radius keeps the input grid.
1365
+
1366
+ Returns
1367
+ -------
1368
+ new_size : tuple of int
1369
+ new_spacing : tuple of float
1370
+ Millimeters. Equal to ``spacing`` when the input grid is kept.
1371
+ target_spacing : float or None
1372
+ Spacing the radius and voxel budget asked for, before the
1373
+ 10% size check. None when the radius does not define a grid.
1374
+ resample : bool
1375
+ False when ``new_size`` is the input size.
1376
+ """
1377
+ size_in = tuple(int(v) for v in np.asarray(size, dtype=np.int64).reshape(3))
1378
+ spacing_in = tuple(
1379
+ float(v) for v in np.asarray(spacing, dtype=np.float64).reshape(3))
1380
+ radius = float(radius_mm)
1381
+ if not np.isfinite(radius) or radius <= 0.0:
1382
+ return size_in, spacing_in, None, False
1383
+
1384
+ size_f = np.asarray(size_in, dtype=np.float64)
1385
+ spacing_f = np.asarray(spacing_in, dtype=np.float64)
1386
+ extent = size_f * spacing_f
1387
+ target = float(np.clip(
1388
+ radius / _VOXELS_PER_RADIUS, _MIN_SPACING_MM, _MAX_SPACING_MM))
1389
+ extent_prod = float(np.prod(extent))
1390
+ if extent_prod > 0.0 and target > 0.0:
1391
+ if extent_prod / (target ** 3) > _MAX_CENTERLINE_VOXELS:
1392
+ target = (extent_prod / _MAX_CENTERLINE_VOXELS) ** (1.0 / 3.0)
1393
+ new_size = np.maximum(1, np.rint(extent / target)).astype(np.int64)
1394
+ # Rounding can land a hair over the budget the closed form aimed at.
1395
+ while (int(np.prod(new_size, dtype=np.int64)) > _MAX_CENTERLINE_VOXELS
1396
+ and target < 1.0e6):
1397
+ target *= 1.01
1398
+ new_size = np.maximum(1, np.rint(extent / target)).astype(np.int64)
1399
+ if np.all(np.abs(new_size.astype(np.float64) - size_f)
1400
+ <= _GRID_SIZE_TOLERANCE * np.maximum(size_f, 1.0)):
1401
+ return size_in, spacing_in, float(target), False
1402
+ return (tuple(int(v) for v in new_size),
1403
+ (float(target), float(target), float(target)),
1404
+ float(target), True)
1405
+
1406
+
1407
+ def resample_for_centerline(segmentation, verbose=False):
1408
+ """Resample ``segmentation`` onto the centerline grid.
1409
+
1410
+ The inscribed-radius distance map (positive inside) is returned when
1411
+ the grid is unchanged, so the caller can skip a second distance
1412
+ transform. A resampled image returns ``None`` for that map.
1413
+
1414
+ Returns
1415
+ -------
1416
+ segmentation : sitk.Image
1417
+ distance_map : sitk.Image or None
1418
+ was_resampled : bool
1419
+ """
1420
+ distance = distance_map_from_seg(segmentation) * -1
1421
+ radius = float(sitk.GetArrayViewFromImage(distance).max())
1422
+ old_size = tuple(int(v) for v in segmentation.GetSize())
1423
+ old_spacing = tuple(float(v) for v in segmentation.GetSpacing())
1424
+ new_size, new_spacing, target, resample = centerline_grid_size(
1425
+ old_size, old_spacing, radius)
1426
+ n_voxels = int(np.prod(new_size, dtype=np.int64))
1427
+ if verbose:
1428
+ target_text = "n/a" if target is None else f"{target:.4f} mm"
1429
+ print(
1430
+ f" Centerline grid: size {old_size} spacing {old_spacing} "
1431
+ f"radius {radius:.3f} mm target spacing {target_text} "
1432
+ f"-> size {new_size} ({n_voxels} voxels)"
1433
+ )
1434
+ if not resample:
1435
+ return segmentation, distance, False
1436
+
1437
+ resampled = sitk.Resample(
1438
+ segmentation,
1439
+ list(new_size),
1440
+ sitk.Transform(),
1441
+ sitk.sitkNearestNeighbor,
1442
+ segmentation.GetOrigin(),
1443
+ list(new_spacing),
1444
+ segmentation.GetDirection(),
1445
+ 0,
1446
+ segmentation.GetPixelID())
1447
+ return resampled, None, True
1448
+
1449
+
1342
1450
  def calc_centerline_fmm(segmentation, seed=None, targets=None,
1343
- min_res=300, out_dir=None, write_files=False,
1451
+ out_dir=None, write_files=False,
1344
1452
  move_target_if_fail=False,
1345
1453
  relax_factor=1, return_target=False,
1346
1454
  return_target_all=False,
@@ -1381,27 +1489,11 @@ def calc_centerline_fmm(segmentation, seed=None, targets=None,
1381
1489
  """
1382
1490
  output = None
1383
1491
  original_segmentation = segmentation
1384
- was_resampled = False
1385
1492
  if verbose:
1386
1493
  print(f"Resolution of segmentation: {segmentation.GetSize()}")
1387
1494
 
1388
- # Resample if segmentation resolution is too low
1389
- if segmentation.GetSize()[2] < min_res:
1390
- print(f" Resampling segmentation from size {segmentation.GetSize()} to size {min_res}")
1391
- # Divide spacing so that the size is at least 50
1392
- divide = segmentation.GetSize()[2] / min_res
1393
- segmentation = sitk.Resample(segmentation,
1394
- [int(x/divide) for x in segmentation
1395
- .GetSize()],
1396
- sitk.Transform(),
1397
- sitk.sitkNearestNeighbor,
1398
- segmentation.GetOrigin(),
1399
- [x*divide for x in segmentation
1400
- .GetSpacing()],
1401
- segmentation.GetDirection(), 0,
1402
- segmentation.GetPixelID())
1403
- was_resampled = True
1404
- print(f" New size: {segmentation.GetSize()}")
1495
+ segmentation, distance_map_surf, was_resampled = resample_for_centerline(
1496
+ segmentation, verbose=verbose)
1405
1497
 
1406
1498
  # Keep user-provided index coordinates anatomically consistent with
1407
1499
  # the resampled grid by mapping old-index -> physical -> new-index.
@@ -1424,10 +1516,9 @@ def calc_centerline_fmm(segmentation, seed=None, targets=None,
1424
1516
  segmentation.TransformPhysicalPointToIndex(target_phys))
1425
1517
  targets_resampled.append(target_resampled)
1426
1518
  targets = targets_resampled
1427
- # Create distance map
1428
- distance_map_surf = distance_map_from_seg(segmentation)
1429
- # Switch sign of distance map
1430
- distance_map_surf = distance_map_surf * -1
1519
+ # The radius check already built this map when the grid was kept.
1520
+ if distance_map_surf is None:
1521
+ distance_map_surf = distance_map_from_seg(segmentation) * -1
1431
1522
  distance_map_surf_np = sitk.GetArrayFromImage(distance_map_surf).transpose(2, 1, 0)
1432
1523
  # Get maximum value of distance map
1433
1524
  max_surf = distance_map_surf_np.max()
@@ -3392,7 +3483,7 @@ def snap_end_points_to_lumen(end_points, radius_np, spacing, direction=None):
3392
3483
 
3393
3484
 
3394
3485
  def calc_multi_component_centerlines(segmentation, nr_seeds=None,
3395
- min_res=300, out_dir=None, write_files=False,
3486
+ out_dir=None, write_files=False,
3396
3487
  move_target_if_fail=False, relax_factor=1,
3397
3488
  verbose=False, return_failed=False,
3398
3489
  post_process_kwargs=None):
@@ -3403,7 +3494,9 @@ def calc_multi_component_centerlines(segmentation, nr_seeds=None,
3403
3494
  This function:
3404
3495
  1. Analyzes the segmentation to find disconnected bodies
3405
3496
  2. Creates optimal seed points for each disconnected body
3406
- 3. Calculates centerlines using fast marching method for each seed
3497
+ 3. Calculates centerlines using fast marching method for each seed.
3498
+ Each component's grid is chosen from its inscribed radius and a
3499
+ voxel budget; see :func:`centerline_grid_size`.
3407
3500
  4. Combines all centerlines into a unified polydata structure
3408
3501
 
3409
3502
  Parameters
@@ -3414,9 +3507,6 @@ def calc_multi_component_centerlines(segmentation, nr_seeds=None,
3414
3507
  Number of seeds to generate. If None, creates one seed per disconnected body.
3415
3508
  If the requested number exceeds the number of disconnected bodies, it will be
3416
3509
  automatically reduced to match the number of available bodies with a warning.
3417
- min_res : int, optional
3418
- Minimum resolution for centerline calculation. If segmentation resolution
3419
- is lower, it will be resampled. Defaults to 300.
3420
3510
  out_dir : str, optional
3421
3511
  Output directory for intermediate files. Defaults to None.
3422
3512
  write_files : bool, optional
@@ -3592,7 +3682,6 @@ def calc_multi_component_centerlines(segmentation, nr_seeds=None,
3592
3682
  segmentation_body,
3593
3683
  seed=seed,
3594
3684
  targets=None, # Let calc_centerline_fmm find targets automatically
3595
- min_res=min_res,
3596
3685
  out_dir=out_dir,
3597
3686
  write_files=write_files,
3598
3687
  move_target_if_fail=move_target_if_fail,
@@ -3944,7 +4033,6 @@ if __name__ == '__main__':
3944
4033
  centerline_multi, success_info = calc_multi_component_centerlines(
3945
4034
  segmentation,
3946
4035
  nr_seeds=None,
3947
- min_res=700,
3948
4036
  out_dir=out_dir,
3949
4037
  write_files=write_files,
3950
4038
  move_target_if_fail=False,
@@ -255,7 +255,6 @@ def initialize_from_seg(segmentation,
255
255
  centerline, success_info = calc_multi_component_centerlines(
256
256
  segmentation,
257
257
  nr_seeds=2,
258
- min_res=500,
259
258
  out_dir=dir_output,
260
259
  write_files=write_samples,
261
260
  move_target_if_fail=False,
@@ -745,7 +745,6 @@ def trace_centerline(
745
745
  success) = calc_centerline_fmm(predicted_vessel,
746
746
  seed,
747
747
  targets,
748
- min_res=40,
749
748
  post_process_kwargs={
750
749
  'merge_method': global_config.get(
751
750
  'CENTERLINE_MERGE_METHOD',
@@ -6,6 +6,7 @@ import SimpleITK as sitk
6
6
  from seqseg.modules.centerline import (
7
7
  backtracking_gradient,
8
8
  calc_centerline_fmm,
9
+ centerline_grid_size,
9
10
  cluster_map,
10
11
  masked_world_frame_gradient,
11
12
  snap_end_points_to_lumen,
@@ -160,8 +161,46 @@ def test_automatic_targets_reach_a_seed_away_from_the_widest_voxel():
160
161
  seed = np.array([10.0, 6.0, 6.0])
161
162
 
162
163
  _centerline, success, successes = calc_centerline_fmm(
163
- image, seed=seed, targets=None, min_res=10,
164
+ image, seed=seed, targets=None,
164
165
  return_success_list=True)
165
166
 
166
167
  assert success
167
168
  assert all(successes)
169
+
170
+
171
+ def test_centerline_grid_keeps_a_fine_isotropic_crop():
172
+ """A 0.5 mm crop with a 1.18 mm radius is already within 10% of the target."""
173
+ size, spacing, _target, resample = centerline_grid_size(
174
+ (320, 289, 184), (0.5, 0.5, 0.5), 1.18)
175
+ assert resample is False
176
+ assert size == (320, 289, 184)
177
+ assert spacing == (0.5, 0.5, 0.5)
178
+
179
+
180
+ def test_centerline_grid_refines_a_thick_slice_axis():
181
+ size, spacing, _target, resample = centerline_grid_size(
182
+ (256, 256, 40), (0.7, 0.7, 3.0), 2.0)
183
+ assert resample is True
184
+ assert size == (269, 269, 180)
185
+ assert int(np.prod(size)) <= 20_000_000
186
+ assert spacing[0] == spacing[1] == spacing[2]
187
+ assert spacing[2] < 1.0
188
+
189
+
190
+ def test_centerline_grid_coarsens_to_the_voxel_budget():
191
+ size, spacing, _target, resample = centerline_grid_size(
192
+ (1000, 1000, 1000), (0.5, 0.5, 0.5), 3.0)
193
+ assert resample is True
194
+ assert int(np.prod(size)) <= 20_000_000
195
+ assert spacing[0] > 1.0
196
+ assert size[0] < 1000
197
+
198
+
199
+ def test_centerline_grid_keeps_the_input_when_radius_is_non_positive():
200
+ for radius in (0.0, -1.0, float("nan")):
201
+ size, spacing, target, resample = centerline_grid_size(
202
+ (40, 30, 20), (0.8, 0.8, 2.0), radius)
203
+ assert resample is False
204
+ assert target is None
205
+ assert size == (40, 30, 20)
206
+ assert spacing == (0.8, 0.8, 2.0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: seqseg
3
- Version: 2.2.0
3
+ Version: 2.2.1
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes