Skip to content

feat(np_utils): add np_split_connected_component with erosion and mincut backends - #144

Merged
Hendrik-code merged 3 commits into
mainfrom
feat/np-split-connected-component
Aug 28, 2026
Merged

feat(np_utils): add np_split_connected_component with erosion and mincut backends#144
Hendrik-code merged 3 commits into
mainfrom
feat/np-split-connected-component

Conversation

@Hendrik-code

Copy link
Copy Markdown
Owner

Upstreams B10 from the cross-repo survey.

What

np_split_connected_component(arr, method="erosion" | "mincut", …) — splits a mask that is one connected component but should be two (two merged vertebral bodies, say) into two spatially separate parts. Returns 0 = background, 1 / 2 = the two parts.

np_connected_component_contact_map(part_a, part_b) — dilates two disjoint parts until they touch and returns the 0/1/2/3 contact map. This is the reusable second half of the erosion algorithm, useful on its own.

Two backends, one API

"erosion" "mincut"
Source spineps get_separating_components nako-segmentation split_cc
Method erode until it breaks apart, re-grow both halves erode to two seeds, then min-cut/max-flow for the minimal separating surface
Deps none networkx
Speed fast considerably slower
Cut quality follows the erosion front minimal-area, spacing-weighted

The min-cut backend takes zoom and weights graph edges by voxel spacing, so anisotropic data is handled correctly.

Reconciling the two

The originals disagreed on what they returned, which would have made the shared API misleading: the erosion version recovered only the eroded cores (discarding the shell the erosion ate), while the min-cut version partitioned the whole volume. On a test dumbbell the erosion path lost 2560 of 4096 voxels.

A full_partition=True flag (default) now grows the erosion cores back over the removed voxels by nearest-seed assignment, so both backends partition the input. Pass full_partition=False for the raw seeds — which is what spineps actually wants, since it fits a separating plane to them.

Two latent bugs fixed

Both are in the min-cut source and both are now covered by tests that would have failed before:

  • add_2d_edges=True raised NameError — the capacity expression referenced an undefined geom.norm. Replaced with np.linalg.norm.
  • max_ignore=None raised NameErrormax_errors was only assigned inside the if max_ignore is not None branch, then read unconditionally at the final voxel-loss check. Now initialised to 0.

Dependency

networkx is imported lazily inside the min-cut backend with a clear message ("the 'mincut' method needs networkx; install it with pip install networkx"), and declared only as a dev dependency. The core package gains no runtime requirement, and the min-cut tests skip if it is absent.

Verification

  • 16 new tests in unit_tests/test_nputils.py: input really is one component; both backends split into two non-empty parts; full_partition keeps every voxel and never paints outside the input; the two parts land on opposite cubes; unknown method rejected; an unsplittable solid block raises; and for min-cut — already-separate input rejected, min_volume and max_cut guards, anisotropic zoom, plus the two previously-crashing branches. Contact map: label set and non-empty contact zone, and that it does not mutate its inputs (the upstream dilation helpers work in place).
  • Full suite: 480 passed, 4 skipped. ruff check clean.

🤖 Generated with Claude Code

…cut backends

Splits a mask that is one connected component but should be two - two merged
vertebral bodies, for instance - into two spatially separate parts.

Unifies two independent implementations behind one API:
- "erosion" (from spineps get_separating_components): erode until the component
  breaks apart, then re-grow both halves. Fast and dependency-free.
- "mincut" (from nako-segmentation split_cc): erode only until two seeds appear,
  then find the minimal separating surface by min-cut/max-flow over the voxel
  adjacency graph, with edge capacities from the voxel spacing. Needs networkx,
  slower, but the cut is minimal-area rather than incidental.

Also adds np_connected_component_contact_map, which dilates two disjoint parts
until they touch and returns the 0/1/2/3 contact map - the reusable second half
of the erosion algorithm.

The two backends originally disagreed on what they returned: the erosion one
recovered only the eroded cores while the mincut one partitioned the whole
volume. A full_partition flag (default True) now grows the erosion cores back
over the removed voxels so both agree; pass False for the raw seeds, which is
what you want when fitting a separating plane to them.

