Skip to content

Commit 001b838

Browse files
authored
Merge pull request #369 from fbanelli/fix/vectorized-sig2noise-flag
fix: apply validity flag in vectorized_sig2noise_ratio
2 parents 5f7fd0f + d2ec6ec commit 001b838

2 files changed

Lines changed: 62 additions & 15 deletions

File tree

openpiv/pyprocess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ def vectorized_sig2noise_ratio(correlation,
646646
out=np.zeros_like(peaks1),
647647
where=(peaks2 > 0.0)
648648
)
649-
peak2peak[flag is True] = 0 # replace invalid values
649+
peak2peak[flag] = 0 # replace invalid values
650650
return peak2peak
651651

652652
elif sig2noise_method == "peak2mean":
@@ -667,7 +667,7 @@ def vectorized_sig2noise_ratio(correlation,
667667
out=np.zeros_like(peaks1max),
668668
where=(peaks2mean > 0.0)
669669
)
670-
peak2mean[flag is True] = 0 # replace invalid values
670+
peak2mean[flag] = 0 # replace invalid values
671671
return peak2mean
672672
else:
673673
raise ValueError(f"sig2noise_method not supported: {sig2noise_method}")

openpiv/test/test_pyprocess.py

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -213,31 +213,39 @@ def test_find_subpixel_peak_position():
213213
find_subpixel_peak_position(corr_gauss, subpixel_method="invalid_method")
214214

215215
def test_vectorized_sig2noise_ratio():
216-
"""Test vectorized_sig2noise_ratio function"""
217-
# Create a simple correlation map with a clear peak
218-
corr = np.zeros((3, 5, 5))
216+
"""Test vectorized_sig2noise_ratio function
219217
220-
# First correlation map: clear peak
221-
corr[0, 2, 2] = 1.0
222-
corr[0, :2, :] = 0.1
223-
corr[0, 3:, :] = 0.1
218+
The correlation maps are 16 x 16 with all peaks away from the map
219+
borders, so no window trips the validity flag (weak or border peaks)
220+
and every ratio stays positive.
221+
"""
222+
corr = np.full((3, 16, 16), 0.05)
224223

225-
# Second correlation map: two peaks
226-
corr[1, 2, 2] = 1.0
227-
corr[1, 0, 0] = 0.5
224+
# First correlation map: clear peak, faint interior second peak
225+
corr[0, 8, 8] = 1.0
226+
corr[0, 4, 4] = 0.1
228227

229-
# Third correlation map: noisy
230-
corr[2, 2, 2] = 0.3
231-
corr[2] = corr[2] + 0.1
228+
# Second correlation map: two interior peaks; the secondary one at
229+
# (8, 10) sits inside the width=2 exclusion box but outside the
230+
# width=1 box, so the ratio must depend on the chosen width
231+
corr[1, 8, 8] = 1.0
232+
corr[1, 8, 10] = 0.5
233+
corr[1, 4, 4] = 0.3
234+
235+
# Third correlation map: noisy, two interior peaks of similar height
236+
corr[2, 8, 8] = 0.4
237+
corr[2, 4, 12] = 0.35
232238

233239
# Test peak2peak method
234240
s2n_p2p = vectorized_sig2noise_ratio(corr, sig2noise_method='peak2peak', width=1)
235241
assert s2n_p2p.shape == (3,)
242+
assert np.all(s2n_p2p > 0) # no window is flagged
236243
assert s2n_p2p[0] > s2n_p2p[2] # Clear peak should have higher S2N than noisy
237244

238245
# Test peak2mean method
239246
s2n_p2m = vectorized_sig2noise_ratio(corr, sig2noise_method='peak2mean')
240247
assert s2n_p2m.shape == (3,)
248+
assert np.all(s2n_p2m > 0) # no window is flagged
241249
assert s2n_p2m[0] > s2n_p2m[2] # Clear peak should have higher S2N than noisy
242250

243251
# Test with different width
@@ -249,6 +257,45 @@ def test_vectorized_sig2noise_ratio():
249257
with pytest.raises(Exception):
250258
vectorized_sig2noise_ratio(corr, sig2noise_method='invalid_method')
251259

260+
def test_vectorized_sig2noise_ratio_flags_invalid_windows():
261+
"""Windows failing the validity checks must return a zero ratio.
262+
263+
Regression test: ``peak2peak[flag is True] = 0`` indexed with the
264+
Python expression ``flag is True`` (a constant ``False``), which numpy
265+
treats as an empty boolean mask, so the flag was silently never
266+
applied and invalid windows leaked raw ratios.
267+
"""
268+
corr = np.full((4, 16, 16), 0.05)
269+
270+
# Window 0: healthy interior peaks -> must stay positive
271+
corr[0, 8, 8] = 1.0
272+
corr[0, 4, 4] = 0.5
273+
274+
# Window 1: first peak on the map border
275+
corr[1, 0, 5] = 1.0
276+
corr[1, 8, 8] = 0.5
277+
278+
# Window 2: no signal (first peak below the 1e-3 threshold)
279+
corr[2] = 1e-5
280+
corr[2, 8, 8] = 5e-4
281+
282+
# Window 3: healthy first peak but second peak on the map border
283+
corr[3, 8, 8] = 1.0
284+
corr[3, 0, 3] = 0.9
285+
286+
s2n_p2p = vectorized_sig2noise_ratio(corr, sig2noise_method='peak2peak', width=1)
287+
assert s2n_p2p[0] > 0
288+
assert s2n_p2p[1] == 0 # border first peak
289+
assert s2n_p2p[2] == 0 # no signal
290+
assert s2n_p2p[3] == 0 # border second peak
291+
292+
# peak2mean only checks the first peak
293+
s2n_p2m = vectorized_sig2noise_ratio(corr, sig2noise_method='peak2mean')
294+
assert s2n_p2m[0] > 0
295+
assert s2n_p2m[1] == 0 # border first peak
296+
assert s2n_p2m[2] == 0 # no signal
297+
assert s2n_p2m[3] > 0 # second peak is irrelevant for peak2mean
298+
252299
def test_fft_correlate_images():
253300
"""Test fft_correlate_images function"""
254301
# Create simple test images

0 commit comments

Comments
 (0)