Skip to content

spinestats: version 3 — endplate-plane Cobb + apex + tightened multi-Cobb - #154

Open
robert-graf wants to merge 34 commits into
mainfrom
cleanup/cobb-endplate-v3
Open

robert-graf wants to merge 34 commits into
mainfrom
cleanup/cobb-endplate-v3

Conversation

@robert-graf

Copy link
Copy Markdown
Collaborator

Summary

Extends the endplate-plane direction machinery (used for v2 lordosis/kyphosis) to the coronal Cobb angle, bumps CURRENT_VERSION to 3 so cached stat files recompute the angle keys, and adds per-curve apex vertebra keys.

Stacks on top of these already-(mostly-)merged branches:

  • dicom-extract-cleanup
  • nako-follow-up-export
  • poi-label-names

Changes

TPTBox/spine/spinestats/angles.py

  • _get_norm routes Vertebra_Direction_Right through a new _endplate_r_direction helper (analogue of _endplate_ap_direction), so Cobb lines are true endplate lines averaged across the shared disc.
  • plot_cobb_angle always uses the endplate right direction — the old use_ivd_direction branch used Vertebra_Disc_Inferior from each vertebra separately, giving two different lines at one anatomic disc.
  • compute_angel_between_two_points_ orders id1/id2 by Vertebra_Instance.order_dict() rather than by numeric label, so T13 (label 28, anatomically between T12 and L1) pairs to the right endplates.
  • compute_max_cobb_angle_multi uses an exclusive split (+ 1 on the below slice) so sibling curves cannot share an endpoint vertebra.
  • VERT_START_COBB is C3 (C2 excluded from Cobb search).
  • compute_lordosis_and_kyphosis emits {curve}_apex keys via a new _find_curve_apex helper; plot_compute_lordosis_and_kyphosis renders a *apex marker.
  • plot_cobb_angle anchors the label at the apex disc and widens the coronal pad from 50 to 80 mm.

TPTBox/spine/spinestats/_run_all.py

  • New POI_INVALIDATING_VERSION = 2 constant replaces a bare literal 2 in _poi_is_stale_wrt_stat; its docstring now references a symbol that actually exists (previously pointed at a nonexistent ANGLES_VERSION).
  • Documents the v3 bump in the CURRENT_VERSION history comment.

TPTBox/spine/spinestats/veridah_angles.py

  • _compute_k_variant also returns lumbar_lordosis_apex so the per-k variants carry the new apex key.

Test plan

  • pytest unit_tests/
  • Run _run_all.py in test=True mode on a NAKO subject and confirm cobb/curv keys, {curve}_apex values, and that a v2 stat file is recomputed to v3 without invalidating POIs.
  • Eyeball a plot_cobb_angle snapshot and a plot_compute_lordosis_and_kyphosis snapshot to verify the apex marker and label placement.

🤖 Generated with Claude Code

robert-graf and others added 25 commits August 26, 2026 14:42
Roll the internal-scripts changes together with the new
precompute_grid_info_parallel helper in _load_nako_wh, which fans
per-file _add_grid_info_to_json calls out to a ProcessPoolExecutor so
the sidecar grid cache is populated up front.
Adds a small registration mechanism so producers can attach
per-vertebra direction vectors (endplate PCA normals, orientation
vectors, ...) or label-keyed scalars (endplate_internal_angle,
curvature_*) to poi.info and have them stay aligned with the POI
points automatically:

- New TPTBox/core/poi_fun/vector_fields.py with the constants
  POI_INFO_VECTOR_FIELDS_KEY / POI_INFO_LABEL_KEYED_FIELDS_KEY and
  three helpers (_transform_direction_vectors_inplace,
  _rotate_direction_vectors_inplace, _remap_vector_field_keys_inplace).
- POI.reorient signed-permutes vectors; POI.resample_from_to applies
  the real R_ref.T @ R_self rotation; POI.rescale is a no-op.
- POI_Global.to_cord_system flips vector components on RAS<->LPS.
- POI.map_labels remaps top-level keys of both registries.
- endplates.py registers angle_superior/inferior_endplate,
  endplate_internal_angle, and curvature_* when they're produced.
- Fix: vertebra_pois_non_centroids.py now (re)runs
  calc_endplate_points_ when S1 is present but its Sacrum_Endplate-
  derived superior-endplate landmark is missing, so the sacrum
  endplate direction actually lands in the buffer.