Two latent bugs in the mincut source are fixed, both now covered by tests:
- add_2d_edges raised NameError on an undefined `geom.norm`
- max_ignore=None raised NameError, since `max_errors` was only assigned inside
  the branch that the same argument disables

networkx is imported lazily with a clear error message and declared only as a
dev dependency, so the core package does not gain a runtime requirement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Benchmark comparison

Speed (wall time per call)

baseline 7e54bda vs head 7e54bda · python 3.11.16 · 5 repeats + 1 warmup

Showing the 5 most-changed measurements per case; the rest are collapsed. Rows with a baseline below 3 ms are tagged (noise) — runner jitter on those swamps any real change.

ct_3d — shape (73, 47, 73)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_load_seg 3.28 ±0.09 2.98 ±0.07 -9.2% 0.000
poi_calc_centroids_nocrop 4.94 ±0.05 4.56 ±0.04 -7.7% 0.000
nii_save 12.40 ±0.08 11.78 ±0.13 -5.1% 0.000
nii_erode_msk 6.41 ±0.07 6.13 ±0.02 -4.3% 0.000
nii_filter_connected_components 4.48 ±0.07 4.35 ±0.04 -2.9% 0.034
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
poi_calc_poi_from_subreg_vert 12.17 ±0.26 11.83 ±0.17 -2.7% 0.147
nii_load_img 12.26 ±0.07 11.98 ±0.11 -2.3% 0.001
nii_fill_holes 4.88 ±0.05 4.79 ±0.03 -1.9% 0.009
nii_resample_from_to 42.39 ±0.24 41.90 ±0.13 -1.2% 0.002
nii_dilate_msk 121.32 ±0.27 120.25 ±0.35 -0.9% 0.000
nii_rescale_seg 3.30 ±0.09 3.29 ±0.02 -0.3% 0.363
nii_rescale 40.18 ±0.17 40.21 ±0.18 +0.1% 0.399
metric_voxels 250463.00 ±0.00 250463.00 ±0.00 +0.0%
metric_labels 3.00 ±0.00 3.00 ±0.00 +0.0%
metric_foreground_pct 14.51 ±0.00 14.51 ±0.00 +0.0%
poi_rescale 0.31 ±0.01 0.23 ±0.02 -24.2% (noise) 0.000
nii_apply_crop 0.87 ±0.02 0.71 ±0.04 -18.0% (noise) 0.000
poi_reorient 0.52 ±0.04 0.43 ±0.02 -16.5% (noise) 0.002
nii_set_dtype 0.58 ±0.03 0.49 ±0.03 -15.6% (noise) 0.000
nii_extract_label 0.55 ±0.02 0.48 ±0.02 -12.8% (noise) 0.000
nii_compute_crop 0.40 ±0.02 0.35 ±0.02 -12.4% (noise) 0.038
nii_pad_to 0.86 ±0.05 0.76 ±0.02 -11.4% (noise) 0.008
nii_map_labels 1.09 ±0.02 0.98 ±0.04 -10.6% (noise) 0.002
poi_to_global 0.25 ±0.01 0.23 ±0.02 -9.5% (noise) 0.081
nii_get_array 0.05 ±0.00 0.06 ±0.00 +8.2% (noise) 0.093
poi_load 1.07 ±0.02 0.98 ±0.08 -7.7% (noise) 0.187
poi_calc_centroids 2.79 ±0.03 2.58 ±0.06 -7.6% (noise) 0.000
nii_reorient 0.91 ±0.09 0.87 ±0.08 -4.6% (noise) 0.538
poi_resample_from_to 0.77 ±0.02 0.74 ±0.08 -4.1% (noise) 0.553
poi_local_to_global_arr 0.26 ±0.01 0.27 ±0.01 +2.6% (noise) 0.430
nii_center_of_masses 1.89 ±0.06 1.87 ±0.02 -1.3% (noise) 0.238
nii_get_connected_components 2.14 ±0.05 2.17 ±0.02 +1.3% (noise) 0.896
poi_save 0.52 ±0.05 0.51 ±0.01 -1.2% (noise) 0.472
nii_unique 0.67 ±0.01 0.66 ±0.01 -1.1% (noise) 0.449
nii_volumes 1.98 ±0.02 1.96 ±0.02 -1.1% (noise) 0.716
poi_map_labels 0.21 ±0.01 0.21 ±0.01 +0.6% (noise) 0.835

ct_2d — shape (73, 47, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_resample_from_to 8.00 ±0.35 7.52 ±0.19 -6.0% 0.010
nii_rescale 6.07 ±0.11 5.82 ±0.13 -4.1% 0.003
nii_dilate_msk 14.21 ±0.31 13.94 ±0.08 -1.9% 0.073
metric_voxels 3431.00 ±0.00 3431.00 ±0.00 +0.0%
metric_labels 3.00 ±0.00 3.00 ±0.00 +0.0%
… 30 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
metric_foreground_pct 37.60 ±0.00 37.60 ±0.00 +0.0%
nii_map_labels 0.61 ±0.02 0.55 ±0.01 -8.8% (noise) 0.003
poi_save 0.60 ±0.02 0.55 ±0.02 -8.5% (noise) 0.018
poi_calc_centroids 1.08 ±0.05 0.99 ±0.03 -8.4% (noise) 0.008
nii_get_array 0.02 ±0.00 0.02 ±0.00 -7.1% (noise) 0.782
poi_load 1.12 ±0.02 1.04 ±0.01 -6.9% (noise) 0.000
nii_filter_connected_components 0.98 ±0.02 0.92 ±0.01 -6.2% (noise) 0.000
poi_rescale 0.32 ±0.01 0.30 ±0.01 -6.0% (noise) 0.063
poi_to_global 0.31 ±0.01 0.29 ±0.00 -6.0% (noise) 0.001
poi_map_labels 0.25 ±0.01 0.24 ±0.01 -5.7% (noise) 0.050
poi_reorient 0.54 ±0.01 0.51 ±0.01 -5.5% (noise) 0.002
poi_calc_centroids_nocrop 1.26 ±0.04 1.20 ±0.02 -5.3% (noise) 0.007
nii_rescale_seg 0.93 ±0.01 0.88 ±0.04 -5.2% (noise) 0.286
nii_save 1.58 ±0.05 1.51 ±0.02 -4.2% (noise) 0.014
nii_load_seg 1.85 ±0.06 1.77 ±0.03 -3.9% (noise) 0.018
nii_center_of_masses 0.26 ±0.01 0.25 ±0.00 -3.7% (noise) 0.108
nii_fill_holes 0.95 ±0.02 0.92 ±0.01 -3.4% (noise) 0.003
nii_load_img 1.67 ±0.09 1.62 ±0.03 -2.9% (noise) 1.000
nii_apply_crop 0.81 ±0.03 0.78 ±0.02 -2.9% (noise) 0.389
nii_unique 0.16 ±0.01 0.16 ±0.01 -2.5% (noise) 0.321
nii_volumes 0.33 ±0.01 0.34 ±0.01 +2.5% (noise) 0.123
nii_get_connected_components 0.75 ±0.02 0.73 ±0.03 -2.3% (noise) 0.055
nii_set_dtype 0.49 ±0.04 0.48 ±0.01 -2.2% (noise) 0.291
nii_pad_to 0.68 ±0.04 0.66 ±0.02 -2.1% (noise) 0.147
poi_local_to_global_arr 0.32 ±0.01 0.31 ±0.01 -1.6% (noise) 0.292
nii_reorient 0.92 ±0.04 0.91 ±0.04 -1.1% (noise) 0.939
nii_extract_label 0.50 ±0.03 0.50 ±0.01 -0.7% (noise) 0.859
nii_compute_crop 0.19 ±0.01 0.19 ±0.01 +0.2% (noise) 0.699
poi_resample_from_to 0.79 ±0.01 0.79 ±0.03 +0.2% (noise) 0.935
nii_erode_msk 1.06 ±0.03 1.06 ±0.02 -0.1% (noise) 0.907

mri_3d — shape (68, 52, 67)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_load_seg 3.18 ±0.03 3.06 ±0.03 -3.8% 0.001
poi_calc_poi_from_subreg_vert 11.44 ±0.25 11.18 ±0.16 -2.2% 0.070
nii_load_img 9.93 ±0.05 9.74 ±0.05 -2.0% 0.000
nii_resample_from_to 40.38 ±0.29 39.77 ±0.21 -1.5% 0.054
nii_dilate_msk 156.76 ±0.17 154.56 ±0.28 -1.4% 0.000
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_save 9.90 ±0.03 9.77 ±0.06 -1.3% 0.002
nii_rescale 38.62 ±0.14 38.16 ±0.20 -1.2% 0.003
nii_rescale_seg 3.22 ±0.06 3.19 ±0.04 -1.1% 0.909
nii_filter_connected_components 4.71 ±0.06 4.68 ±0.05 -0.6% 0.744
nii_fill_holes 5.73 ±0.08 5.70 ±0.04 -0.5% 0.370
poi_calc_centroids_nocrop 9.28 ±0.05 9.32 ±0.09 +0.3% 0.703
nii_erode_msk 7.82 ±0.02 7.82 ±1.08 +0.0% 0.262
metric_voxels 236912.00 ±0.00 236912.00 ±0.00 +0.0%
metric_labels 8.00 ±0.00 8.00 ±0.00 +0.0%
metric_foreground_pct 18.21 ±0.00 18.21 ±0.00 +0.0%
nii_get_array 0.05 ±0.00 0.06 ±0.00 +19.8% (noise) 0.000
nii_set_dtype 0.64 ±0.01 0.59 ±0.02 -7.3% (noise) 0.003
poi_map_labels 0.30 ±0.02 0.28 ±0.01 -7.0% (noise) 0.045
nii_map_labels 1.13 ±0.03 1.07 ±0.01 -5.4% (noise) 0.003
poi_local_to_global_arr 0.30 ±0.01 0.31 ±0.01 +4.4% (noise) 0.042
nii_extract_label 0.55 ±0.02 0.52 ±0.01 -4.2% (noise) 0.018
poi_resample_from_to 0.81 ±0.03 0.85 ±0.03 +4.1% (noise) 0.214
nii_pad_to 0.88 ±0.02 0.85 ±0.01 -4.0% (noise) 0.017
nii_reorient 1.03 ±0.03 0.99 ±0.03 -4.0% (noise) 0.331
poi_reorient 0.58 ±0.02 0.56 ±0.02 -3.6% (noise) 0.026
poi_to_global 0.34 ±0.01 0.33 ±0.02 -3.5% (noise) 0.170
poi_save 0.66 ±0.01 0.63 ±0.02 -3.4% (noise) 0.024
nii_get_connected_components 2.25 ±0.03 2.18 ±0.02 -3.3% (noise) 0.002
nii_unique 0.68 ±0.01 0.66 ±0.01 -3.1% (noise) 0.058
nii_apply_crop 0.85 ±0.02 0.82 ±0.03 -3.0% (noise) 0.322
nii_compute_crop 0.39 ±0.01 0.38 ±0.01 -2.7% (noise) 0.088
nii_center_of_masses 2.15 ±0.02 2.13 ±0.02 -1.1% (noise) 0.044
poi_load 2.14 ±0.01 2.16 ±0.03 +1.1% (noise) 0.236
poi_rescale 0.35 ±0.02 0.35 ±0.03 -0.8% (noise) 0.767
nii_volumes 2.11 ±0.02 2.10 ±0.02 -0.6% (noise) 0.129
poi_calc_centroids 2.95 ±0.06 2.97 ±0.06 +0.6% (noise) 0.988

mri_2d — shape (68, 52, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_rescale 5.90 ±0.10 5.65 ±0.14 -4.3% 0.009
nii_dilate_msk 18.71 ±0.18 18.39 ±0.08 -1.7% 0.012
nii_resample_from_to 7.58 ±0.18 7.55 ±0.17 -0.5% 0.622
metric_voxels 3536.00 ±0.00 3536.00 ±0.00 +0.0%
metric_labels 7.00 ±0.00 7.00 ±0.00 +0.0%
… 30 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
metric_foreground_pct 50.17 ±0.00 50.17 ±0.00 +0.0%
nii_reorient 0.98 ±0.02 0.89 ±0.02 -8.8% (noise) 0.000
nii_map_labels 0.62 ±0.05 0.57 ±0.02 -8.6% (noise) 0.032
nii_load_seg 1.86 ±0.02 1.72 ±0.04 -7.7% (noise) 0.001
nii_set_dtype 0.49 ±0.02 0.45 ±0.01 -7.5% (noise) 0.086
nii_apply_crop 0.82 ±0.02 0.76 ±0.02 -7.2% (noise) 0.000
nii_unique 0.16 ±0.01 0.15 ±0.00 -7.0% (noise) 0.082
nii_pad_to 0.68 ±0.02 0.65 ±0.02 -5.7% (noise) 0.003
poi_reorient 0.56 ±0.02 0.53 ±0.03 -5.6% (noise) 0.086
poi_calc_centroids_nocrop 1.50 ±0.05 1.42 ±0.02 -5.5% (noise) 0.012
nii_load_img 1.65 ±0.02 1.56 ±0.04 -5.5% (noise) 0.008
nii_save 1.57 ±0.02 1.48 ±0.02 -5.3% (noise) 0.000
nii_filter_connected_components 1.00 ±0.01 0.94 ±0.02 -5.2% (noise) 0.000
nii_get_connected_components 0.75 ±0.03 0.71 ±0.02 -5.2% (noise) 0.181
poi_calc_centroids 1.27 ±0.02 1.20 ±0.01 -5.1% (noise) 0.002
poi_local_to_global_arr 0.30 ±0.01 0.31 ±0.01 +4.7% (noise) 0.016
nii_center_of_masses 0.44 ±0.01 0.42 ±0.00 -4.5% (noise) 0.017
nii_erode_msk 1.30 ±0.06 1.25 ±0.02 -4.1% (noise) 0.074
poi_load 2.01 ±0.03 1.94 ±0.02 -3.5% (noise) 0.006
poi_to_global 0.31 ±0.01 0.32 ±0.01 +3.4% (noise) 0.083
nii_get_array 0.02 ±0.01 0.02 ±0.00 -3.3% (noise) 0.735
nii_volumes 0.48 ±0.01 0.50 ±0.03 +3.0% (noise) 0.326
nii_rescale_seg 0.88 ±0.04 0.86 ±0.01 -2.7% (noise) 0.239
poi_map_labels 0.29 ±0.01 0.29 ±0.01 -2.5% (noise) 0.219
poi_resample_from_to 0.85 ±0.01 0.83 ±0.01 -2.5% (noise) 0.058
nii_fill_holes 1.10 ±0.01 1.08 ±0.02 -1.9% (noise) 0.027
poi_rescale 0.33 ±0.02 0.32 ±0.01 -1.9% (noise) 0.697
nii_compute_crop 0.18 ±0.02 0.18 ±0.02 -1.7% (noise) 0.580
poi_save 0.62 ±0.02 0.62 ±0.03 -0.8% (noise) 0.441
nii_extract_label 0.48 ±0.04 0.48 ±0.01 -0.6% (noise) 0.714

Gate: a measurement fails when the baseline is ≥ 1 ms, the median grows by ≥ 50%, and Welch's t-test gives p < 0.05. metric_* rows are context only.

Memory (peak RSS growth per call)

baseline 7e54bda vs head 7e54bda · python 3.11.16 · 5 repeats + 1 warmup · sampler proc-status, isolation fork · measurement floor ≈ 0.90 MiB

Showing the 5 most-changed measurements per case; the rest are collapsed. Rows with a baseline below 3 MiB are tagged (noise) — runner jitter on those swamps any real change.

ct_3d — shape (73, 47, 73)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
poi_resample_from_to 5.28 ±0.00 5.19 ±0.00 -1.6%
nii_reorient 5.91 ±0.00 5.82 ±0.00 -1.5%
nii_dilate_msk 5.93 ±0.00 5.87 ±0.00 -1.1%
nii_erode_msk 5.95 ±0.00 5.88 ±0.00 -1.1%
nii_save 3.68 ±0.00 3.71 ±0.00 +1.0%
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_set_dtype 5.24 ±0.00 5.20 ±0.00 -0.9%
nii_extract_label 5.37 ±0.00 5.32 ±0.00 -0.9%
nii_pad_to 5.43 ±0.00 5.38 ±0.00 -0.9%
nii_map_labels 5.49 ±0.00 5.45 ±0.00 -0.9%
nii_load_seg 5.98 ±0.00 5.93 ±0.00 -0.8%
poi_calc_centroids_nocrop 5.93 ±0.00 5.88 ±0.00 -0.8%
poi_calc_centroids 6.05 ±0.00 6.01 ±0.00 -0.8%
nii_fill_holes 6.28 ±0.00 6.23 ±0.00 -0.7%
nii_rescale_seg 5.91 ±0.00 5.95 ±0.00 +0.7%
nii_apply_crop 5.83 ±0.00 5.82 ±0.00 -0.2%
nii_get_connected_components 6.58 ±0.00 6.57 ±0.00 -0.2%
nii_filter_connected_components 6.83 ±0.00 6.82 ±0.00 -0.2%
poi_calc_poi_from_subreg_vert 7.08 ±0.00 7.07 ±0.00 -0.2%
nii_load_img 3.53 ±0.00 3.53 ±0.00 -0.1%
nii_resample_from_to 6.26 ±0.00 6.27 ±0.00 +0.1%
nii_rescale 6.14 ±0.00 6.14 ±0.00 +0.0%
nii_volumes 3.52 ±0.00 3.52 ±0.00 +0.0%
nii_center_of_masses 3.27 ±0.00 3.27 ±0.00 +0.0%
poi_reorient 3.83 ±0.00 3.83 ±0.00 +0.0%
poi_to_global 3.08 ±0.00 3.08 ±0.00 +0.0%
metric_voxels 250463.00 ±0.00 250463.00 ±0.00 +0.0%
metric_labels 3.00 ±0.00 3.00 ±0.00 +0.0%
metric_foreground_pct 14.51 ±0.00 14.51 ±0.00 +0.0%
nii_get_array 1.65 ±0.00 1.65 ±0.00 +0.0% (noise)
nii_compute_crop 2.90 ±0.00 2.90 ±0.00 +0.0% (noise)
nii_unique 2.96 ±0.00 2.96 ±0.00 +0.0% (noise)
poi_rescale 2.61 ±0.00 2.61 ±0.00 +0.0% (noise)
poi_local_to_global_arr 2.96 ±0.00 2.96 ±0.00 +0.0% (noise)
poi_save 1.40 ±0.00 1.40 ±0.00 +0.0% (noise)
poi_map_labels 1.77 ±0.00 1.77 ±0.00 +0.0% (noise)
poi_load 1.61 ±0.00 1.61 ±0.00 +0.0% (noise)

ct_2d — shape (73, 47, 1)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
poi_resample_from_to 5.21 ±0.00 5.13 ±0.00 -1.6%
nii_reorient 5.84 ±0.00 5.76 ±0.00 -1.5% 0.000
nii_dilate_msk 5.87 ±0.00 5.80 ±0.00 -1.1%
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.1%
nii_save 3.61 ±0.00 3.65 ±0.00 +1.0%
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_set_dtype 5.18 ±0.00 5.13 ±0.00 -0.9%
nii_extract_label 5.30 ±0.00 5.26 ±0.00 -0.9%
nii_pad_to 5.36 ±0.00 5.32 ±0.00 -0.9%
nii_map_labels 5.43 ±0.00 5.38 ±0.00 -0.9%
nii_load_seg 5.91 ±0.00 5.86 ±0.00 -0.8%
poi_calc_centroids_nocrop 5.93 ±0.00 5.88 ±0.00 -0.8%
poi_calc_centroids 5.99 ±0.00 5.95 ±0.00 -0.8% 0.000
nii_fill_holes 6.22 ±0.00 6.17 ±0.00 -0.8%
nii_rescale_seg 5.85 ±0.00 5.89 ±0.00 +0.7%
nii_apply_crop 5.77 ±0.00 5.76 ±0.00 -0.2%
nii_get_connected_components 6.52 ±0.00 6.51 ±0.00 -0.2%
nii_filter_connected_components 6.77 ±0.00 6.76 ±0.00 -0.2%
nii_resample_from_to 6.20 ±0.00 6.20 ±0.00 +0.1%
nii_load_img 3.46 ±0.00 3.46 ±0.00 +0.0%
nii_rescale 6.07 ±0.00 6.07 ±0.00 +0.0%
nii_volumes 3.46 ±0.00 3.46 ±0.00 +0.0%
nii_center_of_masses 3.21 ±0.00 3.21 ±0.00 +0.0%
poi_reorient 3.77 ±0.00 3.77 ±0.00 +0.0%
poi_to_global 3.02 ±0.00 3.02 ±0.00 +0.0%
metric_voxels 3431.00 ±0.00 3431.00 ±0.00 +0.0%
metric_labels 3.00 ±0.00 3.00 ±0.00 +0.0%
metric_foreground_pct 37.60 ±0.00 37.60 ±0.00 +0.0%
nii_get_array 1.59 ±0.00 1.59 ±0.00 +0.0% (noise)
nii_compute_crop 2.84 ±0.00 2.84 ±0.00 +0.0% (noise)
nii_unique 2.90 ±0.00 2.90 ±0.00 +0.0% (noise)
poi_rescale 2.54 ±0.00 2.54 ±0.00 +0.0% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.89 ±0.00 +0.0% (noise)
poi_save 1.34 ±0.00 1.34 ±0.00 +0.0% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.0% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.0% (noise)

mri_3d — shape (68, 52, 67)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
poi_resample_from_to 5.21 ±0.00 5.13 ±0.00 -1.6%
nii_reorient 5.84 ±0.00 5.76 ±0.00 -1.5%
nii_dilate_msk 5.87 ±0.00 5.80 ±0.00 -1.1%
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.1%
nii_save 3.61 ±0.00 3.65 ±0.00 +1.0%
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_set_dtype 5.24 ±0.00 5.20 ±0.00 -0.9%
nii_extract_label 5.30 ±0.00 5.26 ±0.00 -0.9%
nii_pad_to 5.36 ±0.00 5.32 ±0.00 -0.9%
nii_map_labels 5.43 ±0.00 5.38 ±0.00 -0.9%
poi_calc_centroids_nocrop 5.86 ±0.00 5.82 ±0.00 -0.8%
nii_load_seg 5.91 ±0.00 5.86 ±0.00 -0.8%
poi_calc_centroids 5.99 ±0.00 5.95 ±0.00 -0.8%
nii_fill_holes 6.22 ±0.00 6.17 ±0.00 -0.8%
nii_rescale_seg 5.85 ±0.00 5.89 ±0.00 +0.7%
nii_apply_crop 5.77 ±0.00 5.76 ±0.00 -0.2%
nii_get_connected_components 6.52 ±0.00 6.51 ±0.00 -0.2%
nii_filter_connected_components 6.77 ±0.00 6.76 ±0.00 -0.2%
poi_calc_poi_from_subreg_vert 7.08 ±0.00 7.07 ±0.00 -0.2%
nii_resample_from_to 6.20 ±0.00 6.20 ±0.00 +0.1%
nii_load_img 3.53 ±0.00 3.53 ±0.00 +0.0%
nii_rescale 6.07 ±0.00 6.07 ±0.00 +0.0%
nii_volumes 3.46 ±0.00 3.46 ±0.00 +0.0%
nii_center_of_masses 3.21 ±0.00 3.21 ±0.00 +0.0%
poi_reorient 3.77 ±0.00 3.77 ±0.00 +0.0%
poi_to_global 3.02 ±0.00 3.02 ±0.00 +0.0%
metric_voxels 236912.00 ±0.00 236912.00 ±0.00 +0.0%
metric_labels 8.00 ±0.00 8.00 ±0.00 +0.0%
metric_foreground_pct 18.21 ±0.00 18.21 ±0.00 +0.0%
nii_get_array 1.59 ±0.00 1.59 ±0.00 +0.0% (noise)
nii_compute_crop 2.84 ±0.00 2.84 ±0.00 +0.0% (noise)
nii_unique 2.90 ±0.00 2.90 ±0.00 +0.0% (noise)
poi_rescale 2.54 ±0.00 2.54 ±0.00 +0.0% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.89 ±0.00 +0.0% (noise)
poi_save 1.40 ±0.00 1.40 ±0.00 +0.0% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.0% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.0% (noise)

mri_2d — shape (68, 52, 1)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
poi_resample_from_to 5.21 ±0.00 5.13 ±0.00 -1.6%
nii_reorient 5.84 ±0.00 5.76 ±0.00 -1.5%
nii_dilate_msk 5.87 ±0.00 5.80 ±0.00 -1.1%
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.1%
nii_save 3.61 ±0.00 3.65 ±0.00 +1.0%
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_set_dtype 5.24 ±0.00 5.20 ±0.00 -0.9%
nii_extract_label 5.30 ±0.00 5.26 ±0.00 -0.9%
nii_pad_to 5.36 ±0.00 5.32 ±0.00 -0.9%
nii_map_labels 5.43 ±0.00 5.38 ±0.00 -0.9%
nii_load_seg 5.91 ±0.00 5.86 ±0.00 -0.8%
poi_calc_centroids_nocrop 5.93 ±0.00 5.88 ±0.00 -0.8%
poi_calc_centroids 5.99 ±0.00 5.95 ±0.00 -0.8%
nii_fill_holes 6.22 ±0.00 6.17 ±0.00 -0.8%
nii_rescale_seg 5.85 ±0.00 5.89 ±0.00 +0.7%
nii_apply_crop 5.77 ±0.00 5.76 ±0.00 -0.2%
nii_get_connected_components 6.52 ±0.00 6.51 ±0.00 -0.2%
nii_filter_connected_components 6.77 ±0.00 6.76 ±0.00 -0.2%
nii_resample_from_to 6.20 ±0.00 6.20 ±0.00 +0.1%
nii_load_img 3.53 ±0.00 3.53 ±0.00 +0.0%
nii_rescale 6.07 ±0.00 6.07 ±0.00 +0.0%
nii_volumes 3.46 ±0.00 3.46 ±0.00 +0.0%
nii_center_of_masses 3.21 ±0.00 3.21 ±0.00 +0.0%
poi_reorient 3.77 ±0.00 3.77 ±0.00 +0.0%
poi_to_global 3.02 ±0.00 3.02 ±0.00 +0.0%
metric_voxels 3536.00 ±0.00 3536.00 ±0.00 +0.0%
metric_labels 7.00 ±0.00 7.00 ±0.00 +0.0%
metric_foreground_pct 50.17 ±0.00 50.17 ±0.00 +0.0%
nii_get_array 1.59 ±0.00 1.59 ±0.00 +0.0% (noise)
nii_compute_crop 2.84 ±0.00 2.84 ±0.00 +0.0% (noise)
nii_unique 2.90 ±0.00 2.90 ±0.00 +0.0% (noise)
poi_rescale 2.54 ±0.00 2.54 ±0.00 +0.0% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.89 ±0.00 +0.0% (noise)
poi_save 1.40 ±0.00 1.40 ±0.00 +0.0% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.0% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.0% (noise)

Gate: a measurement fails when the baseline is ≥ 1 MiB, the median grows by ≥ 50%, and Welch's t-test gives p < 0.05. metric_* rows are context only.

@Hendrik-code Hendrik-code self-assigned this Aug 28, 2026
@Hendrik-code Hendrik-code added the enhancement New feature or request label Aug 28, 2026
@Hendrik-code
Hendrik-code merged commit d5c35dd into main Aug 28, 2026
6 checks passed
@Hendrik-code
Hendrik-code deleted the feat/np-split-connected-component branch August 28, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants