Skip to content

feat(core): add point-search, boundary-normal and vertebra-angle helpers - #141

Merged
Hendrik-code merged 2 commits into
mainfrom
feat/upstream-point-and-normal-helpers
Aug 28, 2026
Merged

feat(core): add point-search, boundary-normal and vertebra-angle helpers#141
Hendrik-code merged 2 commits into
mainfrom
feat/upstream-point-and-normal-helpers

Conversation

@Hendrik-code

Copy link
Copy Markdown
Owner

Upstreams three helpers found in sibling repos under ongoing_projects/hendrik/, where each had been re-implemented locally several times over, and consolidates the vector helpers that were already duplicated inside TPTBox.

What's added

np_utils

  • np_index — row indices of an (N, D) point array that exactly equal a given point.
  • np_find_closest_point_index — nearest-point search. For integer (voxel) arrays it tries an exact match first, which is the common case when the query already sits on the grid, then falls back to a distance search.
  • np_compute_boundary_normals — per-voxel surface normals where one label touches another: 6-connectivity contact test, then the Sobel gradient of a Gaussian-smoothed mask. np_compute_surface has no normals, and calculate_pca_normal_np returns a single principal axis for a whole segmentation, so this filled a real gap.
  • np_unit_vector / np_angle_between — canonical implementations. spine/spinestats/angles.py and core/poi_fun/ray_casting.py each carried their own copy; both now alias these. angles.py keeps exporting unit_vector / angle_between under their historic names, so nothing downstream breaks.

core/poi_fun/vertebra_direction

  • get_vert_direction_angles — angle between each of a vertebra's Posterior/Inferior/Right direction vectors and the corresponding global axis. A healthy frame stays near-orthogonal; an implausibly large angle flags direction landmarks that are wrong.

What's refactored

Four sites hand-rolled cdist/argmin and now use the shared search:

File Was
pixel_based_point_finder.py (get_nearest_neighbor) cdist(p.T, locs) + .argmin()
pixel_based_point_finder.py (point-set projection loop) cdist_to_point + np.argmin
endplates.py ×2 np.linalg.norm(… , axis=1)

Also removed a dead commented-out duplicate in endplates.py. cdist_to_point itself is unchanged — it was already optimal.

Notes on the source material

Two things did not survive review, and are worth recording:

  • fast_cdist was not ported. Its upstream comment claims it is "much faster than scipy.cdist". It is 2–6× slower — scipy's is compiled C, while the einsum form materialises the (N, 3) difference array. Benchmarked at n=1k→1M across float and int dtypes. Only its exact-match fast path was adopted, reimplemented on top of cdist.
  • The normals point into the label, not outward. The source docstring says outward; the gradient of a smoothed mask points toward increasing density. Verified on a two-slab volume (mean normal [-1, 0, 0] with label 1 at low x) and documented accordingly.

A fourth candidate — a topological hole fill — was dropped entirely: np_fill_holes is already backed by fill_voids (compiled C++) and matches 6-connected binary_fill_holes on both a sealed and a leaky shell. The candidate was a torch conv3d loop iterated to fixpoint.

Similarly, only the angle triple from the vertebra-orientation candidate is new. Its R, corpus_com and rel_to_corpus are already get_vert_direction_matrix, a POI lookup, and get_vert_direction_PIR respectively — confirmed identical over 50 random orthonormal frames rather than assumed.

Verification

  • Full suite: 478 passed, 4 skipped. ruff check clean; the flake8 CI gate (E9,F63,F7,F82) reports 0.
  • 14 new tests across unit_tests/test_nputils.py and unit_tests/test_poi_ops.py, covering exact/multiple/no-hit lookup, brute-force agreement of the nearest-point search on int and float clouds, unit-length and direction of the normals, non-touching and diagonal-only-contact edge cases, and radian/degree consistency of the angle triple.
  • All four refactors proven equivalent to their previous behaviour over randomized inputs, including the column-vector input shape that get_nearest_neighbor's old np.expand_dims path accepted.

🤖 Generated with Claude Code

Upstreams three helpers that sibling repos had each re-implemented locally,
and removes the duplicate vector helpers that already existed inside TPTBox.

np_utils:
- np_index / np_find_closest_point_index: row lookup and nearest-point search
  over an (N, D) point array. The nearest-point search tries an exact match
  first for integer (voxel) arrays, then falls back to scipy's cdist.