- Angles: _endplate_ap_direction preferred over Vertebra_Direction_
  Posterior for MoveTo.TOP/BOTTOM in _get_norm, yielding endplate-
  aligned Cobb-style lordosis/kyphosis lines.

Docs: new section in docs/api/poi_fun.md.
Tests: unit_tests/test_poi_vector_fields.py (13 tests).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Merges the poi-label-names branch (LABEL_NAME + nested
{region: {subregion: name, "name": group}} format in info["label_name"],
new label_name / set_label_name / level_one_name / set_level_one_name
accessors, migration helpers, and level_*_info propagation in copy /
_set_inplace / calc_poi_average / POI_Global.to_other).

Then wires label_name into the auto-transform mapping scheme:

- New vector_fields._remap_label_name_inplace handles the nested
  format: remaps top-level region keys via label_map_region and
  inner subregion keys via label_map_subregion, preserving the
  "name" group entry. Colliding regions merge with last-write-wins
  on inner keys.
- map_labels calls both _remap_vector_field_keys_inplace (for the
  registered flat vector / label-keyed fields) and the new
  _remap_label_name_inplace on all three return paths.
- Also triggers the legacy flat-format migration on remap.

Docs updated with a "Special-cased field" section for label_name.
Tests: 5 new cases in Test_LabelName_MapLabels covering region-only,
subregion-only, combined, region collision, and legacy-format
migration paths (18/18 passing).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…Cobb

Extends the endplate-plane direction machinery used for v2
lordosis/kyphosis to the coronal Cobb angle, and bumps
``CURRENT_VERSION`` to 3 so cached stat files recompute the angle keys
(POI buffers stay valid — no new landmarks).

Angles (``TPTBox/spine/spinestats/angles.py``):
- ``_get_norm`` now routes ``Vertebra_Direction_Right`` through a new
  ``_endplate_r_direction`` helper (analogue of ``_endplate_ap_direction``),
  so Cobb lines are true endplate lines averaged across the shared disc
  instead of the two flanking vertebrae disagreeing on the same boundary.
- ``plot_cobb_angle`` always uses that endplate right direction — the
  ``use_ivd_direction`` branch used ``Vertebra_Disc_Inferior`` from each
  vertebra separately, giving two different lines at one anatomic disc.
- ``compute_angel_between_two_points_`` orders ``id1``/``id2`` by
  ``Vertebra_Instance.order_dict()`` rather than by numeric label, so T13
  (label 28, anatomically between T12 and L1) pairs to the right endplates
  and ``MoveTo.TOP``/``BOTTOM`` are not swapped.
- ``compute_max_cobb_angle_multi`` uses an exclusive split (``+ 1`` on the
  ``below`` slice) so sibling curves cannot share an endpoint vertebra.
- ``VERT_START_COBB`` is C3 (C2 excluded from Cobb search).
- ``compute_lordosis_and_kyphosis`` emits ``{curve}_apex`` keys, computed
  by the new ``_find_curve_apex`` helper (same bisector heuristic as
  ``compute_max_cobb_angle``). ``plot_compute_lordosis_and_kyphosis``
  renders a ``*apex`` marker at that vertebra.
- ``plot_cobb_angle`` anchors the label at the apex disc and widens the
  coronal pad from 50 to 80 mm so labels don't get clipped.

Runner (``TPTBox/spine/spinestats/_run_all.py``):
- Introduces ``POI_INVALIDATING_VERSION = 2`` so the ``_poi_is_stale_wrt_stat``
  threshold is a named constant rather than a magic ``2``, and its docstring
  points at a symbol that actually exists (previously referenced a
  nonexistent ``ANGLES_VERSION``).
- Documents the v3 bump in the ``CURRENT_VERSION`` history comment.

Veridah (``TPTBox/spine/spinestats/veridah_angles.py``):
- ``_compute_k_variant`` also returns ``lumbar_lordosis_apex`` so the
  per-k variants carry the new apex key.

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

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

Benchmark comparison

Speed (wall time per call)

