Merge pull request #1674 from robintw/canny-percentile-thresholds

Added quantile_threshold option to canny edge detection
This commit is contained in:
Juan Nunez-Iglesias
2015-09-03 18:00:52 +02:00
2 changed files with 58 additions and 1 deletions
+19 -1
View File
@@ -50,7 +50,8 @@ def smooth_with_function_and_mask(image, function, mask):
return output_image
def canny(image, sigma=1., low_threshold=None, high_threshold=None, mask=None):
def canny(image, sigma=1., low_threshold=None, high_threshold=None, mask=None,
use_quantiles=False):
"""Edge filter an image using the Canny algorithm.
Parameters
@@ -67,6 +68,10 @@ def canny(image, sigma=1., low_threshold=None, high_threshold=None, mask=None):
If None, high_threshold is set to 20% of dtype's max.
mask : array, dtype=bool, optional
Mask to limit the application of Canny to a certain area.
use_quantiles : bool, optional
If True then treat low_threshold and high_threshold as quantiles of the
edge magnitude image, rather than absolute edge magnitude values. If True
then the thresholds must be in the range [0, 1].
Returns
-------
@@ -246,6 +251,19 @@ def canny(image, sigma=1., low_threshold=None, high_threshold=None, mask=None):
c2 = magnitude[1:, :-1][pts[:-1, 1:]]
c_minus = c2 * w + c1 * (1 - w) <= m
local_maxima[pts] = c_plus & c_minus
#
#---- If use_quantiles is set then calculate the thresholds to use
#
if use_quantiles:
if high_threshold > 1.0 or low_threshold > 1.0:
raise ValueError("Quantile thresholds must not be > 1.0")
if high_threshold < 0.0 or low_threshold < 0.0:
raise ValueError("Quantile thresholds must not be < 0.0")
high_threshold = np.percentile(magnitude, 100.0 * high_threshold)
low_threshold = np.percentile(magnitude, 100.0 * low_threshold)
#
#---- Create two masks at the two thresholds.
#
+39
View File
@@ -1,7 +1,10 @@
import unittest
import numpy as np
from numpy.testing import assert_equal
from scipy.ndimage import binary_dilation, binary_erosion
import skimage.feature as F
from skimage import filters, data
from skimage import img_as_float
class TestCanny(unittest.TestCase):
@@ -66,3 +69,39 @@ class TestCanny(unittest.TestCase):
result1 = F.canny(np.zeros((20, 20)), 4, 0, 0, np.ones((20, 20), bool))
result2 = F.canny(np.zeros((20, 20)), 4, 0, 0)
self.assertTrue(np.all(result1 == result2))
def test_use_quantiles(self):
image = img_as_float(data.camera()[::50,::50])
# Correct output produced manually with quantiles
# of 0.8 and 0.6 for high and low respectively
correct_output = np.array([[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, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=bool)
result = F.canny(image, low_threshold=0.6, high_threshold=0.8, use_quantiles=True)
assert_equal(result, correct_output)
def test_invalid_use_quantiles(self):
image = img_as_float(data.camera()[::50,::50])
self.assertRaises(ValueError, F.canny, image, use_quantiles=True,
low_threshold=0.5, high_threshold=3.6)
self.assertRaises(ValueError, F.canny, image, use_quantiles=True,
low_threshold=-5, high_threshold=0.5)
self.assertRaises(ValueError, F.canny, image, use_quantiles=True,
low_threshold=99, high_threshold=0.9)
self.assertRaises(ValueError, F.canny, image, use_quantiles=True,
low_threshold=0.5, high_threshold=-100)