diff --git a/skimage/filter/edges.py b/skimage/filter/edges.py index ff3b6b03..55799c0a 100644 --- a/skimage/filter/edges.py +++ b/skimage/filter/edges.py @@ -82,7 +82,9 @@ def sobel(image, mask=None): has to be further processed to perform edge detection. """ assert_nD(image, 2) - return np.sqrt(hsobel(image, mask)**2 + vsobel(image, mask)**2) + out = np.sqrt(hsobel(image, mask)**2 + vsobel(image, mask)**2) + out /= np.sqrt(2) + return out def hsobel(image, mask=None): @@ -176,11 +178,13 @@ def scharr(image, mask=None): References ---------- - .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical Optimization - of Kernel Based Image Derivatives. + .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical + Optimization of Kernel Based Image Derivatives. """ - return np.sqrt(hscharr(image, mask)**2 + vscharr(image, mask)**2) + out = np.sqrt(hscharr(image, mask)**2 + vscharr(image, mask)**2) + out /= np.sqrt(2) + return out def hscharr(image, mask=None): @@ -211,8 +215,8 @@ def hscharr(image, mask=None): References ---------- - .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical Optimization - of Kernel Based Image Derivatives. + .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical + Optimization of Kernel Based Image Derivatives. """ assert_nD(image, 2) @@ -249,8 +253,8 @@ def vscharr(image, mask=None): References ---------- - .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical Optimization - of Kernel Based Image Derivatives. + .. [1] D. Kroon, 2009, Short Paper University Twente, Numerical + Optimization of Kernel Based Image Derivatives. """ assert_nD(image, 2) @@ -282,7 +286,9 @@ def prewitt(image, mask=None): and vertical Prewitt transforms. """ assert_nD(image, 2) - return np.sqrt(hprewitt(image, mask)**2 + vprewitt(image, mask)**2) + out = np.sqrt(hprewitt(image, mask)**2 + vprewitt(image, mask)**2) + out /= np.sqrt(2) + return out def hprewitt(image, mask=None): @@ -369,8 +375,10 @@ def roberts(image, mask=None): The Roberts' Cross edge map. """ assert_nD(image, 2) - return np.sqrt(roberts_positive_diagonal(image, mask)**2 + - roberts_negative_diagonal(image, mask)**2) + out = np.sqrt(roberts_positive_diagonal(image, mask)**2 + + roberts_negative_diagonal(image, mask)**2) + out /= np.sqrt(2) + return out def roberts_positive_diagonal(image, mask=None): diff --git a/skimage/filter/tests/test_edges.py b/skimage/filter/tests/test_edges.py index 523d6dd8..ac363e52 100644 --- a/skimage/filter/tests/test_edges.py +++ b/skimage/filter/tests/test_edges.py @@ -1,5 +1,6 @@ import numpy as np -from numpy.testing import assert_array_almost_equal as assert_close +from numpy.testing import (assert_array_almost_equal as assert_close, + assert_, assert_allclose) import skimage.filter as F from skimage.filter.edges import _mask_filter_result @@ -49,10 +50,10 @@ def test_sobel_horizontal(): """Sobel on a horizontal edge should be a horizontal line.""" i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) - result = F.sobel(image) + result = F.sobel(image) * np.sqrt(2) # Fudge the eroded points i[np.abs(j) == 5] = 10000 - assert (np.all(result[i == 0] == 1)) + assert_allclose(result[i == 0], 1) assert (np.all(result[np.abs(i) > 1] == 0)) @@ -60,7 +61,7 @@ def test_sobel_vertical(): """Sobel on a vertical edge should be a vertical line.""" i, j = np.mgrid[-5:6, -5:6] image = (j >= 0).astype(float) - result = F.sobel(image) + result = F.sobel(image) * np.sqrt(2) j[np.abs(i) == 5] = 10000 assert (np.all(result[j == 0] == 1)) assert (np.all(result[np.abs(j) > 1] == 0)) @@ -94,15 +95,15 @@ def test_hsobel_horizontal(): def test_hsobel_vertical(): """Horizontal Sobel on a vertical edge should be zero.""" i, j = np.mgrid[-5:6, -5:6] - image = (j >= 0).astype(float) + image = (j >= 0).astype(float) * np.sqrt(2) result = F.hsobel(image) - assert (np.all(result == 0)) + assert_allclose(result, 0, atol=1e-10) def test_vsobel_zeros(): """Vertical sobel on an array of all zeros.""" result = F.vsobel(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vsobel_mask(): @@ -110,7 +111,7 @@ def test_vsobel_mask(): np.random.seed(0) result = F.vsobel(np.random.uniform(size=(10, 10)), np.zeros((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vsobel_vertical(): @@ -129,32 +130,31 @@ def test_vsobel_horizontal(): i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) result = F.vsobel(image) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(result, 0) def test_scharr_zeros(): """Scharr on an array of all zeros.""" result = F.scharr(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert (np.all(result < 1e-16)) def test_scharr_mask(): """Scharr on a masked array should be zero.""" np.random.seed(0) result = F.scharr(np.random.uniform(size=(10, 10)), - np.zeros((10, 10), bool)) - assert (np.all(result == 0)) + np.zeros((10, 10), bool)) + assert_allclose(result, 0) def test_scharr_horizontal(): """Scharr on an edge should be a horizontal line.""" i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) - result = F.scharr(image) + result = F.scharr(image) * np.sqrt(2) # Fudge the eroded points i[np.abs(j) == 5] = 10000 - assert (np.all(result[i == 0] == 1)) + assert_allclose(result[i == 0], 1) assert (np.all(result[np.abs(i) > 1] == 0)) @@ -162,24 +162,24 @@ def test_scharr_vertical(): """Scharr on a vertical edge should be a vertical line.""" i, j = np.mgrid[-5:6, -5:6] image = (j >= 0).astype(float) - result = F.scharr(image) + result = F.scharr(image) * np.sqrt(2) j[np.abs(i) == 5] = 10000 - assert (np.all(result[j == 0] == 1)) + assert_allclose(result[j == 0], 1) assert (np.all(result[np.abs(j) > 1] == 0)) def test_hscharr_zeros(): """Horizontal Scharr on an array of all zeros.""" result = F.hscharr(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_hscharr_mask(): """Horizontal Scharr on a masked array should be zero.""" np.random.seed(0) result = F.hscharr(np.random.uniform(size=(10, 10)), - np.zeros((10, 10), bool)) - assert (np.all(result == 0)) + np.zeros((10, 10), bool)) + assert_allclose(result, 0) def test_hscharr_horizontal(): @@ -198,21 +198,21 @@ def test_hscharr_vertical(): i, j = np.mgrid[-5:6, -5:6] image = (j >= 0).astype(float) result = F.hscharr(image) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vscharr_zeros(): """Vertical Scharr on an array of all zeros.""" result = F.vscharr(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vscharr_mask(): """Vertical Scharr on a masked array should be zero.""" np.random.seed(0) result = F.vscharr(np.random.uniform(size=(10, 10)), - np.zeros((10, 10), bool)) - assert (np.all(result == 0)) + np.zeros((10, 10), bool)) + assert_allclose(result, 0) def test_vscharr_vertical(): @@ -231,14 +231,13 @@ def test_vscharr_horizontal(): i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) result = F.vscharr(image) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(result, 0) def test_prewitt_zeros(): """Prewitt on an array of all zeros.""" result = F.prewitt(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_prewitt_mask(): @@ -246,37 +245,34 @@ def test_prewitt_mask(): np.random.seed(0) result = F.prewitt(np.random.uniform(size=(10, 10)), np.zeros((10, 10), bool)) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(np.abs(result), 0) def test_prewitt_horizontal(): """Prewitt on an edge should be a horizontal line.""" i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) - result = F.prewitt(image) + result = F.prewitt(image) * np.sqrt(2) # Fudge the eroded points i[np.abs(j) == 5] = 10000 - eps = .000001 assert (np.all(result[i == 0] == 1)) - assert (np.all(np.abs(result[np.abs(i) > 1]) < eps)) + assert_allclose(result[np.abs(i) > 1], 0, atol=1e-10) def test_prewitt_vertical(): """Prewitt on a vertical edge should be a vertical line.""" i, j = np.mgrid[-5:6, -5:6] image = (j >= 0).astype(float) - result = F.prewitt(image) - eps = .000001 + result = F.prewitt(image) * np.sqrt(2) j[np.abs(i) == 5] = 10000 - assert (np.all(result[j == 0] == 1)) - assert (np.all(np.abs(result[np.abs(j) > 1]) < eps)) + assert_allclose(result[j == 0], 1) + assert_allclose(result[np.abs(j) > 1], 0, atol=1e-10) def test_hprewitt_zeros(): """Horizontal prewitt on an array of all zeros.""" result = F.hprewitt(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_hprewitt_mask(): @@ -284,8 +280,7 @@ def test_hprewitt_mask(): np.random.seed(0) result = F.hprewitt(np.random.uniform(size=(10, 10)), np.zeros((10, 10), bool)) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(result, 0) def test_hprewitt_horizontal(): @@ -295,9 +290,8 @@ def test_hprewitt_horizontal(): result = F.hprewitt(image) # Fudge the eroded points i[np.abs(j) == 5] = 10000 - eps = .000001 assert (np.all(result[i == 0] == 1)) - assert (np.all(np.abs(result[np.abs(i) > 1]) < eps)) + assert_allclose(result[np.abs(i) > 1], 0, atol=1e-10) def test_hprewitt_vertical(): @@ -305,14 +299,13 @@ def test_hprewitt_vertical(): i, j = np.mgrid[-5:6, -5:6] image = (j >= 0).astype(float) result = F.hprewitt(image) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(result, 0, atol=1e-10) def test_vprewitt_zeros(): """Vertical prewitt on an array of all zeros.""" result = F.vprewitt(np.zeros((10, 10)), np.ones((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vprewitt_mask(): @@ -320,7 +313,7 @@ def test_vprewitt_mask(): np.random.seed(0) result = F.vprewitt(np.random.uniform(size=(10, 10)), np.zeros((10, 10), bool)) - assert (np.all(result == 0)) + assert_allclose(result, 0) def test_vprewitt_vertical(): @@ -331,8 +324,7 @@ def test_vprewitt_vertical(): # Fudge the eroded points j[np.abs(i) == 5] = 10000 assert (np.all(result[j == 0] == 1)) - eps = .000001 - assert (np.all(np.abs(result[np.abs(j) > 1]) < eps)) + assert_allclose(result[np.abs(j) > 1], 0, atol=1e-10) def test_vprewitt_horizontal(): @@ -340,21 +332,20 @@ def test_vprewitt_horizontal(): i, j = np.mgrid[-5:6, -5:6] image = (i >= 0).astype(float) result = F.vprewitt(image) - eps = .000001 - assert (np.all(np.abs(result) < eps)) + assert_allclose(result, 0) def test_horizontal_mask_line(): """Horizontal edge filters mask pixels surrounding input mask.""" - vgrad, _ = np.mgrid[:1:11j, :1:11j] # vertical gradient with spacing 0.1 - vgrad[5, :] = 1 # bad horizontal line + vgrad, _ = np.mgrid[:1:11j, :1:11j] # vertical gradient with spacing 0.1 + vgrad[5, :] = 1 # bad horizontal line mask = np.ones_like(vgrad) - mask[5, :] = 0 # mask bad line + mask[5, :] = 0 # mask bad line expected = np.zeros_like(vgrad) - expected[1:-1, 1:-1] = 0.2 # constant gradient for most of image, - expected[4:7, 1:-1] = 0 # but line and neighbors masked + expected[1:-1, 1:-1] = 0.2 # constant gradient for most of image, + expected[4:7, 1:-1] = 0 # but line and neighbors masked for grad_func in (F.hprewitt, F.hsobel, F.hscharr): result = grad_func(vgrad, mask) @@ -363,21 +354,36 @@ def test_horizontal_mask_line(): def test_vertical_mask_line(): """Vertical edge filters mask pixels surrounding input mask.""" - _, hgrad = np.mgrid[:1:11j, :1:11j] # horizontal gradient with spacing 0.1 - hgrad[:, 5] = 1 # bad vertical line + _, hgrad = np.mgrid[:1:11j, :1:11j] # horizontal gradient with spacing 0.1 + hgrad[:, 5] = 1 # bad vertical line mask = np.ones_like(hgrad) - mask[:, 5] = 0 # mask bad line + mask[:, 5] = 0 # mask bad line expected = np.zeros_like(hgrad) - expected[1:-1, 1:-1] = 0.2 # constant gradient for most of image, - expected[1:-1, 4:7] = 0 # but line and neighbors masked + expected[1:-1, 1:-1] = 0.2 # constant gradient for most of image, + expected[1:-1, 4:7] = 0 # but line and neighbors masked for grad_func in (F.vprewitt, F.vsobel, F.vscharr): result = grad_func(hgrad, mask) yield assert_close, result, expected +def test_range(): + """Output of edge detection should be in [0, 1]""" + image = np.random.random((100, 100)) + for detector in (F.sobel, F.scharr, F.prewitt, F.roberts): + out = detector(image) + assert_(out.min() >= 0, + "Minimum of `{0}` is smaller than zero".format( + detector.__name__) + ) + assert_(out.max() <= 1, + "Maximum of `{0}` is larger than 1".format( + detector.__name__) + ) + + if __name__ == "__main__": from numpy import testing testing.run_module_suite()