From e9f3bd66ac70cfce7eb3b665238a5ca0d7297b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 14:18:03 +0200 Subject: [PATCH 01/18] TEST: fix mistake --- skimage/transform/tests/test_hough_transform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 651861f3..c1917b66 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -152,9 +152,9 @@ def test_hough_ellipse_zero_angle(): a = 6 b = 8 x0 = 12 - y0 = 12 + y0 = 15 angle = 0 - rr, cc = ellipse_perimeter(x0, x0, b, a) + rr, cc = ellipse_perimeter(y0, x0, b, a) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=9) assert_equal(result[0][0], x0) @@ -165,13 +165,13 @@ def test_hough_ellipse_zero_angle(): def test_hough_ellipse_non_zero_angle(): - img = np.zeros((20, 20), dtype=int) + img = np.zeros((30, 20), dtype=int) a = 6 b = 9 x0 = 10 - y0 = 10 + y0 = 15 angle = np.pi / 1.35 - rr, cc = ellipse_perimeter(x0, x0, b, a, orientation=angle) + rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) assert_almost_equal(result[0][0] / 100., x0 / 100., decimal=1) From 7a1b1c28de7fd7c1c8ad4fb43d4a9ddf1afad2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 15:26:47 +0200 Subject: [PATCH 02/18] FIX: fix angle convention --- skimage/transform/_hough_transform.pyx | 9 +++++++- .../transform/tests/test_hough_transform.py | 23 ++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index e5fd3ed8..db39172e 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -206,9 +206,16 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, hist_max = np.max(hist) if hist_max > threshold: angle = np.arctan2(p1x - p2x, p1y - p2y) - # pi - angle to keep ellipse_perimeter() convention + # to keep ellipse_perimeter() convention if angle != 0: angle = np.pi - angle + # When angle is not in [-pi:pi] + # it would mean in ellipse_perimeter() + # that a < b. But we keep a > b. + if angle > np.pi: + angle = angle - np.pi / 2. + elif angle < - np.pi: + angle = angle + np.pi / 2. b = sqrt(bin_edges[hist.argmax()]) results.append((x0, y0, diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index c1917b66..3e5ed71c 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -165,9 +165,9 @@ def test_hough_ellipse_zero_angle(): def test_hough_ellipse_non_zero_angle(): - img = np.zeros((30, 20), dtype=int) + img = np.zeros((30, 24), dtype=int) a = 6 - b = 9 + b = 12 x0 = 10 y0 = 15 angle = np.pi / 1.35 @@ -176,10 +176,27 @@ def test_hough_ellipse_non_zero_angle(): result = tf.hough_ellipse(img, threshold=15, accuracy=3) assert_almost_equal(result[0][0] / 100., x0 / 100., decimal=1) assert_almost_equal(result[0][1] / 100., y0 / 100., decimal=1) - assert_almost_equal(result[0][2] / 100., b / 100., decimal=1) + assert_almost_equal(result[0][2] / 10., b / 10., decimal=1) assert_almost_equal(result[0][3] / 100., a / 100., decimal=1) assert_almost_equal(result[0][4], angle, decimal=1) +def test_hough_ellipse_non_zero_angle2(): + img = np.zeros((30, 24), dtype=int) + b = 6 + a = 12 + x0 = 10 + y0 = 15 + angle = np.pi / 1.35 + rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + assert_almost_equal(result[0][0] / 100., x0 / 100., decimal=1) + assert_almost_equal(result[0][1] / 100., y0 / 100., decimal=1) + assert_almost_equal(result[0][2] / 100., a / 100., decimal=1) + assert_almost_equal(result[0][3] / 100., b / 100., decimal=1) + assert_almost_equal(result[0][4], angle, decimal=1) + + if __name__ == "__main__": np.testing.run_module_suite() From 7e970b18cd69de0ab9c59b275c9d4c12a615756c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 15:54:01 +0200 Subject: [PATCH 03/18] MAINT change HT return API --- ...lot_circular_elliptical_hough_transform.py | 10 +++---- skimage/transform/_hough_transform.pyx | 8 +++--- .../transform/tests/test_hough_transform.py | 28 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index bd036e03..ca30b136 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -122,11 +122,11 @@ edges = filter.canny(image_gray, sigma=2.0, accum = hough_ellipse(edges, accuracy=10, threshold=170, min_size=50) accum.sort(key=lambda x:x[5]) # Estimated parameters for the ellipse -center_y = int(accum[-1][0]) -center_x = int(accum[-1][1]) -xradius = int(accum[-1][2]) -yradius = int(accum[-1][3]) -angle = np.pi - accum[-1][4] +center_y = int(accum[-1][1]) +center_x = int(accum[-1][2]) +xradius = int(accum[-1][3]) +yradius = int(accum[-1][4]) +angle = np.pi - accum[-1][5] # Draw the ellipse on the original image cx, cy = ellipse_perimeter(center_y, center_x, diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index db39172e..577313a1 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -122,7 +122,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, Returns ------- - res : list of tuples [(x0, y0, a, b, angle, accumulator)] + res : list of tuples [(accumulator, x0, y0, a, b, angle)] Where (x0, y0) is the center, (a, b) major and minor axis. The angle value follows `draw.ellipse_perimeter()` convention. @@ -132,7 +132,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, >>> rr, cc = draw.ellipse_perimeter(10, 10, 6, 8) >>> img[cc, rr] = 1 >>> result = hough_ellipse(img, threshold=8) - [(10.0, 10.0, 8.0, 6.0, 0.0, 10)] + [(10, 10.0, 10.0, 8.0, 6.0, 0.0)] Notes ----- @@ -217,12 +217,12 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, elif angle < - np.pi: angle = angle + np.pi / 2. b = sqrt(bin_edges[hist.argmax()]) - results.append((x0, + results.append((hist_max, # Accumulator + x0, y0, a, b, angle, - hist_max, # Accumulator )) acc = [] diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 3e5ed71c..6c6acb96 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -157,11 +157,11 @@ def test_hough_ellipse_zero_angle(): rr, cc = ellipse_perimeter(y0, x0, b, a) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=9) - assert_equal(result[0][0], x0) - assert_equal(result[0][1], y0) - assert_almost_equal(result[0][2], b, decimal=1) - assert_almost_equal(result[0][3], a, decimal=1) - assert_equal(result[0][4], angle) + assert_equal(result[0][1], x0) + assert_equal(result[0][2], y0) + assert_almost_equal(result[0][3], b, decimal=1) + assert_almost_equal(result[0][4], a, decimal=1) + assert_equal(result[0][5], angle) def test_hough_ellipse_non_zero_angle(): @@ -174,11 +174,11 @@ def test_hough_ellipse_non_zero_angle(): rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - assert_almost_equal(result[0][0] / 100., x0 / 100., decimal=1) - assert_almost_equal(result[0][1] / 100., y0 / 100., decimal=1) - assert_almost_equal(result[0][2] / 10., b / 10., decimal=1) - assert_almost_equal(result[0][3] / 100., a / 100., decimal=1) - assert_almost_equal(result[0][4], angle, decimal=1) + assert_almost_equal(result[0][1] / 100., x0 / 100., decimal=1) + assert_almost_equal(result[0][2] / 100., y0 / 100., decimal=1) + assert_almost_equal(result[0][3] / 10., b / 10., decimal=1) + assert_almost_equal(result[0][4] / 100., a / 100., decimal=1) + assert_almost_equal(result[0][5], angle, decimal=1) def test_hough_ellipse_non_zero_angle2(): @@ -191,11 +191,11 @@ def test_hough_ellipse_non_zero_angle2(): rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - assert_almost_equal(result[0][0] / 100., x0 / 100., decimal=1) - assert_almost_equal(result[0][1] / 100., y0 / 100., decimal=1) - assert_almost_equal(result[0][2] / 100., a / 100., decimal=1) + assert_almost_equal(result[0][1] / 100., x0 / 100., decimal=1) + assert_almost_equal(result[0][2] / 100., y0 / 100., decimal=1) + assert_almost_equal(result[0][3] / 100., a / 100., decimal=1) assert_almost_equal(result[0][3] / 100., b / 100., decimal=1) - assert_almost_equal(result[0][4], angle, decimal=1) + assert_almost_equal(result[0][5], angle, decimal=1) if __name__ == "__main__": From debd4d54d6d81e491f3ca712b8da4d5a721c5d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 16:03:42 +0200 Subject: [PATCH 04/18] ENH: use heapq to select the best match --- ...lot_circular_elliptical_hough_transform.py | 15 ++++---- skimage/transform/_hough_transform.pyx | 10 ++++-- .../transform/tests/test_hough_transform.py | 34 +++++++++++-------- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index ca30b136..d500be79 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -119,14 +119,15 @@ edges = filter.canny(image_gray, sigma=2.0, # The accuracy corresponds to the bin size of a major axis. # The value is chosen in order to get a single high accumulator. # The threshold eliminates low accumulators -accum = hough_ellipse(edges, accuracy=10, threshold=170, min_size=50) -accum.sort(key=lambda x:x[5]) +accum = hough_ellipse(edges, accuracy=10, threshold=150, min_size=50) +# Select the highest accumulator +best = heapq.nlargest(1, accum)[0] # Estimated parameters for the ellipse -center_y = int(accum[-1][1]) -center_x = int(accum[-1][2]) -xradius = int(accum[-1][3]) -yradius = int(accum[-1][4]) -angle = np.pi - accum[-1][5] +center_y = int(best[1]) +center_x = int(best[2]) +xradius = int(best[3]) +yradius = int(best[4]) +angle = np.pi - best[5] # Draw the ellipse on the original image cx, cy = ellipse_perimeter(center_y, center_x, diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 577313a1..958bacc4 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -3,6 +3,7 @@ #cython: nonecheck=False #cython: wraparound=False import numpy as np +import heapq cimport numpy as cnp cimport cython @@ -131,8 +132,12 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, >>> img = np.zeros((25, 25), dtype=int) >>> rr, cc = draw.ellipse_perimeter(10, 10, 6, 8) >>> img[cc, rr] = 1 - >>> result = hough_ellipse(img, threshold=8) + >>> result = hough_ellipse(img, threshold=4) + >>> # extract the highest accumulator + >>> heapq.nlargest(1, result) [(10, 10.0, 10.0, 8.0, 6.0, 0.0)] + >>> # To sort them all + >>> results = [heappop(results) for i in range(len(results))] Notes ----- @@ -217,7 +222,8 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, elif angle < - np.pi: angle = angle + np.pi / 2. b = sqrt(bin_edges[hist.argmax()]) - results.append((hist_max, # Accumulator + heapq.heappush(results, + (hist_max, # Accumulator x0, y0, a, diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 6c6acb96..57d14278 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -1,4 +1,5 @@ import numpy as np +import heapq from numpy.testing import (assert_almost_equal, assert_equal, ) @@ -157,11 +158,12 @@ def test_hough_ellipse_zero_angle(): rr, cc = ellipse_perimeter(y0, x0, b, a) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=9) - assert_equal(result[0][1], x0) - assert_equal(result[0][2], y0) - assert_almost_equal(result[0][3], b, decimal=1) - assert_almost_equal(result[0][4], a, decimal=1) - assert_equal(result[0][5], angle) + best = heapq.nlargest(1, result)[0] + assert_equal(best[1], x0) + assert_equal(best[2], y0) + assert_almost_equal(best[3], b, decimal=1) + assert_almost_equal(best[4], a, decimal=1) + assert_equal(best[5], angle) def test_hough_ellipse_non_zero_angle(): @@ -174,11 +176,12 @@ def test_hough_ellipse_non_zero_angle(): rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - assert_almost_equal(result[0][1] / 100., x0 / 100., decimal=1) - assert_almost_equal(result[0][2] / 100., y0 / 100., decimal=1) - assert_almost_equal(result[0][3] / 10., b / 10., decimal=1) - assert_almost_equal(result[0][4] / 100., a / 100., decimal=1) - assert_almost_equal(result[0][5], angle, decimal=1) + best = heapq.nlargest(1, result)[0] + assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) + assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) + assert_almost_equal(best[3] / 10., b / 10., decimal=1) + assert_almost_equal(best[4] / 100., a / 100., decimal=1) + assert_almost_equal(best[5], angle, decimal=1) def test_hough_ellipse_non_zero_angle2(): @@ -191,11 +194,12 @@ def test_hough_ellipse_non_zero_angle2(): rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - assert_almost_equal(result[0][1] / 100., x0 / 100., decimal=1) - assert_almost_equal(result[0][2] / 100., y0 / 100., decimal=1) - assert_almost_equal(result[0][3] / 100., a / 100., decimal=1) - assert_almost_equal(result[0][3] / 100., b / 100., decimal=1) - assert_almost_equal(result[0][5], angle, decimal=1) + best = heapq.nlargest(1, result)[0] + assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) + assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) + assert_almost_equal(best[3] / 100., a / 100., decimal=1) + assert_almost_equal(best[3] / 100., b / 100., decimal=1) + assert_almost_equal(best[5], angle, decimal=1) if __name__ == "__main__": From 742699c570ef03b41ee61c9d1c2513afa99e3ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 16:18:14 +0200 Subject: [PATCH 05/18] TEST: fix precision --- skimage/transform/tests/test_hough_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 57d14278..db8d1bbc 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -197,7 +197,7 @@ def test_hough_ellipse_non_zero_angle2(): best = heapq.nlargest(1, result)[0] assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) - assert_almost_equal(best[3] / 100., a / 100., decimal=1) + assert_almost_equal(best[3] / 10., a / 10., decimal=1) assert_almost_equal(best[3] / 100., b / 100., decimal=1) assert_almost_equal(best[5], angle, decimal=1) From 594a228a0651343dbb06a136fea7b3575c06dfed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 16:19:14 +0200 Subject: [PATCH 06/18] TEST: fix bad subs --- skimage/transform/tests/test_hough_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index db8d1bbc..69e92492 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -198,7 +198,7 @@ def test_hough_ellipse_non_zero_angle2(): assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) assert_almost_equal(best[3] / 10., a / 10., decimal=1) - assert_almost_equal(best[3] / 100., b / 100., decimal=1) + assert_almost_equal(best[4] / 100., b / 100., decimal=1) assert_almost_equal(best[5], angle, decimal=1) From 1a9d5bb4cafcd120255fa482cf96a52b79114a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 16:36:55 +0200 Subject: [PATCH 07/18] TEST: revert precision --- skimage/transform/tests/test_hough_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 69e92492..8cb4f980 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -197,7 +197,7 @@ def test_hough_ellipse_non_zero_angle2(): best = heapq.nlargest(1, result)[0] assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) - assert_almost_equal(best[3] / 10., a / 10., decimal=1) + assert_almost_equal(best[3] / 100., a / 100., decimal=1) assert_almost_equal(best[4] / 100., b / 100., decimal=1) assert_almost_equal(best[5], angle, decimal=1) From 6a114e8708f106b07143296eba192ddf65a6dacd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 16:40:04 +0200 Subject: [PATCH 08/18] MINOR: fix non ascii char --- doc/examples/plot_circular_elliptical_hough_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index d500be79..d8793ca8 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -120,7 +120,7 @@ edges = filter.canny(image_gray, sigma=2.0, # The value is chosen in order to get a single high accumulator. # The threshold eliminates low accumulators accum = hough_ellipse(edges, accuracy=10, threshold=150, min_size=50) -# Select the highest accumulator +# Select the highest accumulator best = heapq.nlargest(1, accum)[0] # Estimated parameters for the ellipse center_y = int(best[1]) From 7c652c74d001f14a4a6fb9c86ce4606a12a0baff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 17:03:55 +0200 Subject: [PATCH 09/18] add missing import heapq --- doc/examples/plot_circular_elliptical_hough_transform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index d8793ca8..a371bf24 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -104,6 +104,7 @@ References Conference on. Vol. 2. IEEE, 2002 """ import matplotlib.pyplot as plt +import heapq from skimage import data, filter, color from skimage.transform import hough_ellipse From e27b798ffab87f3d3c4554da0c57c65081e6b1fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 19:38:03 +0200 Subject: [PATCH 10/18] FIX: handle correctly main axis def --- skimage/transform/_hough_transform.pyx | 13 +- .../transform/tests/test_hough_transform.py | 177 +++++++++++++++--- 2 files changed, 160 insertions(+), 30 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 958bacc4..b14b2d5b 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -123,8 +123,8 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, Returns ------- - res : list of tuples [(accumulator, x0, y0, a, b, angle)] - Where (x0, y0) is the center, (a, b) major and minor axis. + res : list of tuples [(accumulator, y0, x0, ry, rx, angle)] + Where (y0, x0) is the center, (ry, rx) main axis. The angle value follows `draw.ellipse_perimeter()` convention. Examples @@ -135,7 +135,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, >>> result = hough_ellipse(img, threshold=4) >>> # extract the highest accumulator >>> heapq.nlargest(1, result) - [(10, 10.0, 10.0, 8.0, 6.0, 0.0)] + [(10, 10.0, 10.0, 6.0, 8.0, 0.0)] >>> # To sort them all >>> results = [heappop(results) for i in range(len(results))] @@ -211,6 +211,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, hist_max = np.max(hist) if hist_max > threshold: angle = np.arctan2(p1x - p2x, p1y - p2y) + b = sqrt(bin_edges[hist.argmax()]) # to keep ellipse_perimeter() convention if angle != 0: angle = np.pi - angle @@ -219,13 +220,11 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, # that a < b. But we keep a > b. if angle > np.pi: angle = angle - np.pi / 2. - elif angle < - np.pi: - angle = angle + np.pi / 2. - b = sqrt(bin_edges[hist.argmax()]) + a, b = b, a heapq.heappush(results, (hist_max, # Accumulator - x0, y0, + x0, a, b, angle, diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 8cb4f980..281d049d 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -150,56 +150,187 @@ def test_hough_circle_extended(): def test_hough_ellipse_zero_angle(): img = np.zeros((25, 25), dtype=int) - a = 6 - b = 8 + rx = 6 + ry = 8 x0 = 12 y0 = 15 angle = 0 - rr, cc = ellipse_perimeter(y0, x0, b, a) + rr, cc = ellipse_perimeter(y0, x0, ry, rx) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=9) best = heapq.nlargest(1, result)[0] - assert_equal(best[1], x0) - assert_equal(best[2], y0) - assert_almost_equal(best[3], b, decimal=1) - assert_almost_equal(best[4], a, decimal=1) + assert_equal(best[1], y0) + assert_equal(best[2], x0) + assert_almost_equal(best[3], ry, decimal=1) + assert_almost_equal(best[4], rx, decimal=1) assert_equal(best[5], angle) + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) -def test_hough_ellipse_non_zero_angle(): +def test_hough_ellipse_non_zero_posangle1(): + # ry > rx, angle in [0:pi/2] img = np.zeros((30, 24), dtype=int) - a = 6 - b = 12 + rx = 6 + ry = 12 x0 = 10 y0 = 15 angle = np.pi / 1.35 - rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) best = heapq.nlargest(1, result)[0] - assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) - assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) - assert_almost_equal(best[3] / 10., b / 10., decimal=1) - assert_almost_equal(best[4] / 100., a / 100., decimal=1) + assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) + assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) + assert_almost_equal(best[3] / 10., ry / 10., decimal=1) + assert_almost_equal(best[4] / 100., rx / 100., decimal=1) assert_almost_equal(best[5], angle, decimal=1) + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) -def test_hough_ellipse_non_zero_angle2(): +def test_hough_ellipse_non_zero_posangle2(): + # ry < rx, angle in [0:pi/2] img = np.zeros((30, 24), dtype=int) - b = 6 - a = 12 + rx = 12 + ry = 6 x0 = 10 y0 = 15 angle = np.pi / 1.35 - rr, cc = ellipse_perimeter(y0, x0, b, a, orientation=angle) + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) best = heapq.nlargest(1, result)[0] - assert_almost_equal(best[1] / 100., x0 / 100., decimal=1) - assert_almost_equal(best[2] / 100., y0 / 100., decimal=1) - assert_almost_equal(best[3] / 100., a / 100., decimal=1) - assert_almost_equal(best[4] / 100., b / 100., decimal=1) + assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) + assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) + assert_almost_equal(best[3] / 10., ry / 10., decimal=1) + assert_almost_equal(best[4] / 100., rx / 100., decimal=1) assert_almost_equal(best[5], angle, decimal=1) + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_posangle3(): + # ry < rx, angle in [pi/2:pi] + img = np.zeros((30, 24), dtype=int) + rx = 12 + ry = 6 + x0 = 10 + y0 = 15 + angle = np.pi / 1.35 + np.pi / 2. + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_posangle4(): + # ry < rx, angle in [pi:3pi/4] + img = np.zeros((30, 24), dtype=int) + rx = 12 + ry = 6 + x0 = 10 + y0 = 15 + angle = np.pi / 1.35 + np.pi + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_negangle1(): + # ry > rx, angle in [0:-pi/2] + img = np.zeros((30, 24), dtype=int) + rx = 6 + ry = 12 + x0 = 10 + y0 = 15 + angle = - np.pi / 1.35 + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_negangle2(): + # ry < rx, angle in [0:-pi/2] + img = np.zeros((30, 24), dtype=int) + rx = 12 + ry = 6 + x0 = 10 + y0 = 15 + angle = - np.pi / 1.35 + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_negangle3(): + # ry < rx, angle in [-pi/2:-pi] + img = np.zeros((30, 24), dtype=int) + rx = 12 + ry = 6 + x0 = 10 + y0 = 15 + angle = - np.pi / 1.35 - np.pi / 2. + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) + + +def test_hough_ellipse_non_zero_negangle4(): + # ry < rx, angle in [-pi:-3pi/4] + img = np.zeros((30, 24), dtype=int) + rx = 12 + ry = 6 + x0 = 10 + y0 = 15 + angle = - np.pi / 1.35 - np.pi + rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) + img[rr, cc] = 1 + result = tf.hough_ellipse(img, threshold=15, accuracy=3) + best = heapq.nlargest(1, result)[0] + # Check if I re-draw the ellipse, points are the same! + # ie check API compatibility between hough_ellipse and ellipse_perimeter + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + assert_equal(rr, rr2) + assert_equal(cc, cc2) if __name__ == "__main__": From 313bf0ea9a3e4102e36dbe37d078de13c8f990ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 6 Aug 2013 20:01:38 +0200 Subject: [PATCH 11/18] fix example according to new API --- doc/examples/plot_circular_elliptical_hough_transform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index a371bf24..7ada848c 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -126,12 +126,12 @@ best = heapq.nlargest(1, accum)[0] # Estimated parameters for the ellipse center_y = int(best[1]) center_x = int(best[2]) -xradius = int(best[3]) -yradius = int(best[4]) -angle = np.pi - best[5] +yradius = int(best[3]) +xradius = int(best[4]) +angle = best[5] # Draw the ellipse on the original image -cx, cy = ellipse_perimeter(center_y, center_x, +cy, cx = ellipse_perimeter(center_y, center_x, yradius, xradius, orientation=angle) image_rgb[cy, cx] = (0, 0, 1) # Draw the edge (white) and the resulting ellipse (red) From df2ee4d6364688839f5b38da593ae0f0186d2bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 29 Sep 2013 20:01:24 +0200 Subject: [PATCH 12/18] remove heapq --- ...lot_circular_elliptical_hough_transform.py | 7 +++-- skimage/transform/_hough_transform.pyx | 12 +++------ .../transform/tests/test_hough_transform.py | 27 ++++++++++++------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index 7ada848c..4c148b39 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -104,7 +104,6 @@ References Conference on. Vol. 2. IEEE, 2002 """ import matplotlib.pyplot as plt -import heapq from skimage import data, filter, color from skimage.transform import hough_ellipse @@ -120,9 +119,9 @@ edges = filter.canny(image_gray, sigma=2.0, # The accuracy corresponds to the bin size of a major axis. # The value is chosen in order to get a single high accumulator. # The threshold eliminates low accumulators -accum = hough_ellipse(edges, accuracy=10, threshold=150, min_size=50) -# Select the highest accumulator -best = heapq.nlargest(1, accum)[0] +accum = hough_ellipse(edges, accuracy=10, threshold=170, min_size=50) +accum.sort(key=lambda x:x[0]) +best = accum[-1] # Estimated parameters for the ellipse center_y = int(best[1]) center_x = int(best[2]) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index b14b2d5b..47185d79 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -3,7 +3,6 @@ #cython: nonecheck=False #cython: wraparound=False import numpy as np -import heapq cimport numpy as cnp cimport cython @@ -132,12 +131,8 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, >>> img = np.zeros((25, 25), dtype=int) >>> rr, cc = draw.ellipse_perimeter(10, 10, 6, 8) >>> img[cc, rr] = 1 - >>> result = hough_ellipse(img, threshold=4) - >>> # extract the highest accumulator - >>> heapq.nlargest(1, result) - [(10, 10.0, 10.0, 6.0, 8.0, 0.0)] - >>> # To sort them all - >>> results = [heappop(results) for i in range(len(results))] + >>> result = hough_ellipse(img, threshold=8) + [(10, 10.0, 8.0, 6.0, 0.0, 10.0)] Notes ----- @@ -221,8 +216,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, if angle > np.pi: angle = angle - np.pi / 2. a, b = b, a - heapq.heappush(results, - (hist_max, # Accumulator + results.append((hist_max, # Accumulator y0, x0, a, diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 281d049d..82b538b2 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -1,5 +1,4 @@ import numpy as np -import heapq from numpy.testing import (assert_almost_equal, assert_equal, ) @@ -158,7 +157,7 @@ def test_hough_ellipse_zero_angle(): rr, cc = ellipse_perimeter(y0, x0, ry, rx) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=9) - best = heapq.nlargest(1, result)[0] + best = result[-1] assert_equal(best[1], y0) assert_equal(best[2], x0) assert_almost_equal(best[3], ry, decimal=1) @@ -182,7 +181,8 @@ def test_hough_ellipse_non_zero_posangle1(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) assert_almost_equal(best[3] / 10., ry / 10., decimal=1) @@ -206,7 +206,8 @@ def test_hough_ellipse_non_zero_posangle2(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) assert_almost_equal(best[3] / 10., ry / 10., decimal=1) @@ -230,7 +231,8 @@ def test_hough_ellipse_non_zero_posangle3(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) @@ -249,7 +251,8 @@ def test_hough_ellipse_non_zero_posangle4(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) @@ -268,7 +271,8 @@ def test_hough_ellipse_non_zero_negangle1(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) @@ -287,7 +291,8 @@ def test_hough_ellipse_non_zero_negangle2(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) @@ -306,7 +311,8 @@ def test_hough_ellipse_non_zero_negangle3(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) @@ -325,7 +331,8 @@ def test_hough_ellipse_non_zero_negangle4(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - best = heapq.nlargest(1, result)[0] + result.sort(key=lambda x:x[0]) + best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) From 1236e97ff3626a1796b540bc4e00984b089f356e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 15:38:18 +0200 Subject: [PATCH 13/18] MAINT: np.pi -> M_PI --- skimage/transform/_hough_transform.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 47185d79..7f2b153c 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -209,12 +209,12 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, b = sqrt(bin_edges[hist.argmax()]) # to keep ellipse_perimeter() convention if angle != 0: - angle = np.pi - angle + angle = M_PI - angle # When angle is not in [-pi:pi] # it would mean in ellipse_perimeter() # that a < b. But we keep a > b. - if angle > np.pi: - angle = angle - np.pi / 2. + if angle > M_PI: + angle = angle - M_PI / 2. a, b = b, a results.append((hist_max, # Accumulator y0, From 003dbe419d9e35b30892af9103dc5c1e15eb6e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 15:53:38 +0200 Subject: [PATCH 14/18] MINOR: add missing type --- skimage/transform/_hough_transform.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 7f2b153c..21f6daf7 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -153,7 +153,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, cdef Py_ssize_t num_pixels = pixels.shape[0] cdef list acc = list() cdef list results = list() - cdef bin_size = accuracy**2 + cdef double bin_size = accuracy**2 cdef int max_b_squared if max_size is None: From 4f58c076d46eca78949f80739fd6b0722daf4ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 16:17:42 +0200 Subject: [PATCH 15/18] FIX: import M_PI --- skimage/transform/_hough_transform.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 21f6daf7..2d09751f 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -7,7 +7,7 @@ import numpy as np cimport numpy as cnp cimport cython -from libc.math cimport abs, fabs, sqrt, ceil +from libc.math cimport abs, fabs, sqrt, ceil, M_PI from libc.stdlib cimport rand from skimage.draw import circle_perimeter From def4ab8ccce26181ff19bbe080e9eb268607c2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 6 Oct 2013 22:22:13 +0200 Subject: [PATCH 16/18] PEP8 --- doc/examples/plot_circular_elliptical_hough_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index 4c148b39..e1c868fe 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -120,7 +120,7 @@ edges = filter.canny(image_gray, sigma=2.0, # The value is chosen in order to get a single high accumulator. # The threshold eliminates low accumulators accum = hough_ellipse(edges, accuracy=10, threshold=170, min_size=50) -accum.sort(key=lambda x:x[0]) +accum.sort(key=lambda x: x[0]) best = accum[-1] # Estimated parameters for the ellipse center_y = int(best[1]) From e68ba0db019568325ed648f56dcfa20b256efbb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 11 Oct 2013 18:05:40 +0200 Subject: [PATCH 17/18] Use libc.math.atan2 --- skimage/transform/_hough_transform.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 2d09751f..2ea636fd 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -7,7 +7,7 @@ import numpy as np cimport numpy as cnp cimport cython -from libc.math cimport abs, fabs, sqrt, ceil, M_PI +from libc.math cimport abs, fabs, sqrt, ceil, atan2, M_PI from libc.stdlib cimport rand from skimage.draw import circle_perimeter @@ -205,7 +205,7 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, hist, bin_edges = np.histogram(acc, bins=bins) hist_max = np.max(hist) if hist_max > threshold: - angle = np.arctan2(p1x - p2x, p1y - p2y) + angle = atan2(p1x - p2x, p1y - p2y) b = sqrt(bin_edges[hist.argmax()]) # to keep ellipse_perimeter() convention if angle != 0: From 6c677388087ac9524f8d7623809e39c480f90887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Fri, 11 Oct 2013 18:52:27 +0200 Subject: [PATCH 18/18] Miscellaneous fixes and improvements --- ...lot_circular_elliptical_hough_transform.py | 42 +++++------ skimage/draw/_draw.pyx | 4 +- skimage/transform/_hough_transform.pyx | 75 ++++++++++--------- .../transform/tests/test_hough_transform.py | 71 +++++++++++------- 4 files changed, 105 insertions(+), 87 deletions(-) diff --git a/doc/examples/plot_circular_elliptical_hough_transform.py b/doc/examples/plot_circular_elliptical_hough_transform.py index e1c868fe..7fb67046 100755 --- a/doc/examples/plot_circular_elliptical_hough_transform.py +++ b/doc/examples/plot_circular_elliptical_hough_transform.py @@ -74,7 +74,6 @@ for idx in np.argsort(accums)[::-1][:5]: image[cy, cx] = (220, 20, 20) ax.imshow(image, cmap=plt.cm.gray) -plt.show() """ @@ -96,13 +95,13 @@ an ellipse passes to them. A good match corresponds to high accumulator values. A full description of the algorithm can be found in reference [1]_. - References ---------- .. [1] Xie, Yonghong, and Qiang Ji. "A new efficient ellipse detection method." Pattern Recognition, 2002. Proceedings. 16th International Conference on. Vol. 2. IEEE, 2002 """ + import matplotlib.pyplot as plt from skimage import data, filter, color @@ -110,7 +109,7 @@ from skimage.transform import hough_ellipse from skimage.draw import ellipse_perimeter # Load picture, convert to grayscale and detect edges -image_rgb = data.load('coffee.png')[0:220, 100:450] +image_rgb = data.coffee()[0:220, 160:420] image_gray = color.rgb2gray(image_rgb) edges = filter.canny(image_gray, sigma=2.0, low_threshold=0.55, high_threshold=0.8) @@ -119,30 +118,31 @@ edges = filter.canny(image_gray, sigma=2.0, # The accuracy corresponds to the bin size of a major axis. # The value is chosen in order to get a single high accumulator. # The threshold eliminates low accumulators -accum = hough_ellipse(edges, accuracy=10, threshold=170, min_size=50) -accum.sort(key=lambda x: x[0]) -best = accum[-1] +result = hough_ellipse(edges, accuracy=20, threshold=250, + min_size=100, max_size=120) +result.sort(order='accumulator') + # Estimated parameters for the ellipse -center_y = int(best[1]) -center_x = int(best[2]) -yradius = int(best[3]) -xradius = int(best[4]) -angle = best[5] +best = result[-1] +yc = int(best[1]) +xc = int(best[2]) +a = int(best[3]) +b = int(best[4]) +orientation = best[5] # Draw the ellipse on the original image -cy, cx = ellipse_perimeter(center_y, center_x, - yradius, xradius, orientation=angle) -image_rgb[cy, cx] = (0, 0, 1) +cy, cx = ellipse_perimeter(yc, xc, a, b, orientation) +image_rgb[cy, cx] = (0, 0, 255) # Draw the edge (white) and the resulting ellipse (red) edges = color.gray2rgb(edges) edges[cy, cx] = (250, 0, 0) -fig = plt.subplots(figsize=(10, 6)) -plt.subplot(1, 2, 1) -plt.title('Original picture') -plt.imshow(image_rgb) -plt.subplot(1, 2, 2) -plt.title('Edge (white) and result (red)') -plt.imshow(edges) +fig2, (ax1, ax2) = plt.subplots(ncols=2, nrows=1, figsize=(10, 6)) + +ax1.set_title('Original picture') +ax1.imshow(image_rgb) + +ax2.set_title('Edge (white) and result (red)') +ax2.imshow(edges) plt.show() diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 3d48ce86..c22600a4 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -449,9 +449,9 @@ def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, ---------- cy, cx : int Centre coordinate of ellipse. - yradius, xradius: int + yradius, xradius : int Minor and major semi-axes. ``(x/xradius)**2 + (y/yradius)**2 = 1``. - orientation: double, optional (default 0) + orientation : double, optional (default 0) Major axis orientation in clockwise direction as radians. Returns diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 2ea636fd..29344fa8 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -122,14 +122,15 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, Returns ------- - res : list of tuples [(accumulator, y0, x0, ry, rx, angle)] - Where (y0, x0) is the center, (ry, rx) main axis. - The angle value follows `draw.ellipse_perimeter()` convention. + result : ndarray with fields [(accumulator, y0, x0, a, b, orientation)] + Where ``(yc, xc)`` is the center, ``(a, b)`` the major and minor + axes, respectively. The `orientation` value follows + `skimage.draw.ellipse_perimeter` convention. Examples -------- - >>> img = np.zeros((25, 25), dtype=int) - >>> rr, cc = draw.ellipse_perimeter(10, 10, 6, 8) + >>> img = np.zeros((25, 25), dtype=np.uint8) + >>> rr, cc = ellipse_perimeter(10, 10, 6, 8) >>> img[cc, rr] = 1 >>> result = hough_ellipse(img, threshold=8) [(10, 10.0, 8.0, 6.0, 0.0, 10.0)] @@ -149,47 +150,47 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, if img.ndim != 2: raise ValueError('The input image must be 2D.') - cdef Py_ssize_t[:, :] pixels = np.transpose(np.nonzero(img)) - cdef Py_ssize_t num_pixels = pixels.shape[0] + cdef Py_ssize_t[:, ::1] pixels = np.row_stack(np.nonzero(img)) + cdef Py_ssize_t num_pixels = pixels.shape[1] cdef list acc = list() cdef list results = list() - cdef double bin_size = accuracy**2 + cdef double bin_size = accuracy ** 2 cdef int max_b_squared if max_size is None: if img.shape[0] < img.shape[1]: - max_b_squared = np.round(0.5 * img.shape[0])**2 + max_b_squared = np.round(0.5 * img.shape[0]) ** 2 else: - max_b_squared = np.round(0.5 * img.shape[1])**2 + max_b_squared = np.round(0.5 * img.shape[1]) ** 2 else: max_b_squared = max_size**2 cdef Py_ssize_t p1, p2, p3, p1x, p1y, p2x, p2y, p3x, p3y - cdef double x0, y0, a, b, d, k - cdef double cos_tau_squared, b_squared, f_squared, angle + cdef double xc, yc, a, b, d, k + cdef double cos_tau_squared, b_squared, f_squared, orientation for p1 in range(num_pixels): - p1x = pixels[p1, 1] - p1y = pixels[p1, 0] + p1x = pixels[1, p1] + p1y = pixels[0, p1] for p2 in range(p1): - p2x = pixels[p2, 1] - p2y = pixels[p2, 0] + p2x = pixels[1, p2] + p2y = pixels[0, p2] - # Candidate: center (x0, y0) and main axis a + # Candidate: center (xc, yc) and main axis a a = 0.5 * sqrt((p1x - p2x)**2 + (p1y - p2y)**2) if a > 0.5 * min_size: - x0 = 0.5 * (p1x + p2x) - y0 = 0.5 * (p1y + p2y) + xc = 0.5 * (p1x + p2x) + yc = 0.5 * (p1y + p2y) for p3 in range(num_pixels): - p3x = pixels[p3, 1] - p3y = pixels[p3, 0] + p3x = pixels[1, p3] + p3y = pixels[0, p3] - d = sqrt((p3x - x0)**2 + (p3y - y0)**2) + d = sqrt((p3x - xc)**2 + (p3y - yc)**2) if d > min_size: f_squared = (p3x - p1x)**2 + (p3y - p1y)**2 - cos_tau_squared = ((a**2 + d**2 - f_squared) \ + cos_tau_squared = ((a**2 + d**2 - f_squared) / (2 * a * d))**2 # Consider b2 > 0 and avoid division by zero k = a**2 - d**2 * cos_tau_squared @@ -205,27 +206,29 @@ def hough_ellipse(cnp.ndarray img, int threshold=4, double accuracy=1, hist, bin_edges = np.histogram(acc, bins=bins) hist_max = np.max(hist) if hist_max > threshold: - angle = atan2(p1x - p2x, p1y - p2y) + orientation = atan2(p1x - p2x, p1y - p2y) b = sqrt(bin_edges[hist.argmax()]) # to keep ellipse_perimeter() convention - if angle != 0: - angle = M_PI - angle - # When angle is not in [-pi:pi] + if orientation != 0: + orientation = M_PI - orientation + # When orientation is not in [-pi:pi] # it would mean in ellipse_perimeter() # that a < b. But we keep a > b. - if angle > M_PI: - angle = angle - M_PI / 2. + if orientation > M_PI: + orientation = orientation - M_PI / 2. a, b = b, a results.append((hist_max, # Accumulator - y0, - x0, - a, - b, - angle, - )) + yc, xc, + a, b, + orientation)) acc = [] - return results + return np.array(results, dtype=[('accumulator', np.intp), + ('yc', np.double), + ('xc', np.double), + ('a', np.double), + ('b', np.double), + ('orientation', np.double)]) def hough_line(cnp.ndarray img, diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index 82b538b2..fb19d8c1 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -1,7 +1,5 @@ import numpy as np -from numpy.testing import (assert_almost_equal, - assert_equal, - ) +from numpy.testing import assert_almost_equal, assert_equal import skimage.transform as tf from skimage.draw import line, circle_perimeter, ellipse_perimeter @@ -81,8 +79,10 @@ def test_hough_line_peaks_dist(): img[:, 30] = True img[:, 40] = True hspace, angles, dists = tf.hough_line(img) - assert len(tf.hough_line_peaks(hspace, angles, dists, min_distance=5)[0]) == 2 - assert len(tf.hough_line_peaks(hspace, angles, dists, min_distance=15)[0]) == 1 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_distance=5)[0]) == 2 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_distance=15)[0]) == 1 def test_hough_line_peaks_angle(): @@ -91,18 +91,24 @@ def test_hough_line_peaks_angle(): img[0, :] = True hspace, angles, dists = tf.hough_line(img) - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=90)[0]) == 1 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=45)[0]) == 2 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=90)[0]) == 1 theta = np.linspace(0, np.pi, 100) hspace, angles, dists = tf.hough_line(img, theta) - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=90)[0]) == 1 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=45)[0]) == 2 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=90)[0]) == 1 theta = np.linspace(np.pi / 3, 4. / 3 * np.pi, 100) hspace, angles, dists = tf.hough_line(img, theta) - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 - assert len(tf.hough_line_peaks(hspace, angles, dists, min_angle=90)[0]) == 1 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=45)[0]) == 2 + assert len(tf.hough_line_peaks(hspace, angles, dists, + min_angle=90)[0]) == 1 def test_hough_line_peaks_num(): @@ -165,7 +171,8 @@ def test_hough_ellipse_zero_angle(): assert_equal(best[5], angle) # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -181,7 +188,7 @@ def test_hough_ellipse_non_zero_posangle1(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) @@ -190,7 +197,8 @@ def test_hough_ellipse_non_zero_posangle1(): assert_almost_equal(best[5], angle, decimal=1) # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -206,7 +214,7 @@ def test_hough_ellipse_non_zero_posangle2(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] assert_almost_equal(best[1] / 100., y0 / 100., decimal=1) assert_almost_equal(best[2] / 100., x0 / 100., decimal=1) @@ -215,7 +223,8 @@ def test_hough_ellipse_non_zero_posangle2(): assert_almost_equal(best[5], angle, decimal=1) # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -231,11 +240,12 @@ def test_hough_ellipse_non_zero_posangle3(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -251,11 +261,12 @@ def test_hough_ellipse_non_zero_posangle4(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -271,11 +282,12 @@ def test_hough_ellipse_non_zero_negangle1(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -291,11 +303,12 @@ def test_hough_ellipse_non_zero_negangle2(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -311,11 +324,12 @@ def test_hough_ellipse_non_zero_negangle3(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2) @@ -331,11 +345,12 @@ def test_hough_ellipse_non_zero_negangle4(): rr, cc = ellipse_perimeter(y0, x0, ry, rx, orientation=angle) img[rr, cc] = 1 result = tf.hough_ellipse(img, threshold=15, accuracy=3) - result.sort(key=lambda x:x[0]) + result.sort(order='accumulator') best = result[-1] # Check if I re-draw the ellipse, points are the same! # ie check API compatibility between hough_ellipse and ellipse_perimeter - rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), orientation=best[5]) + rr2, cc2 = ellipse_perimeter(y0, x0, int(best[3]), int(best[4]), + orientation=best[5]) assert_equal(rr, rr2) assert_equal(cc, cc2)