Merge pull request #364 from ahojnnes/scharr

ENH: Add scharr edge filter.
This commit is contained in:
Stefan van der Walt
2012-10-17 00:04:12 -07:00
3 changed files with 260 additions and 26 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
from .lpi_filter import *
from .ctmf import median_filter
from ._canny import canny
from .edges import sobel, hsobel, vsobel, hprewitt, vprewitt, prewitt
from .edges import (sobel, hsobel, vsobel, scharr, hscharr, vscharr, prewitt,
hprewitt, vprewitt)
from ._tv_denoise import tv_denoise
from ._rank_order import rank_order
from .thresholding import threshold_otsu, threshold_adaptive
+136 -23
View File
@@ -1,6 +1,7 @@
"""edges.py - Sobel edge filter
"""edges.py - Edge filters
Originally part of CellProfiler, code licensed under both GPL and BSD licenses.
Sobel and Prewitt filters originally part of CellProfiler, code licensed under
both GPL and BSD licenses.
Website: http://www.cellprofiler.org
Copyright (c) 2003-2009 Massachusetts Institute of Technology
Copyright (c) 2009-2011 Broad Institute
@@ -34,13 +35,13 @@ def _mask_filter_result(result, mask):
def sobel(image, mask=None):
"""Calculate the absolute magnitude Sobel to find edges.
"""Find the edge magnitude using the Sobel transform.
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process.
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -48,7 +49,7 @@ def sobel(image, mask=None):
Returns
-------
output : ndarray
The Sobel edge map.
The Sobel edge map.
Notes
-----
@@ -67,9 +68,9 @@ def hsobel(image, mask=None):
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process.
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -77,7 +78,7 @@ def hsobel(image, mask=None):
Returns
-------
output : ndarray
The Sobel edge map.
The Sobel edge map.
Notes
-----
@@ -102,9 +103,9 @@ def vsobel(image, mask=None):
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -112,7 +113,7 @@ def vsobel(image, mask=None):
Returns
-------
output : ndarray
The Sobel edge map.
The Sobel edge map.
Notes
-----
@@ -132,14 +133,14 @@ def vsobel(image, mask=None):
return _mask_filter_result(result, mask)
def prewitt(image, mask=None):
"""Find the edge magnitude using the Prewitt transform.
def scharr(image, mask=None):
"""Find the edge magnitude using the Scharr transform.
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process.
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -147,7 +148,119 @@ def prewitt(image, mask=None):
Returns
-------
output : ndarray
The Prewitt edge map.
The Scharr edge map.
Notes
-----
Take the square root of the sum of the squares of the horizontal and
vertical Scharrs to get a magnitude that's somewhat insensitive to
direction.
References
----------
.. [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)
def hscharr(image, mask=None):
"""Find the horizontal edges of an image using the Scharr transform.
Parameters
----------
image : 2-D array
Image to process.
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
Returns
-------
output : ndarray
The Scharr edge map.
Notes
-----
We use the following kernel and return the absolute value of the
result at each point::
3 10 3
0 0 0
-3 -10 -3
References
----------
.. [1] D. Kroon, 2009, Short Paper University Twente, Numerical Optimization
of Kernel Based Image Derivatives.
"""
image = img_as_float(image)
result = np.abs(convolve(image,
np.array([[ 3, 10, 3],
[ 0, 0, 0],
[-3, -10, -3]]).astype(float) / 16.0))
return _mask_filter_result(result, mask)
def vscharr(image, mask=None):
"""Find the vertical edges of an image using the Scharr transform.
Parameters
----------
image : 2-D array
Image to process
mask : 2-D array, optional
An optional mask to limit the application to a certain area
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
Returns
-------
output : ndarray
The Scharr edge map.
Notes
-----
We use the following kernel and return the absolute value of the
result at each point::
3 0 -3
10 0 -10
3 0 -3
References
----------
.. [1] D. Kroon, 2009, Short Paper University Twente, Numerical Optimization
of Kernel Based Image Derivatives.
"""
image = img_as_float(image)
result = np.abs(convolve(image,
np.array([[ 3, 0, -3],
[10, 0, -10],
[ 3, 0, -3]]).astype(float) / 16.0))
return _mask_filter_result(result, mask)
def prewitt(image, mask=None):
"""Find the edge magnitude using the Prewitt transform.
Parameters
----------
image : 2-D array
Image to process.
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
Returns
-------
output : ndarray
The Prewitt edge map.
Notes
-----
@@ -162,9 +275,9 @@ def hprewitt(image, mask=None):
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process.
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -172,7 +285,7 @@ def hprewitt(image, mask=None):
Returns
-------
output : ndarray
The Prewitt edge map.
The Prewitt edge map.
Notes
-----
@@ -197,9 +310,9 @@ def vprewitt(image, mask=None):
Parameters
----------
image : array_like, dtype=float
image : 2-D array
Image to process.
mask : array_like, dtype=bool, optional
mask : 2-D array, optional
An optional mask to limit the application to a certain area.
Note that pixels surrounding masked regions are also masked to
prevent masked regions from affecting the result.
@@ -207,7 +320,7 @@ def vprewitt(image, mask=None):
Returns
-------
output : ndarray
The Prewitt edge map.
The Prewitt edge map.
Notes
-----
+122 -2
View File
@@ -9,6 +9,7 @@ def test_sobel_zeros():
result = F.sobel(np.zeros((10, 10)), np.ones((10, 10), bool))
assert (np.all(result == 0))
def test_sobel_mask():
"""Sobel on a masked array should be zero"""
np.random.seed(0)
@@ -16,6 +17,7 @@ def test_sobel_mask():
np.zeros((10, 10), bool))
assert (np.all(result == 0))
def test_sobel_horizontal():
"""Sobel on an edge should be a horizontal line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -26,6 +28,7 @@ def test_sobel_horizontal():
assert (np.all(result[i == 0] == 1))
assert (np.all(result[np.abs(i) > 1] == 0))
def test_sobel_vertical():
"""Sobel on a vertical edge should be a vertical line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -41,6 +44,7 @@ def test_hsobel_zeros():
result = F.hsobel(np.zeros((10, 10)), np.ones((10, 10), bool))
assert (np.all(result == 0))
def test_hsobel_mask():
"""Horizontal Sobel on a masked array should be zero"""
np.random.seed(0)
@@ -48,6 +52,7 @@ def test_hsobel_mask():
np.zeros((10, 10), bool))
assert (np.all(result == 0))
def test_hsobel_horizontal():
"""Horizontal Sobel on an edge should be a horizontal line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -58,6 +63,7 @@ def test_hsobel_horizontal():
assert (np.all(result[i == 0] == 1))
assert (np.all(result[np.abs(i) > 1] == 0))
def test_hsobel_vertical():
"""Horizontal Sobel on a vertical edge should be zero"""
i, j = np.mgrid[-5:6, -5:6]
@@ -71,6 +77,7 @@ def test_vsobel_zeros():
result = F.vsobel(np.zeros((10, 10)), np.ones((10, 10), bool))
assert (np.all(result == 0))
def test_vsobel_mask():
"""Vertical Sobel on a masked array should be zero"""
np.random.seed(0)
@@ -78,6 +85,7 @@ def test_vsobel_mask():
np.zeros((10, 10), bool))
assert (np.all(result == 0))
def test_vsobel_vertical():
"""Vertical Sobel on an edge should be a vertical line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -88,6 +96,7 @@ def test_vsobel_vertical():
assert (np.all(result[j == 0] == 1))
assert (np.all(result[np.abs(j) > 1] == 0))
def test_vsobel_horizontal():
"""vertical Sobel on a horizontal edge should be zero"""
i, j = np.mgrid[-5:6, -5:6]
@@ -97,11 +106,114 @@ def test_vsobel_horizontal():
assert (np.all(np.abs(result) < eps))
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))
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))
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)
# Fudge the eroded points
i[np.abs(j) == 5] = 10000
assert (np.all(result[i == 0] == 1))
assert (np.all(result[np.abs(i) > 1] == 0))
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)
j[np.abs(i) == 5] = 10000
assert (np.all(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))
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))
def test_hscharr_horizontal():
"""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.hscharr(image)
# Fudge the eroded points
i[np.abs(j) == 5] = 10000
assert (np.all(result[i == 0] == 1))
assert (np.all(result[np.abs(i) > 1] == 0))
def test_hscharr_vertical():
"""Horizontal Scharr on a vertical edge should be zero"""
i, j = np.mgrid[-5:6, -5:6]
image = (j >= 0).astype(float)
result = F.hscharr(image)
assert (np.all(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))
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))
def test_vscharr_vertical():
"""Vertical Scharr on an edge should be a vertical line"""
i, j = np.mgrid[-5:6, -5:6]
image = (j >= 0).astype(float)
result = F.vscharr(image)
# Fudge the eroded points
j[np.abs(i) == 5] = 10000
assert (np.all(result[j == 0] == 1))
assert (np.all(result[np.abs(j) > 1] == 0))
def test_vscharr_horizontal():
"""vertical Scharr on a horizontal edge should be zero"""
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))
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))
def test_prewitt_mask():
"""Prewitt on a masked array should be zero"""
np.random.seed(0)
@@ -110,6 +222,7 @@ def test_prewitt_mask():
eps = .000001
assert (np.all(np.abs(result) < eps))
def test_prewitt_horizontal():
"""Prewitt on an edge should be a horizontal line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -121,6 +234,7 @@ def test_prewitt_horizontal():
assert (np.all(result[i == 0] == 1))
assert (np.all(np.abs(result[np.abs(i) > 1]) < eps))
def test_prewitt_vertical():
"""Prewitt on a vertical edge should be a vertical line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -137,6 +251,7 @@ def test_hprewitt_zeros():
result = F.hprewitt(np.zeros((10, 10)), np.ones((10, 10), bool))
assert (np.all(result == 0))
def test_hprewitt_mask():
"""Horizontal prewitt on a masked array should be zero"""
np.random.seed(0)
@@ -145,6 +260,7 @@ def test_hprewitt_mask():
eps = .000001
assert (np.all(np.abs(result) < eps))
def test_hprewitt_horizontal():
"""Horizontal prewitt on an edge should be a horizontal line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -156,6 +272,7 @@ def test_hprewitt_horizontal():
assert (np.all(result[i == 0] == 1))
assert (np.all(np.abs(result[np.abs(i) > 1]) < eps))
def test_hprewitt_vertical():
"""Horizontal prewitt on a vertical edge should be zero"""
i, j = np.mgrid[-5:6, -5:6]
@@ -170,6 +287,7 @@ def test_vprewitt_zeros():
result = F.vprewitt(np.zeros((10, 10)), np.ones((10, 10), bool))
assert (np.all(result == 0))
def test_vprewitt_mask():
"""Vertical prewitt on a masked array should be zero"""
np.random.seed(0)
@@ -177,6 +295,7 @@ def test_vprewitt_mask():
np.zeros((10, 10), bool))
assert (np.all(result == 0))
def test_vprewitt_vertical():
"""Vertical prewitt on an edge should be a vertical line"""
i, j = np.mgrid[-5:6, -5:6]
@@ -188,6 +307,7 @@ def test_vprewitt_vertical():
eps = .000001
assert (np.all(np.abs(result[np.abs(j) > 1]) < eps))
def test_vprewitt_horizontal():
"""Vertical prewitt on a horizontal edge should be zero"""
i, j = np.mgrid[-5:6, -5:6]
@@ -209,7 +329,7 @@ def test_horizontal_mask_line():
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):
for grad_func in (F.hprewitt, F.hsobel, F.hscharr):
result = grad_func(vgrad, mask)
yield assert_close, result, expected
@@ -226,7 +346,7 @@ def test_vertical_mask_line():
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):
for grad_func in (F.vprewitt, F.vsobel, F.vscharr):
result = grad_func(hgrad, mask)
yield assert_close, result, expected