- np_compute_boundary_normals: per-voxel surface normals where one label
  touches another, via a 6-connectivity contact test and the Sobel gradient
  of a Gaussian-smoothed mask. Complements np_compute_surface, which has no
  normals, and calculate_pca_normal_np, which returns a single axis for a
  whole segmentation.
- np_unit_vector / np_angle_between: canonical implementations. angles.py and
  ray_casting.py both carried their own copy; both now alias these, and
  angles.py keeps its historic public names.

poi_fun.vertebra_direction:
- get_vert_direction_angles: angle between each of a vertebra's P/I/R
  direction vectors and the corresponding global axis. Useful as a QC signal
  for direction landmarks that are not orthonormal.

Four existing call sites now use the shared point search instead of
hand-rolling cdist/argmin: pixel_based_point_finder (get_nearest_neighbor and
the point-set projection loop) and endplates (two norm(axis=1) distance
computations). All four were verified equivalent to their previous behaviour
over randomized inputs.

Note on the source material: the upstream fast_cdist was documented as "much
faster than scipy.cdist" but benchmarks 2-6x slower, since scipy's is compiled
C while the einsum form materialises the difference array. Only its
exact-match fast path was adopted, on top of cdist.

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 439b142 vs head 439b142 · 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_save 11.85 ±0.09 11.50 ±0.05 -2.9% 0.001
nii_filter_connected_components 4.32 ±0.04 4.21 ±0.05 -2.6% 0.017
nii_fill_holes 4.69 ±0.07 4.79 ±0.08 +2.1% 0.171
nii_load_img 11.98 ±0.09 11.73 ±0.18 -2.1% 0.204
poi_calc_centroids_nocrop 4.44 ±0.03 4.36 ±0.29 -1.6% 0.603
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_rescale_seg 3.14 ±0.05 3.10 ±0.03 -1.2% 0.051
nii_erode_msk 6.12 ±0.07 6.20 ±0.10 +1.2% 0.258
nii_dilate_msk 119.28 ±0.22 118.24 ±0.48 -0.9% 0.009
nii_resample_from_to 41.95 ±0.20 41.63 ±0.17 -0.8% 0.011
poi_calc_poi_from_subreg_vert 11.61 ±0.07 11.52 ±0.03 -0.7% 0.076
nii_rescale 40.02 ±0.10 39.77 ±0.17 -0.6% 0.028
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 0.06 ±0.00 0.03 ±0.00 -49.8% (noise) 0.000
nii_set_dtype 0.54 ±0.04 0.39 ±0.04 -27.5% (noise) 0.000
poi_reorient 0.42 ±0.03 0.32 ±0.01 -23.2% (noise) 0.001
nii_reorient 0.87 ±0.05 0.69 ±0.03 -20.5% (noise) 0.000
nii_pad_to 0.69 ±0.02 0.56 ±0.02 -17.8% (noise) 0.000
nii_extract_label 0.43 ±0.03 0.36 ±0.05 -17.1% (noise) 0.172
nii_apply_crop 0.64 ±0.04 0.54 ±0.02 -14.7% (noise) 0.004
poi_map_labels 0.17 ±0.02 0.15 ±0.01 -14.5% (noise) 0.010
nii_load_seg 2.98 ±0.06 2.67 ±0.01 -10.4% (noise) 0.000
poi_save 0.42 ±0.03 0.39 ±0.01 -8.1% (noise) 0.029
poi_rescale 0.20 ±0.03 0.19 ±0.01 -6.7% (noise) 0.147
poi_local_to_global_arr 0.26 ±0.00 0.25 ±0.01 -5.4% (noise) 0.002
poi_to_global 0.19 ±0.01 0.18 ±0.03 -4.8% (noise) 0.992
poi_resample_from_to 0.57 ±0.01 0.54 ±0.01 -4.3% (noise) 0.004
nii_map_labels 0.84 ±0.01 0.87 ±0.08 +4.0% (noise) 0.276
nii_unique 0.62 ±0.03 0.60 ±0.02 -3.5% (noise) 0.086
nii_get_connected_components 2.09 ±0.02 2.02 ±0.04 -3.3% (noise) 0.020
nii_center_of_masses 1.86 ±0.01 1.81 ±0.01 -2.8% (noise) 0.000
nii_compute_crop 0.32 ±0.02 0.31 ±0.01 -2.3% (noise) 0.736
poi_calc_centroids 2.47 ±0.02 2.42 ±0.04 -1.7% (noise) 0.036
poi_load 0.86 ±0.02 0.87 ±0.01 +0.8% (noise) 0.506
nii_volumes 1.91 ±0.03 1.91 ±0.01 -0.3% (noise) 0.309

