Skip to content

Commit ca5ee04

Browse files
fix: preserve unexpected peak refinement failures (#140)
* fix: preserve unexpected peak refinement failures * test: cover peak fit warnings and error propagation --------- Co-authored-by: Peter Corke <peter.i.corke@gmail.com>
1 parent 890d805 commit ca5ee04

2 files changed

Lines changed: 30 additions & 4 deletions

File tree

‎src/machinevisiontoolbox/base/findpeaks.py‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from __future__ import annotations
66

7+
import warnings
8+
79
import numpy as np
810
import scipy as sp
911
import spatialmath.base as base
@@ -116,10 +118,13 @@ def findpeaks(
116118
poly, *_ = Polynomial.fit(
117119
x[i - n2 : i + n2 + 1], y[i - n2 : i + n2 + 1], interp, full=True
118120
)
119-
except:
120-
# handle situation where neighbourhood falls off the data
121-
# vector
122-
print(f"Peak at {x[i]} couldn't be fitted, skipping")
121+
except (ValueError, np.linalg.LinAlgError):
122+
# A neighbourhood near the boundary can contain too few samples.
123+
warnings.warn(
124+
f"Peak at {x[i]} couldn't be fitted, skipping",
125+
RuntimeWarning,
126+
stacklevel=2,
127+
)
123128
continue
124129

125130
# find the roots of the polynomial closest to the coarse peak

‎tests/base/test_findpeaks.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
import unittest
2+
from unittest.mock import patch
23

34
import numpy as np
5+
from numpy.polynomial import Polynomial
46

57
from machinevisiontoolbox import findpeaks, findpeaks2d, findpeaks3d
68

79

810
class TestFindpeaks(unittest.TestCase):
911
"""Test cases for 1D peak finding"""
1012

13+
def test_failed_refinement_warns_and_skips(self):
14+
"""Warn when the fit neighbourhood extends beyond the signal."""
15+
y = np.array([0, 2, 0, 0, 0, 0, 0], dtype=float)
16+
with self.assertWarnsRegex(
17+
RuntimeWarning, "Peak at 1 couldn't be fitted, skipping"
18+
):
19+
x, ymax = findpeaks(y, interp=6, npeaks=1)
20+
self.assertEqual(x.size, 0)
21+
self.assertEqual(ymax.size, 0)
22+
23+
def test_refinement_preserves_unexpected_exceptions(self):
24+
"""Let unrelated failures and user interrupts reach the caller."""
25+
y = np.array([0, 1, 2, 1, 0], dtype=float)
26+
for error in (RuntimeError("unexpected failure"), KeyboardInterrupt()):
27+
with self.subTest(error=type(error).__name__):
28+
with patch.object(Polynomial, "fit", side_effect=error):
29+
with self.assertRaises(type(error)):
30+
findpeaks(y, interp=True)
31+
1132
def test_simple_peak(self):
1233
"""Test detection of simple peak"""
1334
y = np.array([0, 0, 1, 2, 0, 0, 0, 3, 1, 0, 0, 0, 0])

0 commit comments

Comments
 (0)