Processing Phases¶
The per-phase processing functions that make up the pipeline.
spineps.phase_pre¶
spineps.phase_pre
¶
Pre-processing phase: crop, N4 bias correction, and intensity normalization of the input MRI before segmentation.
compute_crop
¶
compute_crop(
nii: NII,
out_file: str | Path,
dataset_id=100,
ddevice: Literal["cpu", "cuda", "mps"] = "cuda",
gpu=0,
max_folds=None,
logger=None,
) -> tuple[slice, slice, slice]
Run the Vibe whole-body segmentation and compute a crop region around the spine.
Segments the input with run_vibeseg, keeps only the spine-relevant labels (IVD, vertebra body,
vertebra posterior elements, and sacrum), and returns a bounding-box crop expanded by VIBE_CROP_MARGIN_MM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
nii
|
NII
|
Input MRI image to segment and crop. |
required |
out_file
|
str | Path
|
Path where the Vibe segmentation output is written. |
required |
dataset_id
|
int
|
Vibe model/dataset identifier passed to |
100
|
ddevice
|
Literal['cpu', 'cuda', 'mps']
|
Compute device for inference. Defaults to "cuda". |
'cuda'
|
gpu
|
int
|
GPU index used when running on CUDA. Defaults to 0. |
0
|
max_folds
|
int | None
|
Maximum number of model folds to ensemble. Defaults to None (all folds). |
None
|
logger
|
optional
|
Logger forwarded to |
None
|
Returns:
| Type | Description |
|---|---|
tuple[slice, slice, slice]
|
tuple[slice, slice, slice]: The crop slices around the segmented spine, with a |
Source code in spineps/phase_pre.py
preprocess_input
¶
preprocess_input(
mri_nii: NII,
debug_data: dict,
pad_size: int = 4,
proc_normalize_input: bool = True,
proc_do_n4_bias_correction: bool = True,
proc_crop_input: bool = True,
verbose: bool = False,
) -> tuple[NII | None, ErrCode]
Pre-process an input MRI for segmentation: normalize, crop, N4-correct, and pad.
Optionally rescales intensities to [NORMALIZE_MIN_VALUE, NORMALIZE_MAX_VALUE], crops away empty
background to speed up computation, applies N4 bias field correction on the crop, re-normalizes,
writes the processed crop back into the full image, and finally pads the volume by pad_size on every side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mri_nii
|
NII
|
Input grayscale MRI image. |
required |
debug_data
|
dict
|
Dictionary for collecting intermediate results (unused here, reserved for parity). |
required |
pad_size
|
int
|
Number of voxels of edge padding added on each side per axis. Defaults to 4. |
4
|
proc_normalize_input
|
bool
|
Whether to rescale intensities into the normalization range. Defaults to True. |
True
|
proc_do_n4_bias_correction
|
bool
|
Whether to apply N4 bias field correction. Defaults to True. |
True
|
proc_crop_input
|
bool
|
Whether to crop away background before processing. Defaults to True. |
True
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
NII | None
|
tuple[NII | None, ErrCode]: The padded, pre-processed image and |
ErrCode
|
if the input image is empty. |
Source code in spineps/phase_pre.py
spineps.phase_semantic¶
spineps.phase_semantic
¶
Semantic phase: predict and post-process the subregion (semantic) segmentation mask of the spine.
predict_semantic_mask
¶
predict_semantic_mask(
mri_nii: NII,
model: Segmentation_Model,
debug_data: dict,
proc_fill_3d_holes: bool = True,
proc_clean_beyond_largest_bounding_box: bool = True,
proc_remove_inferior_beyond_canal: bool = False,
proc_clean_small_cc_artifacts: bool = True,
verbose: bool = False,
) -> tuple[NII | None, NII | None, ErrCode]
Predict the semantic (subregion) segmentation mask and run post-processing on it.
Runs the model on the input MRI (resampling to the model's recommended zoom), then optionally removes structures beyond the spinal-canal height, cleans small connected-component artifacts, restricts the mask to the largest bounding box of connected components, and fills 3D holes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mri_nii
|
NII
|
Input grayscale MRI image (intensities must start at 0). |
required |
model
|
Segmentation_Model
|
Model used to produce the semantic segmentation. |
required |
debug_data
|
dict
|
Dictionary for collecting intermediate results (e.g. the raw segmentation). |
required |
proc_fill_3d_holes
|
bool
|
Whether to fill 3D holes in the output mask. Defaults to True. |
True
|
proc_clean_beyond_largest_bounding_box
|
bool
|
Whether to keep only connected components within the largest bounding box. Defaults to True. |
True
|
proc_remove_inferior_beyond_canal
|
bool
|
Whether to remove non-sacrum structures below the spinal-canal height. Defaults to False. |
False
|
proc_clean_small_cc_artifacts
|
bool
|
Whether to delete small connected-component artifacts. Defaults to True. |
True
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
NII | None
|
tuple[NII | None, NII | None, ErrCode]: The post-processed semantic mask, the softmax logits, and an |
NII | None
|
error code ( |
Source code in spineps/phase_semantic.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
remove_nonsacrum_beyond_canal_height
¶
Remove non-sacrum labels that lie above or below the spinal-canal extent.
Computes the inferior-axis (I) extent of the spinal canal/cord, expanded by CANAL_HEIGHT_MARGIN_MM,
and zeroes out everything outside that range. The sacrum (SACRUM_LABEL) is kept regardless of position.
If no canal/cord is present, the mask is returned unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Semantic segmentation mask in ("P", "I", "R") orientation. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
NII |
NII
|
The segmentation mask with structures beyond the canal height removed (sacrum preserved). |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If |
Source code in spineps/phase_semantic.py
semantic_bounding_box_clean
¶
Keep only connected components that fall within the spine's growing bounding box.
Binarizes the mask and labels its connected components. Starting from the largest component's bounding box
(expanded by CC_BBOX_MARGIN_MM), it iteratively merges in any other component whose bounding box overlaps
the current region in all three axes (with extra inferior margin to tolerate gaps in the spine). Voxels
outside the resulting region, and any non-incorporated components, are removed. Components are dropped if the
binary mask has more than MAX_EXPECTED_SEMANTIC_CC parts (logged as strange).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Semantic segmentation mask to clean. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
NII |
NII
|
The cleaned segmentation mask, restored to its original orientation. |
Source code in spineps/phase_semantic.py
overlap_slice
¶
Check whether two ranges defined by slices overlap (borders inclusive).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
slice1
|
slice
|
First range, using its |
required |
slice2
|
slice
|
Second range, using its |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the two ranges overlap or touch at a border, else False. |
Source code in spineps/phase_semantic.py
spineps.phase_instance¶
spineps.phase_instance
¶
Instance phase: turn the subregion semantic mask into per-vertebra instance labels via cutout prediction and merging.
predict_instance_mask
¶
predict_instance_mask(
seg_nii: NII,
model: Segmentation_Model,
debug_data: dict,
pad_size: int = 0,
proc_inst_fill_3d_holes: bool = True,
proc_detect_and_solve_merged_corpi: bool = True,
proc_corpus_clean: bool = True,
proc_inst_clean_small_cc_artifacts: bool = True,
proc_inst_largest_k_cc: int = 0,
verbose: bool = False,
) -> tuple[NII | None, ErrCode]
Build a per-vertebra instance mask from a subregion semantic segmentation.
Reorients and rescales the input to the model's recommended zoom, crops to the segmentation, derives one cutout per corpus center of mass, runs the instance model on each cutout, merges the overlapping per-vertebra predictions into a single label map, optionally cleans and fills it, then uncrops back to the original space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Subregion (semantic) segmentation mask used as input. |
required |
model
|
Segmentation_Model
|
Instance model producing the per-vertebra-body cutout predictions. |
required |
debug_data
|
dict
|
Dictionary for collecting intermediate results across the pipeline. |
required |
pad_size
|
int
|
Edge padding added before processing and removed afterwards. Defaults to 0. |
0
|
proc_inst_fill_3d_holes
|
bool
|
Whether to fill 3D holes in the final vertebra mask. Defaults to True. |
True
|
proc_detect_and_solve_merged_corpi
|
bool
|
Whether to detect and split merged vertebral bodies. Defaults to True. |
True
|
proc_corpus_clean
|
bool
|
Whether to clean small corpus connected-component artifacts. Defaults to True. |
True
|
proc_inst_clean_small_cc_artifacts
|
bool
|
Whether to delete small instance artifacts. Defaults to True. |
True
|
proc_inst_largest_k_cc
|
int
|
Keep only the largest k connected components per cutout label; 0 disables. Defaults to 0. |
0
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
NII | None
|
tuple[NII | None, ErrCode]: The vertebra instance mask in the input space and an error code. Returns |
ErrCode
|
|
tuple[NII | None, ErrCode]
|
are produced, or |
Source code in spineps/phase_instance.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | |
get_corpus_coms
¶
get_corpus_coms(
seg_nii: NII,
corpus_size_cleaning: int,
process_detect_and_solve_merged_corpi: bool = True,
verbose: bool = False,
) -> list | None
Compute the center of mass of every vertebral corpus, optionally splitting merged bodies.
Extracts the corpus region (using the dense corpus label when available, otherwise the eroded and cleaned
corpus-border label) and returns one center of mass per corpus, sorted from bottom to top (plus the dens
when present). When process_detect_and_solve_merged_corpi is set, it cross-checks the vertebra/IVD
height alternation: neighbors at the same height are merged, and a corpus whose volume exceeds the neighbor
average by MERGED_CORPUS_VOLUME_RATIO is split via a separating plane.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Subregion semantic mask in ("P", "I", "R") orientation. |
required |
corpus_size_cleaning
|
int
|
Voxel threshold for removing small corpus-border artifacts; 0 disables cleaning. |
required |
process_detect_and_solve_merged_corpi
|
bool
|
Whether to detect and split merged corpora. Defaults to True. |
True
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
list | None
|
list | None: Corpus center-of-mass coordinates ordered from bottom to top, or None if no corpus is found. |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If |
Source code in spineps/phase_instance.py
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | |
get_separating_components
¶
get_separating_components(
segvert: ndarray,
max_iter: int = 10,
connectivity: int = 3,
) -> tuple[
np.ndarray,
np.ndarray,
np.ndarray,
np.ndarray,
np.ndarray,
]
Split a binary volume into two spatially separate components (S and T) via erosion and dilation.
Designed for a segmentation that is a single connected component but should be separated into two meaningful subregions. Morphological erosion is applied until the volume breaks into multiple connected components (the splitting point), then the two regions are recovered through dilation. Useful for anatomical structures that are initially connected (e.g., two merged vertebral bodies).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segvert
|
ndarray
|
3D binary (or labeled) array representing the volume to split. |
required |
max_iter
|
int
|
Maximum number of erosion iterations allowed to find separable components. Defaults to 10. |
10
|
connectivity
|
int
|
Connectivity for morphological operations (1=6-, 2=18-, 3=26-connectivity). Defaults to 3. |
3
|
Returns:
| Type | Description |
|---|---|
ndarray
|
tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: A 5-tuple ``(spart, tpart, spart_dil, |
ndarray
|
tpart_dil, stpart) |
ndarray
|
are their dilations grown until contact, and |
ndarray
|
2=tpart_dil only, 3=overlap of the two dilations). |
Raises:
| Type | Description |
|---|---|
Exception
|
If the volume cannot be split into two parts within the iterations, or if a resulting part is empty. |
IndentationError
|
If the maximum number of erosion iterations is reached without successful separation. |
Source code in spineps/phase_instance.py
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 | |
get_plane_split
¶
get_plane_split(
segvert: ndarray,
compare_nii: NII,
spart: ndarray,
tpart: ndarray,
spart_dil: ndarray,
tpart_dil: ndarray,
) -> NII
Compute an approximate separating plane between two regions and return it as an NII.
Determines the collision region between the dilated versions of the two components, takes its center of mass
as a point on the plane, and builds a plane orthogonal to the vector between the centers of mass of spart
and tpart. The plane is then filled along the superior axis and returned as an NII matching the input
orientation. If the dilated masks do not touch, an empty volume is returned and a warning is logged.
Note
TODO: Improve accuracy by projecting the collision point onto the line connecting both COMs rather than relying on the COM of the overlap region.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segvert
|
ndarray
|
Original 3D binary or labeled segmentation volume. |
required |
compare_nii
|
NII
|
Reference image providing orientation and spatial metadata for the output. |
required |
spart
|
ndarray
|
Binary mask of the first component (S). |
required |
tpart
|
ndarray
|
Binary mask of the second component (T). |
required |
spart_dil
|
ndarray
|
Dilated mask of |
required |
tpart_dil
|
ndarray
|
Dilated mask of |
required |
Returns:
| Name | Type | Description |
|---|---|---|
NII |
NII
|
A filled binary plane separating |
NII
|
if no collision between the dilated masks is detected. |
Source code in spineps/phase_instance.py
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 | |
split_by_plane
¶
Split a vertebra volume into two parts using a filled separating plane.
Masks the filled plane (whose values are 1 on one side of the plane and 2 on the other) to the foreground
of segvert, yielding a label map that partitions the vertebra into the two sides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
segvert
|
ndarray
|
Binary vertebra volume to split. |
required |
plane_filled_nii
|
NII
|
Filled separating plane (1 above, 2 below) as produced by |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The vertebra voxels labeled 1 (above the plane) and 2 (below the plane); background stays 0. |
Source code in spineps/phase_instance.py
collect_vertebra_predictions
¶
collect_vertebra_predictions(
seg_nii: NII,
model: Segmentation_Model,
corpus_size_cleaning: int,
cutout_size: tuple[int, int, int],
debug_data: dict,
proc_inst_largest_k_cc: int = 0,
process_detect_and_solve_merged_corpi: bool = True,
proc_inst_fill_holes: bool = False,
verbose: bool = False,
) -> tuple[np.ndarray | None, list[str], int]
Run the instance model on a cutout around each corpus center of mass and collect per-label predictions.
Computes corpus centers of mass, and for each one extracts a cutout_size window (nudged inferiorly until
it lands on segmentation), relabels it to the model's expected labels, runs the model, post-processes the
cutout, and stores each predicted label (1/2/3, the three-vertebra hierarchy) as a binary map placed back
into the full-volume frame.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Subregion semantic mask used for the cutouts. |
required |
model
|
Segmentation_Model
|
Instance model producing the three-vertebra-body cutout predictions. |
required |
corpus_size_cleaning
|
int
|
Voxel threshold for cleaning small corpus artifacts when finding coms; 0 disables. |
required |
cutout_size
|
tuple[int, int, int]
|
Cutout window size (per axis) extracted around each center of mass. |
required |
debug_data
|
dict
|
Dictionary for collecting per-cutout intermediate results. |
required |
proc_inst_largest_k_cc
|
int
|
Keep only the largest k connected components per cutout label; 0 disables. Defaults to 0. |
0
|
process_detect_and_solve_merged_corpi
|
bool
|
Whether to detect and split merged corpora. Defaults to True. |
True
|
proc_inst_fill_holes
|
bool
|
Whether to fill holes in each cutout prediction. Defaults to False. |
False
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
ndarray | None
|
tuple[np.ndarray | None, list[str], int]: A hierarchical prediction array of shape |
list[str]
|
|
int
|
produced, and the number of corpus centers of mass. Returns |
Source code in spineps/phase_instance.py
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 | |
post_process_single_3vert_prediction
¶
post_process_single_3vert_prediction(
vert_nii: NII,
labels: list[int] | None = None,
largest_cc: int = 0,
fill_holes: bool = False,
) -> NII
Post-process a single three-vertebra cutout prediction by filtering components and filling holes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vert_nii
|
NII
|
The cutout prediction mask to clean. |
required |
labels
|
list[int] | None
|
Labels to restrict the connected-component filter to. Defaults to None (all labels). |
None
|
largest_cc
|
int
|
Keep only the largest |
0
|
fill_holes
|
bool
|
Whether to fill holes in the prediction. Defaults to False. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
NII |
NII
|
The post-processed cutout prediction mask. |
Source code in spineps/phase_instance.py
str_id_com_label
¶
Build the string identifier for a single (corpus-com, label) prediction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
com_idx
|
int
|
Index of the corpus center of mass. |
required |
label
|
int
|
Label index within that center's three-vertebra prediction. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The identifier |
Source code in spineps/phase_instance.py
from_vert3_predictions_make_vert_mask
¶
from_vert3_predictions_make_vert_mask(
seg_nii: NII,
vert_predictions: ndarray,
hierarchical_existing_predictions: list[str],
vert_size_threshold: int,
debug_data: dict,
proc_inst_clean_small_cc_artifacts: bool = True,
verbose: bool = False,
) -> tuple[NII, dict, ErrCode]
Merge the hierarchical three-vertebra predictions into a single vertebra instance mask.
Each per-label prediction looks among neighboring predictions (center index -2 to +2, all three labels) for its most-agreeing partners (by Dice), forming prediction couples. The couples are then merged into one instance label map, optionally cleaning small connected-component artifacts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Reference segmentation providing shape and spatial metadata. |
required |
vert_predictions
|
ndarray
|
Hierarchical predictions of shape |
required |
hierarchical_existing_predictions
|
list[str]
|
Identifiers of the predictions that were actually produced. |
required |
vert_size_threshold
|
int
|
Voxel threshold for removing small instance artifacts. |
required |
debug_data
|
dict
|
Dictionary for collecting intermediate results. |
required |
proc_inst_clean_small_cc_artifacts
|
bool
|
Whether to delete small instance artifacts. Defaults to True. |
True
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
tuple[NII, dict, ErrCode]
|
tuple[NII, dict, ErrCode]: The merged vertebra instance mask, the (updated) debug data, and an error code. |
Source code in spineps/phase_instance.py
create_prediction_couples
¶
create_prediction_couples(
hierarchical_predictions: ndarray,
hierarchical_existing_predictions,
verbose: bool = False,
) -> dict
Form and rank prediction couples across all hierarchical predictions.
For every (center index, label) prediction, finds its best-agreeing partners and groups them into a couple,
averaging the agreement scores of duplicate couples. The result is sorted so that larger, higher-agreement
couples come first (key = (len(couple) + 1) * mean_agreement).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hierarchical_predictions
|
ndarray
|
Hierarchical predictions of shape |
required |
hierarchical_existing_predictions
|
list[str]
|
Identifiers of the predictions that were actually produced. |
required |
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict
|
Mapping from each couple (a tuple of |
dict
|
ordered by descending size-weighted agreement. |
Source code in spineps/phase_instance.py
parallel_dice
¶
Compute the Dice score between two masks, tagged with a candidate location.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
anchor
|
ndarray
|
Anchor prediction mask. |
required |
pred
|
ndarray
|
Candidate prediction mask to compare against. |
required |
cand_loc
|
tuple
|
Candidate location identifier carried through unchanged. |
required |
Returns:
| Type | Description |
|---|---|
tuple[float, tuple]
|
tuple[float, Any]: The Dice score between |
Source code in spineps/phase_instance.py
find_prediction_couple
¶
find_prediction_couple(
idx,
pred,
hierarchical_predictions: ndarray,
hierarchical_existing_predictions,
n_predictions,
verbose: bool = False,
) -> tuple[tuple | None, float]
Find the best-agreeing partner predictions for one anchor prediction.
Considers candidate predictions within +/-2 of the anchor's center index (all three labels, excluding the anchor itself), ranks them by Dice with the anchor, and keeps up to the two best whose Dice exceeds 0.3. The anchor itself is appended, and the members are returned sorted by center index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
idx
|
int
|
Center-of-mass index of the anchor prediction. |
required |
pred
|
int
|
Label index of the anchor prediction. |
required |
hierarchical_predictions
|
ndarray
|
Hierarchical predictions of shape |
required |
hierarchical_existing_predictions
|
list[str]
|
Identifiers of the predictions that were actually produced. |
required |
n_predictions
|
int
|
Total number of corpus centers of mass. |
required |
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
tuple | None
|
tuple[tuple | None, float]: The couple (a sorted tuple of |
float
|
anchor) and its mean partner agreement. Returns |
Source code in spineps/phase_instance.py
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 | |
merge_coupled_predictions
¶
merge_coupled_predictions(
seg_nii: NII,
coupled_predictions,
hierarchical_predictions: ndarray,
debug_data: dict,
proc_clean_small_cc_artifacts: bool = True,
vert_size_threshold: int = 0,
verbose: bool = False,
) -> tuple[NII, dict, ErrCode]
Assemble the final vertebra instance mask from ranked prediction couples.
Iterates over the couples in priority order, summing their member maps and thresholding by voxel agreement (requiring overlap from at least two members unless the couple is small or low-agreement). Each accepted couple is written as a new instance label into voxels not yet claimed; couples overlapping established vertebrae by more than 60% are skipped. Small connected-component artifacts are optionally cleaned afterwards.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Reference segmentation providing shape and spatial metadata. |
required |
coupled_predictions
|
dict
|
Mapping from couple to mean agreement, ordered by priority. |
required |
hierarchical_predictions
|
ndarray
|
Hierarchical predictions of shape |
required |
debug_data
|
dict
|
Dictionary for collecting intermediate results. |
required |
proc_clean_small_cc_artifacts
|
bool
|
Whether to delete small instance artifacts. Defaults to True. |
True
|
vert_size_threshold
|
int
|
Voxel threshold for removing small instance artifacts. Defaults to 0. |
0
|
verbose
|
bool
|
Emit additional progress logging. Defaults to False. |
False
|
Returns:
| Type | Description |
|---|---|
NII
|
tuple[NII, dict, ErrCode]: The vertebra instance mask, the (updated) debug data, and an error code |
dict
|
( |
Source code in spineps/phase_instance.py
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 | |
spineps.phase_labeling¶
spineps.phase_labeling
¶
Vertebra-labeling phase: turns top-to-bottom vertebra instances into anatomical vertebra labels via a classifier and path search.
perform_labeling_step
¶
perform_labeling_step(
model: VertLabelingClassifier,
img_nii: NII,
vert_nii: NII,
subreg_nii: NII | None = None,
proc_lab_force_no_tl_anomaly: bool = False,
disable_c1: bool = True,
) -> NII
Assign anatomical vertebra labels to a vertebra instance mask using the labeling classifier.
Runs the labeling classifier on each vertebra instance, derives a globally consistent label sequence, and relabels the instance mask accordingly. If a subregion mask is given, the classifier only sees the vertebra corpus (not the whole vertebra). Optionally adds a missing C1 label and zeroes out any instances that could not be matched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
VertLabelingClassifier
|
Classifier used to predict per-instance vertebra labels. |
required |
img_nii
|
NII
|
Input MRI image. |
required |
vert_nii
|
NII
|
Vertebra instance segmentation mask to be relabeled. |
required |
subreg_nii
|
NII | None
|
Subregion semantic mask; if given, vertebrae are masked to their corpus before classification. |
None
|
proc_lab_force_no_tl_anomaly
|
bool
|
If True, disallow thoracolumbar (T13) transitional-vertebra anomalies. |
False
|
disable_c1
|
bool
|
If True, do not predict/add a C1 label. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
NII |
NII
|
The vertebra instance mask relabeled with anatomical vertebra labels (unmatched instances set to 0). |
Source code in spineps/phase_labeling.py
run_model_for_vert_labeling
¶
run_model_for_vert_labeling(
model: VertLabelingClassifier,
img_nii: NII,
vert_nii: NII,
verbose: bool = False,
proc_lab_force_no_tl_anomaly: bool = False,
disable_c1: bool = True,
) -> tuple[
dict[int, int],
float,
list[int],
list[int],
list,
list,
dict,
]
Run the labeling classifier over a whole image/instance pair and resolve a vertebra label sequence.
Reorients, crops around the vertebrae, rescales to the model's recommended zoom, runs the classifier on every vertebra instance, and uses the cheapest-cost path search to turn per-instance predictions into a consistent anatomical sequence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
VertLabelingClassifier
|
Classifier used to predict per-instance vertebra labels. |
required |
img_nii
|
NII
|
Input MRI image. |
required |
vert_nii
|
NII
|
Vertebra instance segmentation mask. |
required |
verbose
|
bool
|
If True, print intermediate weighting/path information. |
False
|
proc_lab_force_no_tl_anomaly
|
bool
|
If True, disallow thoracolumbar (T13) transitional-vertebra anomalies. |
False
|
disable_c1
|
bool
|
If True, do not predict a C1 label. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
tuple[dict[int, int], float, list[int], list[int], list, list, dict]
|
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If the number of original instances does not match the resolved path length. |
Source code in spineps/phase_labeling.py
run_model_for_vert_labeling_cutouts
¶
run_model_for_vert_labeling_cutouts(
model: VertLabelingClassifier,
img_arrays: dict[int, ndarray],
disable_c1: bool = True,
boost_c2: float = 3.0,
allow_cervical_skip: bool = True,
verbose: bool = True,
) -> tuple[
dict[int, int],
float,
list[int],
list[int],
list,
list,
dict,
]
Run the labeling classifier on precomputed per-instance image cutouts and resolve a vertebra label sequence.
Like :func:run_model_for_vert_labeling, but skips reorienting/cropping/rescaling and instead consumes already-prepared
image arrays keyed by instance label.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
VertLabelingClassifier
|
Classifier used to predict per-instance vertebra labels. |
required |
img_arrays
|
dict[int, ndarray]
|
Mapping of vertebra instance label to its cropped image array. |
required |
disable_c1
|
bool
|
If True, do not predict a C1 label. |
True
|
boost_c2
|
float
|
Multiplicative boost applied to a prediction whose argmax is C2. |
3.0
|
allow_cervical_skip
|
bool
|
If True, allow the path search to skip a class within the cervical region. |
True
|
verbose
|
bool
|
If True, print intermediate weighting/path information. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
tuple[dict[int, int], float, list[int], list[int], list, list, dict]
|
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If the number of input arrays does not match the resolved path length. |
Source code in spineps/phase_labeling.py
region_to_vert
¶
Broadcast a 3-region (cervical, thoracic, lumbar) softmax into a per-vertebra-class vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
region_softmax_values
|
ndarray
|
Length-3 region softmax values ordered cervical, thoracic, lumbar. |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: Length- |
Source code in spineps/phase_labeling.py
prepare_vert
¶
prepare_vert(
vert_softmax_values: ndarray,
gaussian_sigma: float = 0.85,
gaussian_radius: int = 2,
gaussian_regionwise: bool = True,
) -> np.ndarray
Smooth and normalize a per-vertebra-class softmax vector.
Optionally applies a 1-D Gaussian filter (either per spinal region or across all classes) and then normalizes to sum to 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vert_softmax_values
|
ndarray
|
Length- |
required |
gaussian_sigma
|
float
|
Gaussian smoothing sigma; 0 disables smoothing. |
0.85
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
2
|
gaussian_regionwise
|
bool
|
If True, smooth each spinal region independently instead of across the whole vector. |
True
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The smoothed, sum-normalized per-class vector. |
Source code in spineps/phase_labeling.py
prepare_vertgrp
¶
prepare_vertgrp(
vertgrp_softmax_values: ndarray,
gaussian_sigma: float = 0.85,
gaussian_radius: int = 2,
gaussian_regionwise: bool = True,
) -> np.ndarray
Expand a vertebra-group softmax to per-vertebra classes, then smooth and normalize it.
Distributes each vertebra-group probability onto its member vertebra classes (via vert_group_idx_to_exact_idx_dict),
optionally applies a 1-D Gaussian filter (per region or globally), and normalizes to sum to 1.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertgrp_softmax_values
|
ndarray
|
Per-vertebra-group softmax values. |
required |
gaussian_sigma
|
float
|
Gaussian smoothing sigma; 0 disables smoothing. |
0.85
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
2
|
gaussian_regionwise
|
bool
|
If True, smooth each spinal region independently instead of across the whole vector. |
True
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The expanded, smoothed, sum-normalized per-class vector. |
Source code in spineps/phase_labeling.py
prepare_visible
¶
prepare_visible(
predictions: dict,
visible_w: float = 1.0,
gaussian_sigma: float = 0.8,
gaussian_radius: int = 2,
) -> np.ndarray
Build a per-instance confidence-weighting chain from the classifier's "fully visible" head.
For each instance, reads the probability of being fully visible (if the FULLYVISIBLE head is present, else assumes 1),
optionally Gaussian-smooths it along the instance axis, and converts it into a multiplicative weight in [0, 1] that
down-weights partially visible (cropped) vertebrae according to visible_w.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
dict
|
Per-instance classifier outputs, each holding a |
required |
visible_w
|
float
|
Strength of the visibility down-weighting; 0 disables it. |
1.0
|
gaussian_sigma
|
float
|
Gaussian smoothing sigma along the instance axis; 0 disables smoothing. |
0.8
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
2
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: Per-instance multiplicative weights clipped to |
Source code in spineps/phase_labeling.py
prepare_region
¶
prepare_region(
region_softmax_values: ndarray,
gaussian_sigma: float = 0.75,
gaussian_radius: int = 1,
) -> np.ndarray
Broadcast a region softmax to per-vertebra classes, then smooth and normalize it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
region_softmax_values
|
ndarray
|
Length-3 region softmax values (cervical, thoracic, lumbar). |
required |
gaussian_sigma
|
float
|
Gaussian smoothing sigma; 0 disables smoothing. |
0.75
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
1
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The broadcast, smoothed, sum-normalized per-class vector. |
Source code in spineps/phase_labeling.py
prepare_vertrel_columns
¶
prepare_vertrel_columns(
vertrel_matrix: ndarray,
gaussian_sigma: float = 0.75,
gaussian_radius: int = 1,
) -> np.ndarray
Smooth and column-normalize the relative-position (VertRel) cost matrix.
For each VertRel label (column, skipping the first), optionally Gaussian-smooths the values along the instance axis and
normalizes the column so its values stay bounded (divides by the column sum when it exceeds 1, otherwise by 1 + sum).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertrel_matrix
|
ndarray
|
Matrix of shape |
required |
gaussian_sigma
|
float
|
Gaussian smoothing sigma along the instance axis; 0 disables smoothing. |
0.75
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
1
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The smoothed, column-normalized relative-position matrix (modified in place and returned). |
Source code in spineps/phase_labeling.py
prepare_vertt13_columns
¶
Column-normalize the T13-anomaly (VertT13) cost matrix.
Normalizes each VertT13 label (column, skipping the first) so it sums to 1 along the instance axis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertt13_matrix
|
ndarray
|
Matrix of shape |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The column-normalized matrix (modified in place and returned). |
Source code in spineps/phase_labeling.py
prepare_vertrel
¶
prepare_vertrel(
vertrel_softmax_values: ndarray,
gaussian_sigma: float = 0.75,
gaussian_radius: int = 1,
) -> np.ndarray
Optionally Gaussian-smooth a relative-position (VertRel) softmax vector.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vertrel_softmax_values
|
ndarray
|
Relative-position softmax values for a single instance. |
required |
gaussian_sigma
|
float
|
Gaussian smoothing sigma; 0 disables smoothing. |
0.75
|
gaussian_radius
|
int
|
Half-width of the Gaussian kernel. |
1
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: The (optionally smoothed) relative-position vector; not re-normalized. |
Source code in spineps/phase_labeling.py
find_vert_path_from_predictions
¶
find_vert_path_from_predictions(
predictions,
visible_w: float = 0.5,
vert_w: float = 0.9,
vertgrp_w: float = 0.8,
region_w: float = 1.1,
vertrel_w: float = 0.6,
vertt13_w: float = 0.4,
disable_c1: bool = True,
boost_c2: float = 1.0,
allow_cervical_skip: bool = False,
allow_thoracic_skip: bool = False,
allow_lumbar_skip: bool = False,
punish_multiple_sequence: float = 0.0,
punish_skip_sequence: float = 0.0,
punish_skip_at_region_sequence: float = 0.0,
region_gaussian_sigma: float = 0.0,
vert_gaussian_sigma: float = 0.8,
vert_gaussian_regionwise: bool = True,
vertgrp_gaussian_sigma: float = 0.8,
vertgrp_gaussian_regionwise: bool = True,
vertrel_column_norm: bool = True,
vertrel_gaussian_sigma: float = 0.6,
focus_tl_gap: bool = True,
argmax_combined_cost_matrix_instead_of_path_algorithm: bool = False,
proc_lab_force_no_tl_anomaly: bool = False,
verbose: bool = False,
) -> tuple[float, list[int], list[int], list, list, dict]
Combine the classifier's prediction heads into a cost matrix and solve for the most probable vertebra label sequence.
Builds a per-instance / per-class cost matrix by weighting and summing the available prediction heads (VERT, VERTGRP,
REGION), down-weighting by the "fully visible" chain, optionally boosting C2, and adding separate relative-position
(VertRel) and T13-anomaly (VertT13) cost terms. The cheapest monotonically increasing label path is then found with
:func:find_most_probably_sequence (unless argmax_combined_cost_matrix_instead_of_path_algorithm is set, which falls
back to a plain per-instance argmax). Special transitional vertebrae (T11 skip, T12/L5 repeats) and per-region skips are
permitted via the corresponding flags. Finally the path is post-processed (see :func:fpath_post_processing).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
predictions
|
dict
|
Per-instance classifier outputs, each holding a |
required |
visible_w
|
float
|
Weight of the "fully visible" down-weighting (must be in |
0.5
|
vert_w
|
float
|
Weight of the per-vertebra (VERT) head. |
0.9
|
vertgrp_w
|
float
|
Weight of the vertebra-group (VERTGRP) head. |
0.8
|
region_w
|
float
|
Weight of the spinal-region (REGION) head. |
1.1
|
vertrel_w
|
float
|
Weight of the relative-position (VERTREL) cost term. |
0.6
|
vertt13_w
|
float
|
Weight of the T13-anomaly (VERTT13) cost term. |
0.4
|
disable_c1
|
bool
|
If True, the path may not start at C1 (starts at C2 instead). |
True
|
boost_c2
|
float
|
Multiplicative boost applied to a prediction whose argmax is C2; 0 disables it. |
1.0
|
allow_cervical_skip
|
bool
|
If True, allow skipping a class within the cervical region. |
False
|
allow_thoracic_skip
|
bool
|
If True, allow skipping a class within the thoracic region. |
False
|
allow_lumbar_skip
|
bool
|
If True, allow skipping a class within the lumbar region. |
False
|
punish_multiple_sequence
|
float
|
Extra cost for repeating an allowed-multiple class. |
0.0
|
punish_skip_sequence
|
float
|
Extra cost for skipping an allowed-skip class. |
0.0
|
punish_skip_at_region_sequence
|
float
|
Extra cost for skipping at a region boundary. |
0.0
|
region_gaussian_sigma
|
float
|
Gaussian sigma for the region head; 0 disables smoothing. |
0.0
|
vert_gaussian_sigma
|
float
|
Gaussian sigma for the vertebra head; 0 disables smoothing. |
0.8
|
vert_gaussian_regionwise
|
bool
|
If True, smooth the vertebra head per region. |
True
|
vertgrp_gaussian_sigma
|
float
|
Gaussian sigma for the vertebra-group head; 0 disables smoothing. |
0.8
|
vertgrp_gaussian_regionwise
|
bool
|
If True, smooth the vertebra-group head per region. |
True
|
vertrel_column_norm
|
bool
|
If True, column-normalize the relative-position matrix. |
True
|
vertrel_gaussian_sigma
|
float
|
Gaussian sigma used when column-normalizing the relative-position matrix. |
0.6
|
focus_tl_gap
|
bool
|
If True, focus on the T11/T13 thoracolumbar gap (reserved for the refinement pass). |
True
|
argmax_combined_cost_matrix_instead_of_path_algorithm
|
bool
|
If True, take a plain per-instance argmax instead of the path search. |
False
|
proc_lab_force_no_tl_anomaly
|
bool
|
If True, disallow T13 transitional-vertebra anomalies (no T11 skip / no T12 repeat). |
False
|
verbose
|
bool
|
If True, print the active head weights. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
tuple[float, list[int], list[int], list, list, dict]
|
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If a weight is negative, |
Source code in spineps/phase_labeling.py
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 | |
fpath_post_processing
¶
Post-process a raw 0-based class path into the final 1-based vertebra label sequence.
Resolves transitional-vertebra anomalies (two consecutive T12 become T12 + T13; a trailing double L5 becomes L5 + L6) and shifts every class index by 1 to the final label convention, leaving the special T13 label untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fpath
|
list[int]
|
Raw 0-based class path from the cost/path search. |
required |
Returns:
| Type | Description |
|---|---|
list[int]
|
list[int]: The post-processed 1-based vertebra label sequence (with T13/L6 anomalies applied). |
Source code in spineps/phase_labeling.py
is_valid_vertebra_sequence
¶
Check whether a vertebra label sequence is anatomically contiguous top-to-bottom.
A sequence is valid if each label follows the previous one by exactly 1, or forms one of the allowed transitional jumps at
the thoracolumbar junction (T13->L1, i.e. 28->20, and T12->L1, i.e. 18->20). VertExact inputs are first converted via
:func:fpath_post_processing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sequence
|
list[VertExact] | list[int]
|
The vertebra label sequence, either as |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if the sequence is a valid contiguous vertebra run, otherwise False. |
Source code in spineps/phase_labeling.py
spineps.phase_post¶
spineps.phase_post
¶
Post-processing phase: clean and reconcile the semantic and vertebra-instance masks and attach IVD/endplate instance labels.
phase_postprocess_combined
¶
phase_postprocess_combined(
img_nii: NII,
seg_nii: NII,
vert_nii: NII,
model_labeling: VertLabelingClassifier | None,
debug_data: dict | None,
labeling_offset: int = 0,
proc_lab_force_no_tl_anomaly: bool = False,
proc_assign_missing_cc: bool = True,
proc_assign_missing_cc_fast=False,
proc_clean_inst_by_sem: bool = True,
n_vert_bodies: int | None = None,
process_merge_vertebra: bool = True,
proc_vertebra_inconsistency: bool = True,
proc_assign_posterior_instance_label: bool = True,
verbose: bool = False,
disable_c1=True,
sacrum_ids=(v_name2idx["S1"],),
) -> tuple[NII, NII]
Run the combined semantic/instance post-processing pipeline and return cleaned, anatomically labeled masks.
Crops both masks to the segmentation, optionally fixes superior/inferior articular inconsistencies, reconciles the instance and semantic masks (reassigning or deleting unmatched connected components), splits accidentally merged vertebrae, fixes mislabeled posterior elements, labels instances top-to-bottom, optionally runs the anatomical labeling classifier, forces the sacrum and dens labels, attaches IVD and endplate instance labels (splitting endplates into superior/inferior), and finally un-crops back to the original field of view.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
img_nii
|
NII
|
Input MRI image. |
required |
seg_nii
|
NII
|
Subregion semantic segmentation mask. |
required |
vert_nii
|
NII
|
Vertebra instance segmentation mask. |
required |
model_labeling
|
VertLabelingClassifier | None
|
Anatomical labeling classifier; if None, instances keep their top-to-bottom labels. |
required |
debug_data
|
dict | None
|
Optional dict that intermediate results are written into; created if None. |
required |
labeling_offset
|
int
|
Offset added to the top-to-bottom instance labels. |
0
|
proc_lab_force_no_tl_anomaly
|
bool
|
If True, disallow T13 transitional-vertebra anomalies during labeling. |
False
|
proc_assign_missing_cc
|
bool
|
If True, reassign semantic connected components not covered by the instance mask. |
True
|
proc_assign_missing_cc_fast
|
bool
|
If True, use the faster infect-based missing-CC assignment. |
False
|
proc_clean_inst_by_sem
|
bool
|
If True, mask the instance mask by the semantic mask before processing. |
True
|
n_vert_bodies
|
int | None
|
Number of vertebra bodies; inferred from the instance mask if None. |
None
|
process_merge_vertebra
|
bool
|
If True, detect and merge accidentally split adjacent vertebrae. |
True
|
proc_vertebra_inconsistency
|
bool
|
If True, reassign inconsistent articular substructures by instance overlap. |
True
|
proc_assign_posterior_instance_label
|
bool
|
If True, fix wrongly labeled posterior instance elements. |
True
|
verbose
|
bool
|
If True, print verbose progress. |
False
|
disable_c1
|
bool
|
If True (and |
True
|
sacrum_ids
|
tuple
|
Semantic label id(s) treated as sacrum and mapped to the S1 instance label. |
(v_name2idx['S1'],)
|
Returns:
| Type | Description |
|---|---|
tuple[NII, NII]
|
tuple[NII, NII]: The cleaned |
Source code in spineps/phase_post.py
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | |
mask_cleaning_other
¶
mask_cleaning_other(
whole_vert_nii: NII,
seg_nii: NII,
n_vert_bodies: int,
proc_assign_missing_cc: bool = False,
proc_assign_missing_cc_fast=False,
verbose: bool = False,
) -> tuple[NII, NII]
Reconcile the vertebra instance mask with the vertebra portion of the semantic mask.
Extracts the vertebra subregions from the semantic mask and (optionally) reassigns semantic connected components that the
instance mask missed, either via a fast infect pass or via :func:assign_missing_cc; deleted components are removed from the
semantic mask. Logs a warning when the unmatched vertebra volume between the two masks is anomalously large.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
whole_vert_nii
|
NII
|
Vertebra instance segmentation mask. |
required |
seg_nii
|
NII
|
Subregion semantic segmentation mask. |
required |
n_vert_bodies
|
int
|
Number of vertebra bodies, used to scale the unmatched-volume warning. |
required |
proc_assign_missing_cc
|
bool
|
If True, reassign missed semantic components via :func: |
False
|
proc_assign_missing_cc_fast
|
bool
|
If True, additionally run a fast infect-based assignment first. |
False
|
verbose
|
bool
|
If True, print verbose progress. |
False
|
Returns:
| Type | Description |
|---|---|
tuple[NII, NII]
|
tuple[NII, NII]: The cleaned |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If, with |
Source code in spineps/phase_post.py
assign_missing_cc
¶
assign_missing_cc(
target_arr: ndarray,
reference_arr: ndarray,
verbose: bool = False,
verbose_deletion: bool = False,
proc_assign_missing_dilate_first: bool = True,
) -> tuple[np.ndarray, np.ndarray, np.ndarray]
Assign reference-mask connected components that the target mask does not cover to a neighboring target label.
Finds connected components of reference_arr (e.g. the vertebra semantic mask) that have no overlap with target_arr
(the instance mask). Optionally dilates the target mask once first to absorb thin gaps. Each remaining component is dilated
locally and assigned to the most common neighboring target label; components with no labeled neighbor are deleted from the
reference mask and recorded in a deletion map.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_arr
|
ndarray
|
Instance label array that components are assigned to. |
required |
reference_arr
|
ndarray
|
Reference (semantic) label array whose uncovered components are processed. |
required |
verbose
|
bool
|
If True, log each assignment. |
False
|
verbose_deletion
|
bool
|
If True, log each deletion even when |
False
|
proc_assign_missing_dilate_first
|
bool
|
If True, dilate the target mask once before searching for uncovered components. |
True
|
Returns:
| Type | Description |
|---|---|
tuple[ndarray, ndarray, ndarray]
|
tuple[np.ndarray, np.ndarray, np.ndarray]: |
Raises:
| Type | Description |
|---|---|
AssertionError
|
If |
Source code in spineps/phase_post.py
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | |
add_ivd_ep_vert_label
¶
add_ivd_ep_vert_label(
whole_vert_nii: NII, seg_nii: NII, verbose=True
) -> tuple[np.ndarray, np.ndarray]
Attach intervertebral-disc and endplate instance labels and split endplates into superior/inferior.
Reorients both masks to PIR, computes each vertebra corpus center of mass along the inferior-superior axis, then assigns
every IVD and endplate connected component to the nearest lower vertebra. IVD voxels are written into the instance array
with IVD_LABEL_OFFSET added; endplate voxels with ENDPLATE_LABEL_OFFSET added. Endplates are further divided into
inferior/superior plates by iteratively dilating each vertebra into the endplate region. Logs the number of assigned
components and restores the original orientation before returning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
whole_vert_nii
|
NII
|
Vertebra instance segmentation mask. |
required |
seg_nii
|
NII
|
Subregion semantic segmentation mask (must contain disc/endplate/corpus labels). |
required |
verbose
|
bool
|
If True, print endplate-detection progress. |
True
|
Returns:
| Type | Description |
|---|---|
tuple[ndarray, ndarray]
|
tuple[np.ndarray, np.ndarray]: |
Source code in spineps/phase_post.py
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |
find_nearest_lower
¶
Return the largest element of seq strictly smaller than x, or the minimum if none exists.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seq
|
Sequence[float]
|
Values to search. |
required |
x
|
float
|
Reference value. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
float |
float
|
The greatest element below |
Source code in spineps/phase_post.py
label_instance_top_to_bottom
¶
Relabel vertebra instances consecutively from top to bottom by center-of-mass height.
Reorients to PIR, sorts the instances by their center of mass along the inferior-superior axis, and assigns consecutive
labels (1 + labeling_offset upward) from top to bottom, then restores the original orientation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vert_nii
|
NII
|
Vertebra instance segmentation mask (modified in place). |
required |
labeling_offset
|
int
|
Offset added to the consecutive labels. |
0
|
Returns:
| Type | Description |
|---|---|
tuple[NII, ndarray]
|
tuple[NII, np.ndarray]: The relabeled instance mask and its array of unique labels. |
Source code in spineps/phase_post.py
assign_vertebra_inconsistency
¶
assign_vertebra_inconsistency(
vert_nii: NII,
seg_nii: NII,
locations=(
Location.Superior_Articular_Left,
Location.Superior_Articular_Right,
Location.Inferior_Articular_Left,
Location.Inferior_Articular_Right,
),
) -> None
Reassign articular-process components to the vertebra instance they most overlap with.
For each given articular subregion location, finds its connected components in the semantic mask and, for each component,
reassigns its instance label to the vertebra whose overlap volume dominates the second-largest by ARTICULAR_DOMINANCE_RATIO.
Updates vert_nii in place.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vert_nii
|
NII
|
Vertebra instance segmentation mask (modified in place). |
required |
seg_nii
|
NII
|
Subregion semantic segmentation mask. |
required |
locations
|
tuple[Location, ...]
|
Articular subregion locations to reconcile. |
(Superior_Articular_Left, Superior_Articular_Right, Inferior_Articular_Left, Inferior_Articular_Right)
|
Returns:
| Name | Type | Description |
|---|---|---|
None |
None
|
|
Source code in spineps/phase_post.py
detect_and_solve_merged_vertebra
¶
Detect and merge a vertebra (typically C2) that was split into two stacked instances.
Builds a height-sorted list of IVD components and vertebra instances. If the two topmost entries are both vertebrae, the
upper one is significantly smaller (below MERGED_VERTEBRA_SIZE_RATIO of the other), and the two masks touch over more
than MERGED_VERTEBRA_MIN_CONTACT_MM2 of area, the smaller instance is merged into the larger one in vert_nii.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
seg_nii
|
NII
|
Subregion semantic segmentation mask. |
required |
vert_nii
|
NII
|
Vertebra instance segmentation mask (modified in place when a merge occurs). |
required |
Returns:
| Type | Description |
|---|---|
tuple[NII, NII]
|
tuple[NII, NII]: The |