From b60bd3f8274d52a88fefa2664af29fb83b7bce5f Mon Sep 17 00:00:00 2001 From: rtmalikian Date: Thu, 18 Jun 2026 00:00:25 -0700 Subject: [PATCH 1/2] fix: use P-axis length for ys calculation and clip S-axis bounds Fixes part 2 of #167: - Use mask_RPS.shape[1] (P-axis) instead of shape[2] (S-axis) for ys calculation, since the defacing line iterates over P positions and computes S-axis limits for each. - Clip y values to mask_RPS.shape[2] to prevent IndexError when the defacing line extends beyond the S-axis range. Previously, using shape[2] for the arrange caused the defacing loop to iterate over S-axis positions instead of P-axis positions, which produced incorrect defacing masks for images with narrow S axes. The old code also lacked bounds clipping, causing IndexError when computed S-axis limits exceeded the array dimensions. --- .../defacing/quickshear/nipy_quickshear.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py b/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py index 6654eaa..4fd4b13 100644 --- a/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py +++ b/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py @@ -155,11 +155,12 @@ def run_quickshear(bet_img: nb.nifti1.Nifti1Image, buffer: int = 10) -> NDArray: slope = ydiffs[0] / xdiffs[0] yint = low[1][0] - (low[0][0] * slope) - buffer - ys = np.arange(0, mask_RPS.shape[2]) * slope + yint + ys = np.arange(0, mask_RPS.shape[1]) * slope + yint defaced_mask_RPS = np.ones(mask_RPS.shape, dtype="bool") for x, y in zip(np.nonzero(ys > 0)[0], ys.astype(int)): - defaced_mask_RPS[:, x, :y] = 0 + y_clipped = min(y, mask_RPS.shape[2]) + defaced_mask_RPS[:, x, :y_clipped] = 0 defaced_mask = nb.orientations.apply_orientation(defaced_mask_RPS, from_RPS) From d74ba162f8d7f25347d29d1770a5805000632a41 Mon Sep 17 00:00:00 2001 From: Raphael Malikian Date: Sun, 1 Nov 2026 15:34:49 -0800 Subject: [PATCH 2/2] fix: read quickshear cutoff from ys[x] instead of mismatched zip pairing Copilot review on #180: zipping the filtered P-axis indices with the unfiltered ys array pairs each position with the wrong cutoff whenever ys starts with a nonpositive prefix, and a negative cutoff makes the slice erase the wrong region. Read each cutoff from ys[x] so it corresponds to the P-axis position being processed. Behaviour is unchanged on the common falling-ys path (verified identical output hashes on synthetic masks); the rising-ys path is now correct. Signed-off-by: Raphael Malikian --- .../defacing/quickshear/nipy_quickshear.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py b/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py index 4fd4b13..2123887 100644 --- a/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py +++ b/brainles_preprocessing/defacing/quickshear/nipy_quickshear.py @@ -158,8 +158,12 @@ def run_quickshear(bet_img: nb.nifti1.Nifti1Image, buffer: int = 10) -> NDArray: ys = np.arange(0, mask_RPS.shape[1]) * slope + yint defaced_mask_RPS = np.ones(mask_RPS.shape, dtype="bool") - for x, y in zip(np.nonzero(ys > 0)[0], ys.astype(int)): - y_clipped = min(y, mask_RPS.shape[2]) + for x in np.nonzero(ys > 0)[0]: + # Read the cutoff from ys[x]: zipping the filtered indices with the + # unfiltered ys array pairs each position with the wrong cutoff + # whenever ys starts with a nonpositive prefix, and a negative cutoff + # would make the slice erase the wrong region. + y_clipped = min(int(ys[x]), mask_RPS.shape[2]) defaced_mask_RPS[:, x, :y_clipped] = 0 defaced_mask = nb.orientations.apply_orientation(defaced_mask_RPS, from_RPS)