baseline cc2dd08 vs head cc2dd08 · 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_filter_connected_components 4.22 ±0.12 4.12 ±0.05 -2.4% 0.120
poi_calc_centroids_nocrop 4.07 ±0.15 4.12 ±0.17 +1.5% 0.811
nii_save 11.49 ±0.07 11.34 ±0.06 -1.3% 0.005
nii_dilate_msk 125.46 ±0.36 124.02 ±2.67 -1.2% 0.971
nii_erode_msk 6.12 ±0.09 6.05 ±0.11 -1.1% 0.372
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_fill_holes 4.38 ±0.06 4.36 ±0.08 -0.5% 0.973
nii_load_img 10.62 ±0.02 10.57 ±0.09 -0.5% 0.518
nii_rescale 39.08 ±0.15 39.14 ±0.09 +0.2% 0.139
poi_calc_poi_from_subreg_vert 12.51 ±0.17 12.50 ±0.16 -0.1% 0.599
nii_resample_from_to 41.33 ±0.21 41.32 ±0.13 -0.0% 0.844
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.04 ±0.00 0.03 ±0.00 -29.9% (noise) 0.000
nii_pad_to 0.56 ±0.07 0.52 ±0.04 -7.6% (noise) 0.110
poi_to_global 0.16 ±0.01 0.15 ±0.02 -7.4% (noise) 0.375
poi_local_to_global_arr 0.26 ±0.02 0.28 ±0.01 +6.0% (noise) 0.854
nii_load_seg 2.67 ±0.16 2.51 ±0.03 -5.9% (noise) 0.041
nii_extract_label 0.32 ±0.01 0.30 ±0.01 -5.4% (noise) 0.329
poi_rescale 0.17 ±0.02 0.16 ±0.01 -4.7% (noise) 0.229
nii_set_dtype 0.40 ±0.01 0.38 ±0.03 -3.5% (noise) 0.594
nii_get_connected_components 2.15 ±0.08 2.08 ±0.03 -3.1% (noise) 0.193
poi_resample_from_to 0.56 ±0.01 0.58 ±0.02 +3.1% (noise) 0.062
nii_map_labels 0.87 ±0.01 0.89 ±0.02 +3.1% (noise) 0.052
nii_reorient 0.67 ±0.06 0.69 ±0.07 +2.3% (noise) 0.727
nii_rescale_seg 2.98 ±0.02 3.04 ±0.06 +2.3% (noise) 0.101
poi_calc_centroids 2.33 ±0.04 2.28 ±0.03 -2.0% (noise) 0.114
nii_unique 0.59 ±0.02 0.58 ±0.01 -1.6% (noise) 0.138
poi_save 0.35 ±0.03 0.35 ±0.02 +1.6% (noise) 0.716
poi_map_labels 0.13 ±0.01 0.13 ±0.02 -1.4% (noise) 0.705
nii_apply_crop 0.52 ±0.05 0.52 ±0.03 +1.1% (noise) 0.499
poi_reorient 0.33 ±0.03 0.34 ±0.02 +1.0% (noise) 0.566
poi_load 0.89 ±0.04 0.89 ±0.01 -0.9% (noise) 0.615
nii_center_of_masses 1.69 ±0.03 1.68 ±0.02 -0.7% (noise) 0.843
nii_compute_crop 0.27 ±0.02 0.27 ±0.01 -0.4% (noise) 0.729
nii_volumes 1.78 ±0.01 1.77 ±0.06 -0.1% (noise) 0.253

