From f3771aba7e097f769bd5b538a8347c9d40c1b7e5 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 30 Nov 2014 14:19:51 -0800 Subject: [PATCH 01/14] Saner defaults for peak_local_max, untested. First attempt at fixing #1246. --- skimage/feature/peak.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 421abeec..26177837 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -3,9 +3,9 @@ import scipy.ndimage as ndi from ..filters import rank_order -def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, - exclude_border=True, indices=True, num_peaks=np.inf, - footprint=None, labels=None): +def peak_local_max(image, min_distance=10, threshold_abs=-np.inf, + threshold_rel=None, exclude_border=True, indices=True, + num_peaks=np.inf, footprint=None, labels=None): """ Find peaks in an image, and return them as coordinates or a boolean array. @@ -28,7 +28,8 @@ def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, threshold_abs : float Minimum intensity of peaks. threshold_rel : float - Minimum intensity of peaks calculated as `max(image) * threshold_rel`. + Minimum intensity of peaks calculated as `max(image) * threshold_rel`; + not used if set to None (the default). exclude_border : bool If True, `min_distance` excludes peaks from the border of the image as well as from each other. @@ -42,7 +43,7 @@ def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, footprint : ndarray of bools, optional If provided, `footprint == 1` represents the local region within which to search for peaks at every point in `image`. Overrides - `min_distance`, except for border exclusion if `exclude_border=True`. + `min_distance` (also for `exclude_border`). labels : ndarray of ints, optional If provided, each unique region `labels == value` represents a unique region to search for peaks. Zero is reserved for background. @@ -138,12 +139,16 @@ def peak_local_max(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, # zero out the image borders for i in range(image.ndim): image = image.swapaxes(0, i) - image[:min_distance] = 0 - image[-min_distance:] = 0 + remove = (footprint.shape[i] if footprint is not None + else 2 * min_distance) + image[:remove // 2] = 0 + image[-remove // 2:] = 0 image = image.swapaxes(0, i) # find top peak candidates above a threshold - peak_threshold = max(np.max(image.ravel()) * threshold_rel, threshold_abs) + peak_threshold = threshold_abs + if threshold_rel is not None: + peak_threshold = max(peak_threshold, image.max()) # get coordinates of peaks coordinates = np.argwhere(image > peak_threshold) From 58283e7bd5fbb1e32a03834721d98257cdd4cb7a Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 30 Nov 2014 23:09:53 -0800 Subject: [PATCH 02/14] Change default min_distance; improve docstring. --- skimage/feature/peak.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 26177837..fd2c7047 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -3,7 +3,7 @@ import scipy.ndimage as ndi from ..filters import rank_order -def peak_local_max(image, min_distance=10, threshold_abs=-np.inf, +def peak_local_max(image, min_distance=1, threshold_abs=-np.inf, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): """ @@ -19,25 +19,25 @@ def peak_local_max(image, min_distance=10, threshold_abs=-np.inf, ---------- image : ndarray of floats Input image. - min_distance : int + min_distance : int, optional Minimum number of pixels separating peaks in a region of `2 * min_distance + 1` (i.e. peaks are separated by at least `min_distance`). If `exclude_border` is True, this value also excludes a border `min_distance` from the image boundary. To find the maximum number of peaks, use `min_distance=1`. - threshold_abs : float + threshold_abs : float, optional Minimum intensity of peaks. - threshold_rel : float + threshold_rel : float, optional Minimum intensity of peaks calculated as `max(image) * threshold_rel`; not used if set to None (the default). - exclude_border : bool + exclude_border : bool, optional If True, `min_distance` excludes peaks from the border of the image as well as from each other. - indices : bool - If True, the output will be an array representing peak coordinates. - If False, the output will be a boolean array shaped as `image.shape` - with peaks present at True elements. - num_peaks : int + indices : bool, optional + If True (the default), the output will be an array representing peak + coordinates. If False, the output will be a boolean array shaped as + `image.shape` with peaks present at True elements. + num_peaks : int, optional Maximum number of peaks. When the number of peaks exceeds `num_peaks`, return `num_peaks` peaks based on highest peak intensity. footprint : ndarray of bools, optional From bd2ecff62f444fce081a3b9ceb54469d8024d34b Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 21 Jan 2015 17:14:05 -0800 Subject: [PATCH 03/14] WIP: fix impl. of peak_local_max, update tests. --- skimage/feature/corner.py | 2 +- skimage/feature/peak.py | 24 ++++----- skimage/feature/tests/test_corner.py | 77 ++++++++++++++++++---------- skimage/feature/tests/test_peak.py | 33 +++++++----- 4 files changed, 84 insertions(+), 52 deletions(-) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index 6081e1a2..e8d31abe 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -799,7 +799,7 @@ def corner_subpix(image, corners, window_size=11, alpha=0.99): return corners_subpix -def corner_peaks(image, min_distance=10, threshold_abs=0, threshold_rel=0.1, +def corner_peaks(image, min_distance=1, threshold_abs=None, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): """Find corners in corner measure response image. diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index fd2c7047..8bac92bf 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -3,7 +3,7 @@ import scipy.ndimage as ndi from ..filters import rank_order -def peak_local_max(image, min_distance=1, threshold_abs=-np.inf, +def peak_local_max(image, min_distance=1, threshold_abs=None, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): """ @@ -28,8 +28,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=-np.inf, threshold_abs : float, optional Minimum intensity of peaks. threshold_rel : float, optional - Minimum intensity of peaks calculated as `max(image) * threshold_rel`; - not used if set to None (the default). + Minimum intensity of peaks, calculated as `max(image) * threshold_rel`. exclude_border : bool, optional If True, `min_distance` excludes peaks from the border of the image as well as from each other. @@ -124,7 +123,6 @@ def peak_local_max(image, min_distance=1, threshold_abs=-np.inf, else: return out - image = image.copy() # Non maximum filter if footprint is not None: image_max = ndi.maximum_filter(image, footprint=footprint, @@ -133,22 +131,24 @@ def peak_local_max(image, min_distance=1, threshold_abs=-np.inf, size = 2 * min_distance + 1 image_max = ndi.maximum_filter(image, size=size, mode='constant') mask = (image == image_max) - image *= mask if exclude_border: # zero out the image borders - for i in range(image.ndim): - image = image.swapaxes(0, i) + for i in range(mask.ndim): + mask = mask.swapaxes(0, i) remove = (footprint.shape[i] if footprint is not None else 2 * min_distance) - image[:remove // 2] = 0 - image[-remove // 2:] = 0 - image = image.swapaxes(0, i) + mask[:remove // 2] = mask[-remove // 2:] = False + mask = mask.swapaxes(0, i) # find top peak candidates above a threshold - peak_threshold = threshold_abs + thresholds = [] + if threshold_abs is not None: + thresholds.append(threshold_abs) if threshold_rel is not None: - peak_threshold = max(peak_threshold, image.max()) + thresholds.append(threshold_rel * image.max()) + if thresholds: + mask &= image > max(thresholds) # get coordinates of peaks coordinates = np.argwhere(image > peak_threshold) diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index 953e47e3..e8db0b46 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -107,21 +107,25 @@ def test_square_image(): im[:25, :25] = 1. # Moravec - results = peak_local_max(corner_moravec(im)) + results = peak_local_max(corner_moravec(im), + min_distance=10, threshold_rel=0) # interest points along edge assert len(results) == 57 # Harris - results = peak_local_max(corner_harris(im, method='k')) + results = peak_local_max(corner_harris(im, method='k'), + min_distance=10, threshold_rel=0) # interest at corner assert len(results) == 1 - results = peak_local_max(corner_harris(im, method='eps')) + results = peak_local_max(corner_harris(im, method='eps'), + min_distance=10, threshold_rel=0) # interest at corner assert len(results) == 1 # Shi-Tomasi - results = peak_local_max(corner_shi_tomasi(im)) + results = peak_local_max(corner_shi_tomasi(im), + min_distance=10, threshold_rel=0) # interest at corner assert len(results) == 1 @@ -133,18 +137,22 @@ def test_noisy_square_image(): im = im + np.random.uniform(size=im.shape) * .2 # Moravec - results = peak_local_max(corner_moravec(im)) + results = peak_local_max(corner_moravec(im), + min_distance=10, threshold_rel=0) # undefined number of interest points assert results.any() # Harris - results = peak_local_max(corner_harris(im, sigma=1.5, method='k')) + results = peak_local_max(corner_harris(im, method='k'), + min_distance=10, threshold_rel=0) assert len(results) == 1 - results = peak_local_max(corner_harris(im, sigma=1.5, method='eps')) + results = peak_local_max(corner_harris(im, method='eps'), + min_distance=10, threshold_rel=0) assert len(results) == 1 # Shi-Tomasi - results = peak_local_max(corner_shi_tomasi(im, sigma=1.5)) + results = peak_local_max(corner_shi_tomasi(im, sigma=1.5), + min_distance=10, threshold_rel=0) assert len(results) == 1 @@ -156,11 +164,13 @@ def test_squared_dot(): # Moravec fails # Harris - results = peak_local_max(corner_harris(im)) + results = peak_local_max(corner_harris(im), + min_distance=10, threshold_rel=0) assert (results == np.array([[6, 6]])).all() # Shi-Tomasi - results = peak_local_max(corner_shi_tomasi(im)) + results = peak_local_max(corner_shi_tomasi(im), + min_distance=10, threshold_rel=0) assert (results == np.array([[6, 6]])).all() @@ -173,20 +183,26 @@ def test_rotated_img(): im_rotated = im.T # Moravec - results = peak_local_max(corner_moravec(im)) - results_rotated = peak_local_max(corner_moravec(im_rotated)) + results = peak_local_max(corner_moravec(im), + min_distance=10, threshold_rel=0) + results_rotated = peak_local_max(corner_moravec(im_rotated), + min_distance=10, threshold_rel=0) assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all() assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all() # Harris - results = peak_local_max(corner_harris(im)) - results_rotated = peak_local_max(corner_harris(im_rotated)) + results = peak_local_max(corner_harris(im), + min_distance=10, threshold_rel=0) + results_rotated = peak_local_max(corner_harris(im_rotated), + min_distance=10, threshold_rel=0) assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all() assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all() # Shi-Tomasi - results = peak_local_max(corner_shi_tomasi(im)) - results_rotated = peak_local_max(corner_shi_tomasi(im_rotated)) + results = peak_local_max(corner_shi_tomasi(im), + min_distance=10, threshold_rel=0) + results_rotated = peak_local_max(corner_shi_tomasi(im_rotated), + min_distance=10, threshold_rel=0) assert (np.sort(results[:, 0]) == np.sort(results_rotated[:, 1])).all() assert (np.sort(results[:, 1]) == np.sort(results_rotated[:, 0])).all() @@ -195,7 +211,8 @@ def test_subpix_edge(): img = np.zeros((50, 50)) img[:25, :25] = 255 img[25:, 25:] = 255 - corner = peak_local_max(corner_harris(img), num_peaks=1) + corner = peak_local_max(corner_harris(img), + min_distance=10, threshold_rel=0, num_peaks=1) subpix = corner_subpix(img, corner) assert_array_equal(subpix[0], (24.5, 24.5)) @@ -203,7 +220,8 @@ def test_subpix_edge(): def test_subpix_dot(): img = np.zeros((50, 50)) img[25, 25] = 255 - corner = peak_local_max(corner_harris(img), num_peaks=1) + corner = peak_local_max(corner_harris(img), + min_distance=10, threshold_rel=0, num_peaks=1) subpix = corner_subpix(img, corner) assert_array_equal(subpix[0], (25, 25)) @@ -214,7 +232,8 @@ def test_subpix_no_class(): assert_array_equal(subpix[0], (np.nan, np.nan)) img[25, 25] = 1e-10 - corner = peak_local_max(corner_harris(img), num_peaks=1) + corner = peak_local_max(corner_harris(img), + min_distance=10, threshold_rel=0, num_peaks=1) subpix = corner_subpix(img, np.array([[25, 25]])) assert_array_equal(subpix[0], (np.nan, np.nan)) @@ -223,7 +242,7 @@ def test_subpix_border(): img = np.zeros((50, 50)) img[1:25,1:25] = 255 img[25:-1,25:-1] = 255 - corner = corner_peaks(corner_harris(img), min_distance=1) + corner = corner_peaks(corner_harris(img), threshold_rel=0) subpix = corner_subpix(img, corner, window_size=11) ref = np.array([[ 0.52040816, 0.52040816], [ 0.52040816, 24.47959184], @@ -244,7 +263,8 @@ def test_num_peaks(): for i in range(20): n = np.random.random_integers(20) - results = peak_local_max(img_corners, num_peaks=n) + results = peak_local_max(img_corners, + min_distance=10, threshold_rel=0, num_peaks=n) assert (results.shape[0] == n) @@ -252,14 +272,16 @@ def test_corner_peaks(): response = np.zeros((5, 5)) response[2:4, 2:4] = 1 - corners = corner_peaks(response, exclude_border=False) + corners = corner_peaks(response, exclude_border=False, min_distance=10, + threshold_rel=0) assert len(corners) == 1 - corners = corner_peaks(response, exclude_border=False, min_distance=0) + corners = corner_peaks(response, exclude_border=False, min_distance=0, + threshold_rel=0) assert len(corners) == 4 corners = corner_peaks(response, exclude_border=False, min_distance=0, - indices=False) + threshold_rel=0, indices=False) assert np.sum(corners) == 4 @@ -323,7 +345,8 @@ def test_corner_fast_lena(): [492, 139], [494, 169], [496, 266]]) - actual = corner_peaks(corner_fast(img, 12, 0.3)) + actual = corner_peaks(corner_fast(img, 12, 0.3), + min_distance=10, threshold_rel=0) assert_array_equal(actual, expected) @@ -355,7 +378,6 @@ def test_corner_orientations_astronaut(): -4.40598471e-01, 3.14918803e-01, -1.76069982e+00, 3.05330950e+00, 2.39291733e+00, -1.22091334e-01, -3.09279990e-01, 1.45931342e+00]) - actual = corner_orientations(img, corners, octagon(3, 2)) assert_almost_equal(actual, expected) @@ -363,7 +385,8 @@ def test_corner_orientations_astronaut(): def test_corner_orientations_square(): square = np.zeros((12, 12)) square[3:9, 3:9] = 1 - corners = corner_peaks(corner_fast(square, 9), min_distance=1) + corners = corner_peaks(corner_fast(square, 9), + min_distance=1, threshold_rel=0) actual_orientations = corner_orientations(square, corners, octagon(3, 2)) actual_orientations_degrees = np.rad2deg(actual_orientations) expected_orientations_degree = np.array([ 45., 135., -45., -135.]) diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index e6256208..43a87369 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -70,12 +70,14 @@ def test_num_peaks(): image[1, 5] = 12 image[3, 5] = 8 image[5, 3] = 7 - assert len(peak.peak_local_max(image, min_distance=1)) == 5 - peaks_limited = peak.peak_local_max(image, min_distance=1, num_peaks=2) + assert len(peak.peak_local_max(image, min_distance=1, threshold_abs=0)) == 5 + peaks_limited = peak.peak_local_max( + image, min_distance=1, threshold_abs=0, num_peaks=2) assert len(peaks_limited) == 2 assert (1, 3) in peaks_limited assert (1, 5) in peaks_limited - peaks_limited = peak.peak_local_max(image, min_distance=1, num_peaks=4) + peaks_limited = peak.peak_local_max( + image, min_distance=1, threshold_abs=0, num_peaks=4) assert len(peaks_limited) == 4 assert (1, 3) in peaks_limited assert (1, 5) in peaks_limited @@ -272,7 +274,8 @@ def test_disk(): min_distance=1, threshold_rel=0, indices=False, exclude_border=False) assert np.all(result) - result = peak.peak_local_max(image, footprint=footprint) + result = peak.peak_local_max(image, footprint=footprint, indices=False, + exclude_border=False) assert np.all(result) @@ -280,11 +283,14 @@ def test_3D(): image = np.zeros((30, 30, 30)) image[15, 15, 15] = 1 image[5, 5, 5] = 1 - assert_equal(peak.peak_local_max(image), [[15, 15, 15]]) - assert_equal(peak.peak_local_max(image, min_distance=6), [[15, 15, 15]]) - assert_equal(peak.peak_local_max(image, exclude_border=False), + assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0), + [[15, 15, 15]]) + assert_equal(peak.peak_local_max(image, min_distance=6, threshold_rel=0), + [[15, 15, 15]]) + assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0, + exclude_border=False), [[5, 5, 5], [15, 15, 15]]) - assert_equal(peak.peak_local_max(image, min_distance=5), + assert_equal(peak.peak_local_max(image, min_distance=5, threshold_rel=0), [[5, 5, 5], [15, 15, 15]]) @@ -292,11 +298,14 @@ def test_4D(): image = np.zeros((30, 30, 30, 30)) image[15, 15, 15, 15] = 1 image[5, 5, 5, 5] = 1 - assert_equal(peak.peak_local_max(image), [[15, 15, 15, 15]]) - assert_equal(peak.peak_local_max(image, min_distance=6), [[15, 15, 15, 15]]) - assert_equal(peak.peak_local_max(image, exclude_border=False), + assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0), + [[15, 15, 15, 15]]) + assert_equal(peak.peak_local_max(image, min_distance=6, threshold_rel=0), + [[15, 15, 15, 15]]) + assert_equal(peak.peak_local_max(image, min_distance=10, threshold_rel=0, + exclude_border=False), [[5, 5, 5, 5], [15, 15, 15, 15]]) - assert_equal(peak.peak_local_max(image, min_distance=5), + assert_equal(peak.peak_local_max(image, min_distance=5, threshold_rel=0), [[5, 5, 5, 5], [15, 15, 15, 15]]) From fe9d7c73a1f8a9a46e83b9e6fabdb812d6ab6388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 10:27:44 +0100 Subject: [PATCH 04/14] Fix coordinate extraction --- skimage/feature/peak.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 8bac92bf..dd4c824c 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -151,10 +151,11 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, mask &= image > max(thresholds) # get coordinates of peaks - coordinates = np.argwhere(image > peak_threshold) + coordinates = np.transpose(mask.nonzero()) if coordinates.shape[0] > num_peaks: - intensities = image.flat[np.ravel_multi_index(coordinates.transpose(),image.shape)] + intensities = image.flat[np.ravel_multi_index(coordinates.transpose(), + image.shape)] idx_maxsort = np.argsort(intensities)[::-1] coordinates = coordinates[idx_maxsort][:num_peaks] From 5751b0cb28f1300468b06344e5a3cf231b3d0f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 10:37:29 +0100 Subject: [PATCH 05/14] Some improvements to the doc string and code formatting --- skimage/feature/peak.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index dd4c824c..4366923f 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -6,15 +6,17 @@ from ..filters import rank_order def peak_local_max(image, min_distance=1, threshold_abs=None, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): - """ - Find peaks in an image, and return them as coordinates or a boolean array. + """Find peaks in an image as coordinate list or boolean mask. 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 adjacent pixels have identical + If peaks are flat (i.e. multiple adjacent pixels have identical intensities), the coordinates of all such pixels are returned. + If both `threshold_abs` and `threshold_rel` are provided, the maximum + of the two is chosen as the minimum intensity threshold of peaks. + Parameters ---------- image : ndarray of floats @@ -33,7 +35,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, If True, `min_distance` excludes peaks from the border of the image as well as from each other. indices : bool, optional - If True (the default), the output will be an array representing peak + If True, the output will be an array representing peak coordinates. If False, the output will be a boolean array shaped as `image.shape` with peaks present at True elements. num_peaks : int, optional @@ -58,10 +60,10 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, Notes ----- The peak local maximum function returns the coordinates of local peaks - (maxima) in a image. A maximum filter is used for finding local maxima. - This operation dilates the original image. After comparison between - dilated and original image, peak_local_max function returns the - coordinates of peaks where dilated image = original. + (maxima) in an image. A maximum filter is used for finding local maxima. + This operation dilates the original image. After comparison of the dilated + and original image, this function returns the coordinates or a mask of the + peaks where the dilated image equals the original image. Examples -------- @@ -90,7 +92,9 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, array([[10, 10, 10]]) """ + out = np.zeros_like(image, dtype=np.bool) + # In the case of labels, recursively build and return an output # operating on each label separately if labels is not None: @@ -130,7 +134,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, else: size = 2 * min_distance + 1 image_max = ndi.maximum_filter(image, size=size, mode='constant') - mask = (image == image_max) + mask = image == image_max if exclude_border: # zero out the image borders From 3f912903cb5cfa7f031ed044723cc9ae8bef4a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 10:45:13 +0100 Subject: [PATCH 06/14] Fix corner orientations unit test --- skimage/feature/tests/test_corner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index e8db0b46..27f67b10 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -365,7 +365,8 @@ def test_corner_orientations_even_shape_error(): @test_parallel() def test_corner_orientations_astronaut(): img = rgb2gray(data.astronaut()) - corners = corner_peaks(corner_fast(img, 11, 0.35)) + corners = corner_peaks(corner_fast(img, 11, 0.35), + min_distance=10, threshold_abs=0, threshold_rel=0.1) expected = np.array([-1.75220190e+00, 2.01197383e+00, -2.01162417e+00, -1.88247204e-01, 1.19134149e+00, -6.61151410e-01, -2.99143370e+00, 2.17103132e+00, -7.52950306e-04, From 88ee3ce96e5d7db62d700c1d81615fa15a0ba282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 10:55:14 +0100 Subject: [PATCH 07/14] Raise error for invalid min_distance values --- skimage/feature/peak.py | 5 ++++- skimage/feature/tests/test_peak.py | 9 ++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 4366923f..8aaf9119 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -19,7 +19,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, Parameters ---------- - image : ndarray of floats + image : ndarray Input image. min_distance : int, optional Minimum number of pixels separating peaks in a region of `2 * @@ -93,6 +93,9 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, """ + if min_distance < 1: + raise ValueError("`min_disance` must greater than 0") + out = np.zeros_like(image, dtype=np.bool) # In the case of labels, recursively build and return an output diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 43a87369..e6a08eef 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -1,6 +1,6 @@ import numpy as np from numpy.testing import (assert_array_almost_equal as assert_close, - assert_equal) + assert_equal, assert_raises) from scipy import ndimage as ndi from skimage.feature import peak @@ -309,6 +309,13 @@ def test_4D(): [[5, 5, 5, 5], [15, 15, 15, 15]]) +def test_invalid_min_distance(): + assert_raises(ValueError, peak.peak_local_max, np.zeros((10, 10)), + min_distance=0) + assert_raises(ValueError, peak.peak_local_max, np.zeros((10, 10)), + min_distance=-1) + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 024c4328f20a5d4e98779259f9bc67126311e95d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 10:56:13 +0100 Subject: [PATCH 08/14] Fix corner_peaks doctest --- skimage/feature/corner.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index e8d31abe..08d4cfd7 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -823,18 +823,13 @@ def corner_peaks(image, min_distance=1, threshold_abs=None, threshold_rel=None, [ 0., 0., 1., 1., 0.], [ 0., 0., 1., 1., 0.], [ 0., 0., 0., 0., 0.]]) - >>> peak_local_max(response, exclude_border=False) + >>> peak_local_max(response) array([[2, 2], [2, 3], [3, 2], [3, 3]]) - >>> corner_peaks(response, exclude_border=False) + >>> corner_peaks(response) array([[2, 2]]) - >>> corner_peaks(response, exclude_border=False, min_distance=0) - array([[2, 2], - [2, 3], - [3, 2], - [3, 3]]) """ From e74da89ede6f1e4d6100bc9885b312d00dd5ccad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 11:01:41 +0100 Subject: [PATCH 09/14] Fix corner_fast doctest --- skimage/feature/corner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/corner.py b/skimage/feature/corner.py index 08d4cfd7..fef9b046 100644 --- a/skimage/feature/corner.py +++ b/skimage/feature/corner.py @@ -610,7 +610,7 @@ def corner_fast(image, n=12, threshold=0.15): [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) - >>> corner_peaks(corner_fast(square, 9), min_distance=1) + >>> corner_peaks(corner_fast(square, 9), min_distance=1, threshold_rel=0.1) array([[3, 3], [3, 8], [8, 3], From bbb427b9eab3e966c70c7142c1048c9fa9df6eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 11:03:04 +0100 Subject: [PATCH 10/14] Fix peak_local_max doctest --- skimage/feature/peak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 8aaf9119..76f27a76 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -3,7 +3,7 @@ import scipy.ndimage as ndi from ..filters import rank_order -def peak_local_max(image, min_distance=1, threshold_abs=None, +def peak_local_max(image, min_distance=1, threshold_abs=0, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): """Find peaks in an image as coordinate list or boolean mask. From dfae40f9a815cfc3b842de3f7c5206d96f6b17c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 18:29:27 +0100 Subject: [PATCH 11/14] Change default threshold_abs value to minimum image intensity --- skimage/feature/peak.py | 13 ++++++------- skimage/feature/tests/test_corner.py | 11 +++++------ skimage/feature/tests/test_peak.py | 14 ++++---------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 76f27a76..d3e3f4f5 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -3,7 +3,7 @@ import scipy.ndimage as ndi from ..filters import rank_order -def peak_local_max(image, min_distance=1, threshold_abs=0, +def peak_local_max(image, min_distance=1, threshold_abs=None, threshold_rel=None, exclude_border=True, indices=True, num_peaks=np.inf, footprint=None, labels=None): """Find peaks in an image as coordinate list or boolean mask. @@ -28,7 +28,8 @@ def peak_local_max(image, min_distance=1, threshold_abs=0, a border `min_distance` from the image boundary. To find the maximum number of peaks, use `min_distance=1`. threshold_abs : float, optional - Minimum intensity of peaks. + Minimum intensity of peaks. By default, the absolute threshold is + the minimum intensity of the image. threshold_rel : float, optional Minimum intensity of peaks, calculated as `max(image) * threshold_rel`. exclude_border : bool, optional @@ -93,9 +94,6 @@ def peak_local_max(image, min_distance=1, threshold_abs=0, """ - if min_distance < 1: - raise ValueError("`min_disance` must greater than 0") - out = np.zeros_like(image, dtype=np.bool) # In the case of labels, recursively build and return an output @@ -150,8 +148,9 @@ def peak_local_max(image, min_distance=1, threshold_abs=0, # find top peak candidates above a threshold thresholds = [] - if threshold_abs is not None: - thresholds.append(threshold_abs) + if threshold_abs is None: + threshold_abs = image.min() + thresholds.append(threshold_abs) if threshold_rel is not None: thresholds.append(threshold_rel * image.max()) if thresholds: diff --git a/skimage/feature/tests/test_corner.py b/skimage/feature/tests/test_corner.py index 27f67b10..c3919fe8 100644 --- a/skimage/feature/tests/test_corner.py +++ b/skimage/feature/tests/test_corner.py @@ -269,19 +269,18 @@ def test_num_peaks(): def test_corner_peaks(): - response = np.zeros((5, 5)) - response[2:4, 2:4] = 1 + response = np.zeros((10, 10)) + response[2:5, 2:5] = 1 corners = corner_peaks(response, exclude_border=False, min_distance=10, threshold_rel=0) assert len(corners) == 1 - corners = corner_peaks(response, exclude_border=False, min_distance=0, - threshold_rel=0) + corners = corner_peaks(response, exclude_border=False, min_distance=1) assert len(corners) == 4 - corners = corner_peaks(response, exclude_border=False, min_distance=0, - threshold_rel=0, indices=False) + corners = corner_peaks(response, exclude_border=False, min_distance=1, + indices=False) assert np.sum(corners) == 4 diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index e6a08eef..8161ca39 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -272,11 +272,12 @@ def test_disk(): result = peak.peak_local_max(image, labels=np.ones((10, 20)), footprint=footprint, min_distance=1, threshold_rel=0, - indices=False, exclude_border=False) - assert np.all(result) - result = peak.peak_local_max(image, footprint=footprint, indices=False, + threshold_abs=-1, indices=False, exclude_border=False) assert np.all(result) + result = peak.peak_local_max(image, footprint=footprint, threshold_abs=-1, + indices=False, exclude_border=False) + assert np.all(result) def test_3D(): @@ -309,13 +310,6 @@ def test_4D(): [[5, 5, 5, 5], [15, 15, 15, 15]]) -def test_invalid_min_distance(): - assert_raises(ValueError, peak.peak_local_max, np.zeros((10, 10)), - min_distance=0) - assert_raises(ValueError, peak.peak_local_max, np.zeros((10, 10)), - min_distance=-1) - - if __name__ == '__main__': from numpy import testing testing.run_module_suite() From e552002e72285aa1ec44668ddb3647ad5bd9b97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 18:31:22 +0100 Subject: [PATCH 12/14] Fix BRIEF test cases --- skimage/feature/tests/test_brief.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/skimage/feature/tests/test_brief.py b/skimage/feature/tests/test_brief.py index 610dc984..7f6ae9b7 100644 --- a/skimage/feature/tests/test_brief.py +++ b/skimage/feature/tests/test_brief.py @@ -18,7 +18,8 @@ def test_normal_mode(): """Verify the computed BRIEF descriptors with expected for normal mode.""" img = data.coins() - keypoints = corner_peaks(corner_harris(img), min_distance=5) + keypoints = corner_peaks(corner_harris(img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor = BRIEF(descriptor_size=8, sigma=2) @@ -40,7 +41,8 @@ def test_uniform_mode(): """Verify the computed BRIEF descriptors with expected for uniform mode.""" img = data.coins() - keypoints = corner_peaks(corner_harris(img), min_distance=5) + keypoints = corner_peaks(corner_harris(img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor = BRIEF(descriptor_size=8, sigma=2, mode='uniform') From 91ed3780194b200b9008f158d1f1c8aaceab7fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Sun, 24 Jan 2016 18:41:11 +0100 Subject: [PATCH 13/14] Fix bug in exclude_border option --- skimage/feature/peak.py | 2 +- skimage/feature/tests/test_peak.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index d3e3f4f5..0cc1b26d 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -137,7 +137,7 @@ def peak_local_max(image, min_distance=1, threshold_abs=None, image_max = ndi.maximum_filter(image, size=size, mode='constant') mask = image == image_max - if exclude_border: + if exclude_border and (footprint is not None or min_distance > 0): # zero out the image borders for i in range(mask.ndim): mask = mask.swapaxes(0, i) diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 8161ca39..40292cd9 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -310,6 +310,19 @@ def test_4D(): [[5, 5, 5, 5], [15, 15, 15, 15]]) +def test_threshold_rel_default(): + image = np.ones((5, 5)) + + image[2, 2] = 1 + assert len(peak.peak_local_max(image)) == 0 + + image[2, 2] = 2 + assert_equal(peak.peak_local_max(image), [[2, 2]]) + + image[2, 2] = 0 + assert len(peak.peak_local_max(image, min_distance=0)) == image.size - 1 + + if __name__ == '__main__': from numpy import testing testing.run_module_suite() From 433523805ff04df6603d560e69d0d4ba9ab60847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 29 Jan 2016 09:23:36 +0100 Subject: [PATCH 14/14] Fix matching test cases --- skimage/feature/tests/test_match.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/skimage/feature/tests/test_match.py b/skimage/feature/tests/test_match.py index 31d20d36..770dc37b 100644 --- a/skimage/feature/tests/test_match.py +++ b/skimage/feature/tests/test_match.py @@ -36,11 +36,13 @@ def test_binary_descriptors_lena_rotation_crosscheck_false(): extractor = BRIEF(descriptor_size=512) - keypoints1 = corner_peaks(corner_harris(img), min_distance=5) + keypoints1 = corner_peaks(corner_harris(img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor.extract(img, keypoints1) descriptors1 = extractor.descriptors - keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5) + keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor.extract(rotated_img, keypoints2) descriptors2 = extractor.descriptors @@ -69,11 +71,13 @@ def test_binary_descriptors_lena_rotation_crosscheck_true(): extractor = BRIEF(descriptor_size=512) - keypoints1 = corner_peaks(corner_harris(img), min_distance=5) + keypoints1 = corner_peaks(corner_harris(img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor.extract(img, keypoints1) descriptors1 = extractor.descriptors - keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5) + keypoints2 = corner_peaks(corner_harris(rotated_img), min_distance=5, + threshold_abs=0, threshold_rel=0.1) extractor.extract(rotated_img, keypoints2) descriptors2 = extractor.descriptors