mekosotto Claude Sonnet 4.6 commited on
Commit
782869f
·
1 Parent(s): 5833bcb

fix(mri): warn on all-False mask; document 6-connectivity erosion caveat

Browse files

Addresses I1, I2, M1 from code review:
- Docstring step-2 now calls out 6-connectivity, iterations=1, and the
thin-feature erosion caveat (suggesting 26-connectivity for production).
- New paragraph documents the all-False WARNING behaviour.
- logger.warning emitted when cleaned.any() is False, reporting volume
min/max and effective threshold so silent feature-zeroing is visible
in production logs.
- .astype(bool) moved onto the binary_opening line; return is just cleaned.
- New regression test: constant-valued volume produces empty mask + WARNING.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

src/pipelines/mri_pipeline.py CHANGED
@@ -63,8 +63,15 @@ def mask_brain(
63
  `None`, use the volume's mean as a robust auto-threshold (works on
64
  the synthetic fixture where brain ≫ background; for real data the
65
  caller should pass an Otsu or BET-derived threshold explicitly).
66
- 2. Morphological opening (`scipy.ndimage.binary_opening`) to remove
67
- isolated noise voxels and disconnected fragments.
 
 
 
 
 
 
 
68
 
69
  Args:
70
  volume: 3-D numeric `np.ndarray` (must satisfy `is_valid_volume`).
@@ -77,5 +84,12 @@ def mask_brain(
77
  intensity_threshold = float(volume.mean())
78
 
79
  raw = volume > intensity_threshold
80
- cleaned = scipy_ndimage.binary_opening(raw, iterations=1)
81
- return cleaned.astype(bool)
 
 
 
 
 
 
 
 
63
  `None`, use the volume's mean as a robust auto-threshold (works on
64
  the synthetic fixture where brain ≫ background; for real data the
65
  caller should pass an Otsu or BET-derived threshold explicitly).
66
+ 2. Morphological opening (`scipy.ndimage.binary_opening`, 6-connectivity,
67
+ iterations=1) to remove isolated noise voxels and disconnected
68
+ fragments. Note: thin features (< 3 voxels wide along any axis pair)
69
+ may be eroded entirely; for production data with cortical sheets or
70
+ sulcal bridges, prefer 26-connectivity or pass `iterations=0` upstream.
71
+
72
+ If the resulting mask is all-False (e.g. caller passed a threshold above
73
+ the volume's max intensity, or the volume is constant-valued), a WARNING
74
+ is emitted so silent feature-zeroing is visible in production logs.
75
 
76
  Args:
77
  volume: 3-D numeric `np.ndarray` (must satisfy `is_valid_volume`).
 
84
  intensity_threshold = float(volume.mean())
85
 
86
  raw = volume > intensity_threshold
87
+ cleaned = scipy_ndimage.binary_opening(raw, iterations=1).astype(bool)
88
+ if not cleaned.any():
89
+ logger.warning(
90
+ "mask_brain produced an all-False mask "
91
+ "(volume min=%.4f, max=%.4f, threshold=%.4f); "
92
+ "downstream features for this volume will be all-zero.",
93
+ float(volume.min()), float(volume.max()), intensity_threshold,
94
+ )
95
+ return cleaned
tests/pipelines/test_mri_pipeline.py CHANGED
@@ -103,3 +103,28 @@ class TestMaskBrain:
103
  # Without cleanup, the single voxel would survive. With morphological
104
  # opening, it must be removed.
105
  assert mask.sum() == 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  # Without cleanup, the single voxel would survive. With morphological
104
  # opening, it must be removed.
105
  assert mask.sum() == 0
106
+
107
+ def test_constant_volume_returns_all_false_mask_with_warning(self) -> None:
108
+ """A constant-valued volume produces an empty mask AND logs a WARNING."""
109
+ import io
110
+ import logging
111
+
112
+ from src.core.logger import get_logger
113
+ from src.pipelines import mri_pipeline as mod
114
+
115
+ vol = np.full((8, 8, 8), 5.0, dtype=np.float64)
116
+
117
+ logger = get_logger(mod.__name__, level=logging.INFO)
118
+ handler = logger.handlers[0]
119
+ buf = io.StringIO()
120
+ original_stream = handler.stream
121
+ handler.stream = buf
122
+ try:
123
+ mask = mask_brain(vol)
124
+ finally:
125
+ handler.stream = original_stream
126
+
127
+ assert mask.sum() == 0
128
+ log_output = buf.getvalue()
129
+ assert "all-False mask" in log_output
130
+ assert "downstream features for this volume will be all-zero" in log_output