ct_2d — shape (73, 47, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_dilate_msk 14.77 ±0.17 14.57 ±0.10 -1.4% 0.021
nii_resample_from_to 7.43 ±0.04 7.35 ±0.09 -1.0% 0.450
nii_rescale 5.27 ±0.12 5.23 ±0.07 -0.8% 0.978
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% —
poi_rescale 0.25 ±0.01 0.22 ±0.01 -11.8% (noise) 0.000
poi_map_labels 0.18 ±0.01 0.20 ±0.01 +10.1% (noise) 0.053
nii_get_connected_components 0.58 ±0.03 0.63 ±0.04 +7.7% (noise) 0.708
nii_map_labels 0.44 ±0.02 0.47 ±0.03 +7.3% (noise) 0.058
nii_fill_holes 0.80 ±0.01 0.85 ±0.05 +5.7% (noise) 0.158
nii_unique 0.11 ±0.01 0.11 ±0.01 -5.7% (noise) 0.137
nii_extract_label 0.39 ±0.05 0.41 ±0.03 +5.0% (noise) 0.751
poi_resample_from_to 0.69 ±0.01 0.72 ±0.03 +4.8% (noise) 0.041
nii_volumes 0.26 ±0.01 0.25 ±0.01 -4.2% (noise) 0.066
poi_save 0.48 ±0.04 0.47 ±0.03 -3.3% (noise) 0.329
poi_local_to_global_arr 0.33 ±0.01 0.32 ±0.02 -3.3% (noise) 0.361
nii_save 1.31 ±0.04 1.27 ±0.03 -3.0% (noise) 0.096
nii_filter_connected_components 0.81 ±0.03 0.79 ±0.02 -2.8% (noise) 0.154
nii_apply_crop 0.64 ±0.02 0.65 ±0.03 +2.5% (noise) 0.382
nii_set_dtype 0.39 ±0.02 0.38 ±0.01 -2.3% (noise) 0.737
nii_erode_msk 0.96 ±0.03 0.94 ±0.05 -2.2% (noise) 0.367
nii_rescale_seg 0.75 ±0.03 0.74 ±0.09 -2.1% (noise) 0.956
poi_calc_centroids_nocrop 1.09 ±0.03 1.07 ±0.02 -1.9% (noise) 0.086
poi_reorient 0.41 ±0.02 0.40 ±0.02 -1.9% (noise) 0.976
nii_pad_to 0.52 ±0.03 0.53 ±0.04 +1.8% (noise) 0.691
poi_to_global 0.20 ±0.01 0.21 ±0.01 +1.7% (noise) 0.850
nii_reorient 0.79 ±0.03 0.80 ±0.03 +1.5% (noise) 0.973
nii_load_img 1.42 ±0.03 1.44 ±0.02 +1.2% (noise) 0.291
nii_get_array 0.01 ±0.00 0.01 ±0.00 +0.7% (noise) 0.688
nii_center_of_masses 0.19 ±0.01 0.19 ±0.01 -0.5% (noise) 0.546
poi_load 0.98 ±0.02 0.99 ±0.03 +0.5% (noise) 0.171
nii_load_seg 1.59 ±0.04 1.59 ±0.02 +0.3% (noise) 0.505
nii_compute_crop 0.14 ±0.00 0.14 ±0.01 +0.2% (noise) 0.745
poi_calc_centroids 0.90 ±0.03 0.90 ±0.03 -0.1% (noise) 0.229

mri_3d — shape (68, 52, 67)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
poi_calc_centroids_nocrop 8.66 ±0.14 8.44 ±0.11 -2.5% 0.039
nii_dilate_msk 163.83 ±0.78 159.83 ±0.76 -2.4% 0.000
nii_filter_connected_components 4.62 ±0.08 4.53 ±0.06 -1.9% 0.079
nii_load_img 8.70 ±0.04 8.58 ±0.06 -1.4% 0.003
nii_rescale_seg 3.03 ±0.03 3.07 ±0.06 +1.3% 0.570
… 31 more measurements
Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
poi_calc_poi_from_subreg_vert 11.92 ±0.04 11.77 ±0.67 -1.3% 0.414
nii_save 9.25 ±0.12 9.15 ±0.09 -1.0% 0.051
nii_resample_from_to 39.79 ±0.44 40.17 ±0.74 +1.0% 0.297
nii_fill_holes 5.34 ±0.05 5.30 ±0.16 -0.7% 0.534
nii_erode_msk 7.67 ±0.14 7.64 ±0.08 -0.3% 0.168
nii_rescale 37.47 ±0.33 37.40 ±0.28 -0.2% 0.393
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_set_dtype 0.54 ±0.04 0.49 ±0.02 -7.7% (noise) 0.128
poi_save 0.53 ±0.03 0.49 ±0.03 -7.7% (noise) 0.072
nii_extract_label 0.45 ±0.03 0.42 ±0.02 -6.5% (noise) 0.153
nii_pad_to 0.70 ±0.04 0.66 ±0.03 -6.0% (noise) 0.151
nii_unique 0.66 ±0.01 0.63 ±0.01 -5.3% (noise) 0.002
nii_load_seg 2.82 ±0.10 2.67 ±0.18 -5.1% (noise) 0.373
nii_apply_crop 0.68 ±0.03 0.65 ±0.01 -5.0% (noise) 0.028
nii_get_connected_components 2.18 ±0.06 2.10 ±0.06 -3.8% (noise) 0.117
poi_map_labels 0.23 ±0.01 0.22 ±0.01 -3.7% (noise) 0.342
poi_local_to_global_arr 0.33 ±0.01 0.31 ±0.00 -3.7% (noise) 0.066
poi_resample_from_to 0.73 ±0.03 0.76 ±0.02 +3.5% (noise) 0.057
poi_rescale 0.27 ±0.02 0.26 ±0.01 -3.3% (noise) 0.504
poi_to_global 0.26 ±0.01 0.25 ±0.01 -2.3% (noise) 0.445
nii_map_labels 0.99 ±0.03 0.97 ±0.04 -2.0% (noise) 0.691
nii_volumes 1.94 ±0.01 1.90 ±0.01 -1.9% (noise) 0.002
poi_reorient 0.45 ±0.02 0.45 ±0.02 -1.8% (noise) 0.852
nii_get_array 0.05 ±0.01 0.05 ±0.00 +1.7% (noise) 0.335
poi_load 2.15 ±0.04 2.13 ±0.01 -1.1% (noise) 0.105
nii_center_of_masses 1.96 ±0.02 1.97 ±0.00 +0.4% (noise) 0.256
nii_reorient 0.83 ±0.02 0.83 ±0.03 -0.4% (noise) 0.942
nii_compute_crop 0.32 ±0.01 0.32 ±0.02 +0.3% (noise) 0.863
poi_calc_centroids 2.72 ±0.04 2.72 ±0.05 -0.2% (noise) 0.604

mri_2d — shape (68, 52, 1)

Measurement baseline ms (median ±½·range) head ms (median ±½·range) Δ % p
nii_resample_from_to 7.49 ±0.13 7.38 ±0.08 -1.5% 0.092
nii_dilate_msk 19.78 ±0.05 19.50 ±0.11 -1.4% 0.003
nii_rescale 5.24 ±0.04 5.24 ±0.06 -0.0% 0.885
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_unique 0.12 ±0.01 0.10 ±0.01 -11.7% (noise) 0.083
nii_extract_label 0.39 ±0.02 0.42 ±0.06 +7.1% (noise) 0.136
nii_set_dtype 0.40 ±0.02 0.37 ±0.01 -6.6% (noise) 0.172
nii_get_array 0.01 ±0.00 0.02 ±0.00 +6.0% (noise) 0.282
nii_load_img 1.47 ±0.05 1.39 ±0.02 -5.3% (noise) 0.049
poi_reorient 0.43 ±0.02 0.45 ±0.02 +4.7% (noise) 0.153
nii_fill_holes 1.06 ±0.04 1.10 ±0.04 +4.2% (noise) 0.089
poi_local_to_global_arr 0.32 ±0.01 0.31 ±0.01 -3.9% (noise) 0.022
nii_save 1.29 ±0.04 1.25 ±0.05 -3.8% (noise) 0.075
nii_apply_crop 0.65 ±0.04 0.63 ±0.02 -3.5% (noise) 0.804
nii_map_labels 0.46 ±0.01 0.47 ±0.03 +3.1% (noise) 0.918
nii_filter_connected_components 0.82 ±0.03 0.79 ±0.02 -2.5% (noise) 0.344
nii_center_of_masses 0.39 ±0.01 0.39 ±0.01 +2.4% (noise) 0.321
nii_reorient 0.83 ±0.02 0.81 ±0.04 -2.4% (noise) 0.445
poi_calc_centroids_nocrop 1.42 ±0.04 1.38 ±0.04 -2.4% (noise) 0.390
poi_to_global 0.24 ±0.02 0.23 ±0.01 -2.2% (noise) 0.188
nii_get_connected_components 0.60 ±0.03 0.59 ±0.04 -2.0% (noise) 0.327
nii_compute_crop 0.14 ±0.01 0.13 ±0.00 -1.8% (noise) 0.239
poi_save 0.53 ±0.01 0.53 ±0.05 +1.4% (noise) 0.371
poi_load 1.96 ±0.05 1.94 ±0.03 -1.3% (noise) 0.565
nii_pad_to 0.53 ±0.03 0.52 ±0.05 -1.1% (noise) 0.969
poi_map_labels 0.21 ±0.01 0.22 ±0.01 +0.9% (noise) 0.801
nii_erode_msk 1.25 ±0.03 1.26 ±0.02 +0.8% (noise) 0.642
nii_load_seg 1.59 ±0.03 1.60 ±0.03 +0.7% (noise) 0.135
nii_rescale_seg 0.73 ±0.02 0.73 ±0.03 -0.7% (noise) 0.729
poi_resample_from_to 0.75 ±0.04 0.74 ±0.03 -0.5% (noise) 0.837
nii_volumes 0.44 ±0.01 0.44 ±0.02 +0.4% (noise) 0.522
poi_calc_centroids 1.11 ±0.01 1.11 ±0.05 +0.4% (noise) 0.684
poi_rescale 0.26 ±0.03 0.26 ±0.01 +0.1% (noise) 0.439

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 cc2dd08 vs head cc2dd08 · 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.18 ±0.00 -1.9% —
nii_reorient 5.91 ±0.00 5.80 ±0.00 -1.7% —
nii_rescale 6.17 ±0.00 6.07 ±0.00 -1.6% —
nii_resample_from_to 6.29 ±0.00 6.20 ±0.00 -1.6% —
nii_set_dtype 5.24 ±0.00 5.18 ±0.00 -1.2% —
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_extract_label 5.37 ±0.00 5.30 ±0.00 -1.2% —
nii_pad_to 5.43 ±0.00 5.36 ±0.00 -1.2% —
nii_map_labels 5.49 ±0.00 5.43 ±0.00 -1.1% —
nii_apply_crop 5.87 ±0.00 5.80 ±0.00 -1.1% —
poi_calc_centroids_nocrop 5.93 ±0.00 5.86 ±0.00 -1.1% —
nii_dilate_msk 5.93 ±0.00 5.87 ±0.00 -1.1% —
poi_calc_centroids 6.05 ±0.00 5.99 ±0.00 -1.0% —
nii_fill_holes 6.28 ±0.00 6.22 ±0.00 -1.0% —
nii_rescale_seg 5.94 ±0.00 5.88 ±0.00 -1.0% —
nii_erode_msk 5.94 ±0.00 5.88 ±0.00 -1.0% —
nii_get_connected_components 6.62 ±0.00 6.55 ±0.00 -0.9% —
nii_filter_connected_components 6.87 ±0.00 6.80 ±0.00 -0.9% —
poi_calc_poi_from_subreg_vert 7.11 ±0.00 7.05 ±0.00 -0.9% —
nii_load_seg 6.01 ±0.00 5.96 ±0.00 -0.8% —
nii_load_img 3.56 ±0.00 3.57 ±0.00 +0.3% —
nii_save 3.71 ±0.00 3.71 ±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.11 ±0.00 -1.9% —
nii_reorient 5.84 ±0.00 5.74 ±0.00 -1.7% —
nii_rescale 6.11 ±0.00 6.01 ±0.00 -1.6% —
nii_resample_from_to 6.23 ±0.00 6.13 ±0.00 -1.6% —
nii_set_dtype 5.18 ±0.00 5.12 ±0.00 -1.2% —
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_extract_label 5.30 ±0.00 5.24 ±0.00 -1.2% —
nii_pad_to 5.36 ±0.00 5.30 ±0.00 -1.2% —
nii_map_labels 5.43 ±0.00 5.37 ±0.00 -1.2% —
nii_apply_crop 5.80 ±0.00 5.74 ±0.00 -1.1% —
nii_dilate_msk 5.87 ±0.00 5.80 ±0.00 -1.1% —
poi_calc_centroids_nocrop 5.93 ±0.00 5.86 ±0.00 -1.1% —
poi_calc_centroids 5.99 ±0.00 5.93 ±0.00 -1.0% —
nii_fill_holes 6.22 ±0.00 6.16 ±0.00 -1.0% —
nii_rescale_seg 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_get_connected_components 6.55 ±0.00 6.49 ±0.00 -1.0% —
nii_filter_connected_components 6.80 ±0.00 6.74 ±0.00 -0.9% —
nii_load_seg 5.95 ±0.00 5.89 ±0.00 -0.9% —
nii_load_img 3.50 ±0.00 3.51 ±0.00 +0.3% —
nii_save 3.65 ±0.00 3.65 ±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.11 ±0.00 -1.9% —
nii_reorient 5.84 ±0.00 5.74 ±0.00 -1.7% —
nii_rescale 6.11 ±0.00 6.01 ±0.00 -1.6% —
nii_resample_from_to 6.23 ±0.00 6.13 ±0.00 -1.6% —
nii_set_dtype 5.24 ±0.00 5.18 ±0.00 -1.2% —
… 31 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_extract_label 5.30 ±0.00 5.24 ±0.00 -1.2% —
nii_pad_to 5.36 ±0.00 5.30 ±0.00 -1.2% —
nii_map_labels 5.43 ±0.00 5.37 ±0.00 -1.2% —
nii_apply_crop 5.80 ±0.00 5.74 ±0.00 -1.1% —
poi_calc_centroids_nocrop 5.86 ±0.00 5.80 ±0.00 -1.1% —
nii_dilate_msk 5.87 ±0.00 5.80 ±0.03 -1.1% 0.004
poi_calc_centroids 5.99 ±0.00 5.93 ±0.00 -1.0% —
nii_fill_holes 6.22 ±0.00 6.16 ±0.00 -1.0% —
nii_rescale_seg 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_get_connected_components 6.55 ±0.00 6.49 ±0.00 -1.0% —
nii_filter_connected_components 6.80 ±0.00 6.74 ±0.00 -0.9% —
poi_calc_poi_from_subreg_vert 7.11 ±0.00 7.05 ±0.00 -0.9% —
nii_load_seg 5.95 ±0.00 5.89 ±0.00 -0.9% —
nii_load_img 3.56 ±0.00 3.57 ±0.00 +0.3% —
nii_save 3.65 ±0.00 3.65 ±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.11 ±0.00 -1.9% —
nii_reorient 5.84 ±0.00 5.74 ±0.00 -1.7% —
nii_rescale 6.11 ±0.00 6.01 ±0.00 -1.6% —
nii_resample_from_to 6.23 ±0.00 6.13 ±0.00 -1.6% —
nii_set_dtype 5.24 ±0.00 5.18 ±0.00 -1.2% —
… 30 more measurements
Measurement baseline MiB (median ±½·range) head MiB (median ±½·range) Δ % p
nii_extract_label 5.30 ±0.00 5.24 ±0.00 -1.2% —
nii_pad_to 5.36 ±0.00 5.30 ±0.00 -1.2% —
nii_map_labels 5.43 ±0.00 5.37 ±0.00 -1.2% —
nii_apply_crop 5.80 ±0.00 5.74 ±0.00 -1.1% —
nii_dilate_msk 5.87 ±0.00 5.80 ±0.00 -1.1% —
poi_calc_centroids_nocrop 5.93 ±0.00 5.86 ±0.00 -1.1% —
poi_calc_centroids 5.99 ±0.00 5.93 ±0.00 -1.0% —
nii_fill_holes 6.22 ±0.00 6.16 ±0.00 -1.0% —
nii_rescale_seg 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_erode_msk 5.88 ±0.00 5.82 ±0.00 -1.0% —
nii_get_connected_components 6.55 ±0.00 6.49 ±0.00 -1.0% —
nii_filter_connected_components 6.80 ±0.00 6.74 ±0.00 -0.9% —
nii_load_seg 5.95 ±0.00 5.89 ±0.00 -0.9% —
nii_load_img 3.56 ±0.00 3.57 ±0.00 +0.3% —
nii_save 3.65 ±0.00 3.65 ±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.

robert-graf and others added 3 commits September 22, 2026 11:10
Cherry-picked content from tanja@50c8b4d without the whitespace-only
reformatting noise:
- Add h2d/tse2d1-4/tser2d regex to series-description → format mapping;
  drop the broad ".*t2w?_tse.*" match.
- Fall back to SequenceName when SeriesDescription is empty.

Co-Authored-By: Tanja Lerchl <tanja.lerchl@tum.de>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@robert-graf robert-graf added the enhancement New feature or request label Sep 22, 2026
robert-graf and others added 6 commits September 22, 2026 11:18
Manual fixes in ``TPTBox/spine/spinestats/_load_nako_wh.py``:
- D205 (verify_missing_images, verify_hardlink): reword docstrings so the
  summary line stands alone from the description body.
- PLW2901 (``v = preferred`` at line 787): add ``# noqa: PLW2901`` to
  match the existing noqa on the sibling ``k = mapping.get(k, k)``
  rebinding — the loop deliberately overrides both bindings.
- PERF102 (line 1385): iterate the ``t2w_chunk`` mapping via ``.values()``
  since only the value is used.
- TRY300 (``_grid_worker``): move the success ``return`` out of the
  ``try`` block; only ``_add_grid_info_to_json`` may raise.

Everything else is ``ruff check --fix`` and ``ruff format`` output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant