From b9b9cd5c655d01cdbaac88f6a2a46775b9cc251c Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sun, 15 Apr 2012 23:15:18 -0400 Subject: [PATCH 1/7] Fix import --- skimage/feature/tests/test_peak.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 19f9e95f..48e8f67c 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -1,6 +1,6 @@ import numpy as np -from skimage import feature +from skimage.feature import peak def test_noisy_peaks(): @@ -11,7 +11,7 @@ def test_noisy_peaks(): for r, c in peak_locations: image[r, c] = 1 - peaks_detected = feature.peak_local_max(image, min_distance=5) + peaks_detected = peak.peak_local_max(image, min_distance=5) assert len(peaks_detected) == len(peak_locations) for loc in peaks_detected: From 187bc6d5b85766d041f6f18a0691e54709de315c Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sun, 15 Apr 2012 23:30:06 -0400 Subject: [PATCH 2/7] Deprecate threshold and replace absolute and relative thresholds. --- skimage/feature/peak.py | 22 +++++++++++++++++----- skimage/feature/tests/test_peak.py | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 225f42cd..3471381b 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -1,8 +1,10 @@ +import warnings import numpy as np from scipy import ndimage -def peak_local_max(image, min_distance=10, threshold=0.1): +def peak_local_max(image, min_distance=10, threshold='deprecated', + threshold_abs=0, threshold_rel=0.1): """Return coordinates of peaks in an image. Peaks are the local maxima in a region of `2 * min_distance + 1` @@ -13,11 +15,17 @@ def peak_local_max(image, min_distance=10, threshold=0.1): image: ndarray of floats Input image. - min_distance: int, optional + min_distance: int Minimum number of pixels separating peaks and image boundary. - threshold: float, optional - Candidate peaks are calculated as `max(image) * threshold`. + threshold : float + Deprecated. See `threshold_rel`. + + threshold_abs: float + Minimum intensity of peaks. + + threshold_rel: float + Minimum intensity of peaks calculated as `max(image) * threshold_rel`. Returns ------- @@ -37,8 +45,12 @@ def peak_local_max(image, min_distance=10, threshold=0.1): image[:, :min_distance] = 0 image[:, -min_distance:] = 0 + if not threshold == 'deprecated': + msg = "`threshold` parameter deprecated; use `threshold_rel instead." + warnings.warn(msg, DeprecationWarning) + threshold_rel = threshold # find top corner candidates above a threshold - corner_threshold = np.max(image.ravel()) * threshold + corner_threshold = max(np.max(image.ravel()) * threshold_rel, threshold_abs) image_t = (image >= corner_threshold) * 1 # get coordinates of peaks diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 48e8f67c..7f54b7cd 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -1,4 +1,5 @@ import numpy as np +from numpy.testing import assert_array_almost_equal as assert_close from skimage.feature import peak @@ -18,6 +19,23 @@ def test_noisy_peaks(): assert tuple(loc) in peak_locations +def test_relative_threshold(): + image = np.zeros((5, 5), dtype=np.uint8) + image[1, 1] = 10 + image[3, 3] = 21 + peaks = peak.peak_local_max(image, min_distance=1, threshold_rel=0.5) + assert len(peaks) == 1 + assert_close(peaks, [(3, 3)]) + + +def test_absolute_threshold(): + image = np.zeros((5, 5), dtype=np.uint8) + image[1, 1] = 10 + image[3, 3] = 21 + peaks = peak.peak_local_max(image, min_distance=1, threshold_abs=11) + assert len(peaks) == 1 + assert_close(peaks, [(3, 3)]) + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 2585a323ac57004179b0ba643f953162650f39ee Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sun, 15 Apr 2012 23:48:27 -0400 Subject: [PATCH 3/7] Change threshold test from >= to >. --- skimage/feature/peak.py | 2 +- skimage/feature/tests/test_peak.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 3471381b..30e1a84b 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -51,7 +51,7 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', threshold_rel = threshold # find top corner candidates above a threshold corner_threshold = max(np.max(image.ravel()) * threshold_rel, threshold_abs) - image_t = (image >= corner_threshold) * 1 + image_t = (image > corner_threshold) * 1 # get coordinates of peaks coordinates = np.transpose(image_t.nonzero()) diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 7f54b7cd..7f960e3b 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -22,7 +22,7 @@ def test_noisy_peaks(): def test_relative_threshold(): image = np.zeros((5, 5), dtype=np.uint8) image[1, 1] = 10 - image[3, 3] = 21 + image[3, 3] = 20 peaks = peak.peak_local_max(image, min_distance=1, threshold_rel=0.5) assert len(peaks) == 1 assert_close(peaks, [(3, 3)]) @@ -31,8 +31,8 @@ def test_relative_threshold(): def test_absolute_threshold(): image = np.zeros((5, 5), dtype=np.uint8) image[1, 1] = 10 - image[3, 3] = 21 - peaks = peak.peak_local_max(image, min_distance=1, threshold_abs=11) + image[3, 3] = 20 + peaks = peak.peak_local_max(image, min_distance=1, threshold_abs=10) assert len(peaks) == 1 assert_close(peaks, [(3, 3)]) From ae6ef22a69ba6f7a31e6638a22083a8a5f87b80f Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Sun, 15 Apr 2012 23:53:53 -0400 Subject: [PATCH 4/7] Fix: return empty list for flat image. --- skimage/feature/peak.py | 2 ++ skimage/feature/tests/test_peak.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 30e1a84b..ebacfa3a 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -32,6 +32,8 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', coordinates : (N, 2) array (row, column) coordinates of peaks. """ + if np.all(image == image.flat[0]): + return [] image = image.copy() # Non maximum filter size = 2 * min_distance + 1 diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 7f960e3b..7fb090ba 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -36,6 +36,13 @@ def test_absolute_threshold(): assert len(peaks) == 1 assert_close(peaks, [(3, 3)]) + +def test_constant_image(): + image = 128 * np.ones((20, 20), dtype=np.uint8) + peaks = peak.peak_local_max(image, min_distance=1) + assert len(peaks) == 0 + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 0b33dd5811b19c3013742135e5b5139e660328e6 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Mon, 16 Apr 2012 00:02:45 -0400 Subject: [PATCH 5/7] Add docstring comment about flat peaks. --- skimage/feature/peak.py | 3 +++ skimage/feature/tests/test_peak.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index ebacfa3a..fe7ed081 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -10,6 +10,9 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', Peaks are the local maxima in a region of `2 * min_distance + 1` (i.e. peaks are separated by at least `min_distance`). + NOTE: If peaks are flat (i.e. multiple pixels have exact same intensity), + the coordinates of all pixels are returned. + Parameters ---------- image: ndarray of floats diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 7fb090ba..907370cc 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -43,6 +43,13 @@ def test_constant_image(): assert len(peaks) == 0 +def test_flat_peak(): + image = np.zeros((5, 5), dtype=np.uint8) + image[1:3, 1:3] = 10 + peaks = peak.peak_local_max(image, min_distance=1) + assert len(peaks) == 4 + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 8bdc9f2ab48a6a7e4b668b167323c9011a519c01 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Mon, 16 Apr 2012 00:05:12 -0400 Subject: [PATCH 6/7] Rename for clarity "corner" is a relic from the original implementation in the harris filter. --- skimage/feature/peak.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index fe7ed081..c904648e 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -54,9 +54,9 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', msg = "`threshold` parameter deprecated; use `threshold_rel instead." warnings.warn(msg, DeprecationWarning) threshold_rel = threshold - # find top corner candidates above a threshold - corner_threshold = max(np.max(image.ravel()) * threshold_rel, threshold_abs) - image_t = (image > corner_threshold) * 1 + # find top peak candidates above a threshold + peak_threshold = max(np.max(image.ravel()) * threshold_rel, threshold_abs) + image_t = (image > peak_threshold) * 1 # get coordinates of peaks coordinates = np.transpose(image_t.nonzero()) From c3af26d37c16c92cac021971e9024d72354e7f48 Mon Sep 17 00:00:00 2001 From: Tony S Yu Date: Mon, 16 Apr 2012 00:19:56 -0400 Subject: [PATCH 7/7] ENH: Add `num_peaks` to limit the number of peaks. --- skimage/feature/peak.py | 11 ++++++++++- skimage/feature/tests/test_peak.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index c904648e..280263af 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -4,7 +4,7 @@ from scipy import ndimage def peak_local_max(image, min_distance=10, threshold='deprecated', - threshold_abs=0, threshold_rel=0.1): + threshold_abs=0, threshold_rel=0.1, num_peaks=np.inf): """Return coordinates of peaks in an image. Peaks are the local maxima in a region of `2 * min_distance + 1` @@ -30,6 +30,10 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', threshold_rel: float Minimum intensity of peaks calculated as `max(image) * threshold_rel`. + num_peaks : int + Maximum number of peaks. When the number of peaks exceeds `num_peaks`, + return `num_peaks` coordinates based on peak intensity. + Returns ------- coordinates : (N, 2) array @@ -61,5 +65,10 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', # get coordinates of peaks coordinates = np.transpose(image_t.nonzero()) + if len(coordinates) > num_peaks: + intensities = image[tuple(coordinates.T)] + idx_maxsort = np.argsort(intensities)[::-1] + coordinates = coordinates[idx_maxsort][:2] + return coordinates diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 907370cc..91d38d99 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -50,6 +50,18 @@ def test_flat_peak(): assert len(peaks) == 4 +def test_num_peaks(): + image = np.zeros((3, 7), dtype=np.uint8) + image[1, 1] = 10 + image[1, 3] = 11 + image[1, 5] = 12 + assert len(peak.peak_local_max(image, min_distance=1)) == 3 + peaks_limited = peak.peak_local_max(image, min_distance=1, num_peaks=2) + assert len(peaks_limited) == 2 + assert (1, 3) in peaks_limited + assert (1, 5) in peaks_limited + + if __name__ == '__main__': from numpy import testing testing.run_module_suite()