ct_2d — shape (73, 47, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_rescale 5.56 ±0.16 5.34 ±0.04 -4.0% 0.094
nii_resample_from_to 7.42 ±0.17 7.13 ±0.12 -3.9% 0.029
nii_dilate_msk 13.72 ±0.06 13.79 ±0.04 +0.5% 0.104
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_compute_crop 0.17 ±0.01 0.15 ±0.01 -12.8% (noise) 0.105
poi_map_labels 0.24 ±0.01 0.22 ±0.01 -7.7% (noise) 0.006
poi_resample_from_to 0.76 ±0.03 0.71 ±0.03 -7.0% (noise) 0.018
poi_local_to_global_arr 0.31 ±0.01 0.29 ±0.00 -6.6% (noise) 0.007
nii_fill_holes 0.85 ±0.04 0.90 ±0.02 +6.3% (noise) 0.045
nii_set_dtype 0.44 ±0.01 0.46 ±0.01 +6.0% (noise) 0.003
nii_get_connected_components 0.71 ±0.04 0.75 ±0.04 +5.6% (noise) 0.077
nii_volumes 0.31 ±0.01 0.29 ±0.01 -5.5% (noise) 0.054
nii_center_of_masses 0.23 ±0.01 0.24 ±0.02 +4.7% (noise) 0.359
poi_to_global 0.27 ±0.01 0.26 ±0.01 -4.3% (noise) 0.626
nii_erode_msk 0.92 ±0.02 0.96 ±0.03 +4.2% (noise) 0.041
nii_load_seg 1.73 ±0.09 1.66 ±0.02 -3.8% (noise) 0.425
nii_unique 0.15 ±0.01 0.14 ±0.01 -3.5% (noise) 0.242
nii_pad_to 0.58 ±0.03 0.60 ±0.03 +3.0% (noise) 0.178
nii_get_array 0.02 ±0.00 0.02 ±0.00 -2.6% (noise) 0.276
nii_map_labels 0.54 ±0.01 0.52 ±0.02 -2.4% (noise) 0.090
poi_reorient 0.47 ±0.01 0.48 ±0.01 +2.4% (noise) 0.192
nii_filter_connected_components 0.87 ±0.02 0.89 ±0.02 +2.4% (noise) 0.014
nii_reorient 0.84 ±0.03 0.82 ±0.01 -1.8% (noise) 0.114
poi_save 0.55 ±0.01 0.54 ±0.02 -1.7% (noise) 0.119
nii_rescale_seg 0.82 ±0.02 0.80 ±0.01 -1.6% (noise) 0.260
nii_extract_label 0.45 ±0.03 0.46 ±0.02 +1.5% (noise) 0.725
nii_save 1.42 ±0.05 1.40 ±0.02 -1.4% (noise) 0.198
nii_apply_crop 0.72 ±0.02 0.71 ±0.02 -1.2% (noise) 0.458
poi_load 1.01 ±0.02 1.00 ±0.02 -0.8% (noise) 0.069
poi_calc_centroids_nocrop 1.13 ±0.02 1.12 ±0.01 -0.8% (noise) 0.389
nii_load_img 1.50 ±0.05 1.49 ±0.04 -0.6% (noise) 0.562
poi_calc_centroids 0.95 ±0.03 0.94 ±0.04 -0.2% (noise) 0.760
poi_rescale 0.29 ±0.01 0.29 ±0.01 -0.2% (noise) 0.183

mri_3d — shape (68, 52, 67)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_filter_connected_components 4.75 ±0.06 4.61 ±0.02 -3.0% 0.003
nii_load_img 9.72 ±0.06 9.55 ±0.05 -1.8% 0.003
nii_fill_holes 5.64 ±0.02 5.54 ±0.04 -1.7% 0.004
nii_erode_msk 7.74 ±0.05 7.61 ±0.15 -1.6% 0.737
poi_calc_centroids_nocrop 9.05 ±0.07 9.16 ±0.08 +1.2% 0.547
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_save 9.64 ±0.08 9.56 ±0.04 -0.9% 0.017
nii_resample_from_to 39.38 ±0.34 39.06 ±0.19 -0.8% 0.057
nii_dilate_msk 153.89 ±0.57 152.97 ±0.46 -0.6% 0.033
nii_rescale_seg 3.14 ±0.07 3.12 ±0.01 -0.6% 0.130
nii_rescale 37.43 ±0.13 37.34 ±0.18 -0.2% 0.757
poi_calc_poi_from_subreg_vert 10.90 ±0.03 10.90 ±0.07 -0.0% 0.595
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.04 ±0.00 0.06 ±0.00 +35.5% (noise) 0.000
nii_set_dtype 0.53 ±0.02 0.59 ±0.05 +12.1% (noise) 0.041
poi_to_global 0.29 ±0.02 0.31 ±0.01 +6.7% (noise) 0.032
nii_extract_label 0.54 ±0.05 0.51 ±0.02 -6.1% (noise) 0.053
nii_get_connected_components 2.22 ±0.07 2.10 ±0.03 -5.5% (noise) 0.011
poi_rescale 0.33 ±0.01 0.31 ±0.02 -5.3% (noise) 0.145
nii_pad_to 0.81 ±0.03 0.77 ±0.01 -5.0% (noise) 0.071
poi_resample_from_to 0.77 ±0.02 0.81 ±0.02 +4.8% (noise) 0.007
nii_load_seg 2.98 ±0.10 2.84 ±0.02 -4.4% (noise) 0.055
poi_save 0.57 ±0.02 0.60 ±0.01 +4.4% (noise) 0.082
poi_reorient 0.53 ±0.03 0.52 ±0.01 -3.3% (noise) 0.436
nii_unique 0.66 ±0.01 0.64 ±0.01 -3.0% (noise) 0.037
poi_map_labels 0.26 ±0.01 0.26 ±0.00 +2.7% (noise) 0.307
nii_reorient 0.88 ±0.06 0.90 ±0.02 +2.2% (noise) 0.537
poi_calc_centroids 2.93 ±0.03 2.89 ±0.04 -1.5% (noise) 0.057
poi_local_to_global_arr 0.29 ±0.01 0.29 ±0.01 +1.5% (noise) 0.244
nii_map_labels 1.05 ±0.02 1.03 ±0.02 -1.5% (noise) 0.052
poi_load 2.10 ±0.04 2.12 ±0.03 +1.0% (noise) 0.842
nii_apply_crop 0.77 ±0.02 0.76 ±0.02 -0.5% (noise) 0.273
nii_center_of_masses 2.10 ±0.02 2.10 ±0.02 -0.2% (noise) 0.578
nii_volumes 2.04 ±0.02 2.05 ±0.02 +0.1% (noise) 0.284
nii_compute_crop 0.37 ±0.01 0.37 ±0.01 +0.0% (noise) 0.360

mri_2d — shape (68, 52, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_rescale 5.81 ±0.07 5.41 ±0.05 -6.8% 0.000
nii_resample_from_to 7.35 ±0.19 7.21 ±0.12 -2.0% 0.258
nii_dilate_msk 18.27 ±0.06 18.16 ±0.17 -0.6% 0.876
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_rescale_seg 0.86 ±0.04 0.79 ±0.01 -8.6% (noise) 0.005
poi_to_global 0.32 ±0.01 0.29 ±0.02 -8.2% (noise) 0.024
nii_center_of_masses 0.44 ±0.01 0.40 ±0.01 -8.1% (noise) 0.001
poi_rescale 0.33 ±0.03 0.30 ±0.01 -7.1% (noise) 0.170
nii_erode_msk 1.28 ±0.04 1.20 ±0.03 -6.8% (noise) 0.006
nii_reorient 0.90 ±0.03 0.84 ±0.01 -6.7% (noise) 0.003
poi_calc_centroids_nocrop 1.45 ±0.03 1.35 ±0.04 -6.5% (noise) 0.017
nii_unique 0.15 ±0.01 0.14 ±0.01 -5.2% (noise) 0.177
nii_get_array 0.02 ±0.00 0.02 ±0.00 -4.7% (noise) 0.533
nii_fill_holes 1.09 ±0.02 1.04 ±0.04 -4.6% (noise) 0.085
poi_reorient 0.53 ±0.01 0.51 ±0.02 -4.4% (noise) 0.043
nii_set_dtype 0.46 ±0.04 0.44 ±0.02 -4.2% (noise) 0.480
nii_extract_label 0.46 ±0.02 0.44 ±0.01 -4.2% (noise) 0.050
nii_compute_crop 0.17 ±0.02 0.17 ±0.01 -3.8% (noise) 0.907
nii_pad_to 0.62 ±0.02 0.60 ±0.02 -3.6% (noise) 0.487
nii_volumes 0.49 ±0.01 0.47 ±0.01 -3.6% (noise) 0.048
nii_load_img 1.46 ±0.03 1.51 ±0.11 +3.3% (noise) 0.284
poi_save 0.60 ±0.02 0.62 ±0.02 +2.6% (noise) 0.322
poi_local_to_global_arr 0.30 ±0.01 0.30 ±0.03 +2.6% (noise) 0.313
nii_save 1.41 ±0.02 1.39 ±0.02 -1.7% (noise) 0.315
poi_load 1.91 ±0.02 1.88 ±0.01 -1.1% (noise) 0.073
nii_get_connected_components 0.69 ±0.03 0.68 ±0.03 -1.1% (noise) 0.495
poi_calc_centroids 1.15 ±0.03 1.14 ±0.01 -1.0% (noise) 0.835
nii_filter_connected_components 0.90 ±0.02 0.89 ±0.00 -0.9% (noise) 0.066
nii_map_labels 0.55 ±0.04 0.55 ±0.04 -0.9% (noise) 0.626
nii_apply_crop 0.72 ±0.02 0.72 ±0.03 -0.9% (noise) 0.841
poi_map_labels 0.26 ±0.01 0.26 ±0.01 +0.4% (noise) 0.583
poi_resample_from_to 0.79 ±0.01 0.79 ±0.01 -0.4% (noise) 0.514
nii_load_seg 1.65 ±0.06 1.64 ±0.02 -0.1% (noise) 0.887

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 439b142 vs head 439b142 · 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
nii_load_img 3.52 ±0.00 3.57 ±0.00 +1.4%
poi_resample_from_to 5.18 ±0.00 5.14 ±0.00 -0.8%
nii_set_dtype 5.18 ±0.00 5.15 ±0.00 -0.7%
nii_apply_crop 5.81 ±0.00 5.77 ±0.00 -0.7%
poi_calc_centroids_nocrop 5.87 ±0.00 5.83 ±0.00 -0.7%
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_extract_label 5.31 ±0.00 5.27 ±0.00 -0.7%
nii_pad_to 5.37 ±0.00 5.33 ±0.00 -0.7%
poi_calc_centroids 6.00 ±0.00 5.96 ±0.00 -0.7%
nii_map_labels 5.43 ±0.00 5.40 ±0.00 -0.6%
nii_resample_from_to 6.20 ±0.00 6.16 ±0.00 -0.6%
nii_fill_holes 6.23 ±0.00 6.19 ±0.00 -0.6%
nii_reorient 5.81 ±0.00 5.77 ±0.00 -0.6%
nii_rescale_seg 5.89 ±0.00 5.85 ±0.00 -0.6%
nii_rescale 6.07 ±0.00 6.04 ±0.00 -0.6%
nii_filter_connected_components 6.81 ±0.00 6.77 ±0.00 -0.6%
poi_calc_poi_from_subreg_vert 7.06 ±0.00 7.02 ±0.00 -0.6% 0.000
nii_get_connected_components 6.56 ±0.00 6.52 ±0.00 -0.5%
poi_to_global 3.08 ±0.00 3.07 ±0.00 -0.5%
nii_dilate_msk 5.86 ±0.00 5.84 ±0.00 -0.5%
nii_erode_msk 5.88 ±0.00 5.85 ±0.00 -0.5%
nii_load_seg 5.91 ±0.00 5.94 ±0.00 +0.5%
poi_reorient 3.83 ±0.00 3.82 ±0.00 -0.4%
nii_center_of_masses 3.27 ±0.00 3.26 ±0.00 -0.4%
nii_volumes 3.52 ±0.00 3.51 ±0.00 -0.3%
nii_save 3.71 ±0.00 3.70 ±0.00 -0.3%
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 2.61 ±0.00 2.59 ±0.00 -0.6% (noise)
poi_local_to_global_arr 2.96 ±0.00 2.94 ±0.00 -0.5% (noise)
nii_get_array 1.64 ±0.00 1.65 ±0.00 +0.5% (noise)
nii_compute_crop 2.89 ±0.00 2.88 ±0.00 -0.4% (noise)
nii_unique 2.96 ±0.00 2.95 ±0.00 -0.4% (noise)
poi_save 1.40 ±0.00 1.41 ±0.00 +0.3% (noise)
poi_load 1.61 ±0.00 1.62 ±0.00 +0.2% (noise)
poi_map_labels 1.77 ±0.00 1.78 ±0.00 +0.2% (noise)

ct_2d — shape (73, 47, 1)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_load_img 3.46 ±0.00 3.51 ±0.00 +1.4%
poi_resample_from_to 5.12 ±0.00 5.08 ±0.00 -0.8%
nii_set_dtype 5.12 ±0.00 5.08 ±0.00 -0.8%
nii_extract_label 5.25 ±0.00 5.21 ±0.00 -0.7%
nii_pad_to 5.30 ±0.00 5.27 ±0.00 -0.7%
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_map_labels 5.37 ±0.00 5.33 ±0.00 -0.7%
nii_fill_holes 6.16 ±0.00 6.12 ±0.00 -0.7%
nii_reorient 5.75 ±0.00 5.71 ±0.00 -0.7%
nii_apply_crop 5.75 ±0.00 5.71 ±0.00 -0.7%
nii_rescale_seg 5.82 ±0.00 5.79 ±0.00 -0.7%
poi_calc_centroids_nocrop 5.87 ±0.00 5.83 ±0.00 -0.7%
poi_calc_centroids 5.93 ±0.00 5.89 ±0.00 -0.7% 0.000
nii_rescale 6.01 ±0.00 5.97 ±0.00 -0.6%
nii_resample_from_to 6.14 ±0.00 6.10 ±0.00 -0.6%
nii_get_connected_components 6.50 ±0.00 6.46 ±0.00 -0.6%
nii_filter_connected_components 6.75 ±0.00 6.71 ±0.00 -0.6%
nii_dilate_msk 5.80 ±0.00 5.77 ±0.00 -0.5%
nii_erode_msk 5.82 ±0.00 5.79 ±0.00 -0.5%
poi_to_global 3.02 ±0.00 3.00 ±0.00 -0.5%
nii_center_of_masses 3.21 ±0.00 3.19 ±0.00 -0.5%
nii_volumes 3.46 ±0.00 3.44 ±0.00 -0.5%
nii_save 3.64 ±0.00 3.63 ±0.00 -0.4%
poi_reorient 3.77 ±0.00 3.75 ±0.00 -0.4%
nii_load_seg 5.85 ±0.00 5.88 ±0.00 +0.4%
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%
poi_rescale 2.54 ±0.00 2.52 ±0.00 -0.6% (noise)
nii_compute_crop 2.83 ±0.00 2.82 ±0.00 -0.6% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
nii_unique 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
poi_save 1.34 ±0.00 1.34 ±0.00 +0.3% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.3% (noise)
nii_get_array 1.58 ±0.00 1.59 ±0.00 +0.2% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.2% (noise)

mri_3d — shape (68, 52, 67)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_load_img 3.52 ±0.00 3.57 ±0.00 +1.3%
poi_resample_from_to 5.12 ±0.00 5.08 ±0.00 -0.8%
nii_set_dtype 5.18 ±0.00 5.14 ±0.00 -0.8%
nii_extract_label 5.25 ±0.00 5.21 ±0.00 -0.7%
nii_pad_to 5.30 ±0.00 5.27 ±0.00 -0.7%
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_map_labels 5.37 ±0.00 5.33 ±0.00 -0.7%
nii_fill_holes 6.16 ±0.00 6.12 ±0.00 -0.7%
nii_reorient 5.75 ±0.00 5.71 ±0.00 -0.7%
nii_apply_crop 5.75 ±0.00 5.71 ±0.00 -0.7%
poi_calc_centroids_nocrop 5.80 ±0.00 5.77 ±0.00 -0.7%
nii_rescale_seg 5.82 ±0.00 5.79 ±0.00 -0.7%
poi_calc_centroids 5.93 ±0.00 5.89 ±0.00 -0.7%
nii_rescale 6.01 ±0.00 5.97 ±0.00 -0.6%
nii_resample_from_to 6.14 ±0.00 6.10 ±0.00 -0.6%
nii_get_connected_components 6.50 ±0.00 6.46 ±0.00 -0.6%
nii_filter_connected_components 6.75 ±0.00 6.71 ±0.00 -0.6%
poi_calc_poi_from_subreg_vert 7.05 ±0.00 7.02 ±0.00 -0.6%
nii_dilate_msk 5.80 ±0.00 5.77 ±0.00 -0.5%
nii_erode_msk 5.82 ±0.00 5.79 ±0.00 -0.5%
poi_to_global 3.02 ±0.00 3.00 ±0.00 -0.5%
nii_center_of_masses 3.21 ±0.00 3.19 ±0.00 -0.5%
nii_volumes 3.46 ±0.00 3.44 ±0.00 -0.5%
nii_save 3.64 ±0.00 3.63 ±0.00 -0.4%
poi_reorient 3.77 ±0.00 3.75 ±0.00 -0.4%
nii_load_seg 5.85 ±0.00 5.88 ±0.00 +0.4%
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%
poi_rescale 2.54 ±0.00 2.52 ±0.00 -0.6% (noise)
nii_compute_crop 2.83 ±0.00 2.82 ±0.00 -0.6% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
nii_unique 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
poi_save 1.40 ±0.00 1.40 ±0.00 +0.3% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.3% (noise)
nii_get_array 1.58 ±0.00 1.59 ±0.00 +0.2% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.2% (noise)

mri_2d — shape (68, 52, 1)

Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_load_img 3.52 ±0.00 3.57 ±0.00 +1.3%
poi_resample_from_to 5.12 ±0.00 5.08 ±0.00 -0.8%
nii_set_dtype 5.18 ±0.00 5.14 ±0.00 -0.8%
nii_extract_label 5.25 ±0.00 5.21 ±0.00 -0.7%
nii_pad_to 5.30 ±0.00 5.27 ±0.00 -0.7%
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_map_labels 5.37 ±0.00 5.33 ±0.00 -0.7%
nii_fill_holes 6.16 ±0.00 6.12 ±0.00 -0.7%
nii_reorient 5.75 ±0.00 5.71 ±0.00 -0.7%
nii_apply_crop 5.75 ±0.00 5.71 ±0.00 -0.7%
nii_rescale_seg 5.82 ±0.00 5.79 ±0.00 -0.7%
poi_calc_centroids_nocrop 5.87 ±0.00 5.83 ±0.00 -0.7%
poi_calc_centroids 5.93 ±0.00 5.89 ±0.00 -0.7%
nii_rescale 6.01 ±0.00 5.97 ±0.00 -0.6%
nii_resample_from_to 6.14 ±0.00 6.10 ±0.00 -0.6%
nii_get_connected_components 6.50 ±0.00 6.46 ±0.00 -0.6%
nii_filter_connected_components 6.75 ±0.00 6.71 ±0.00 -0.6%
nii_dilate_msk 5.80 ±0.00 5.77 ±0.00 -0.5%
nii_erode_msk 5.82 ±0.00 5.79 ±0.00 -0.5%
poi_to_global 3.02 ±0.00 3.00 ±0.00 -0.5%
nii_center_of_masses 3.21 ±0.00 3.19 ±0.00 -0.5%
nii_volumes 3.46 ±0.00 3.44 ±0.00 -0.5%
nii_save 3.64 ±0.00 3.63 ±0.00 -0.4%
poi_reorient 3.77 ±0.00 3.75 ±0.00 -0.4%
nii_load_seg 5.85 ±0.00 5.88 ±0.00 +0.4%
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%
poi_rescale 2.54 ±0.00 2.52 ±0.00 -0.6% (noise)
nii_compute_crop 2.83 ±0.00 2.82 ±0.00 -0.6% (noise)
poi_local_to_global_arr 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
nii_unique 2.89 ±0.00 2.88 ±0.00 -0.5% (noise)
poi_save 1.40 ±0.00 1.40 ±0.00 +0.3% (noise)
poi_load 1.55 ±0.00 1.55 ±0.00 +0.3% (noise)
nii_get_array 1.58 ±0.00 1.59 ±0.00 +0.2% (noise)
poi_map_labels 1.71 ±0.00 1.71 ±0.00 +0.2% (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 27, 2026
@Hendrik-code
Hendrik-code merged commit 748c8e0 into main Aug 28, 2026
6 checks passed
@Hendrik-code
Hendrik-code deleted the feat/upstream-point-and-normal-helpers branch August 28, 2026 10:39
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