From a997195881756d04839b4eba566572cd96d59fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 9 Feb 2013 20:00:19 +0100 Subject: [PATCH 01/14] hough transform for circles --- skimage/transform/_hough_transform.pyx | 86 +++++++++++++++++++++++++- skimage/transform/hough_transform.py | 32 +++++++++- 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 662b3b88..6164f304 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -7,7 +7,7 @@ import numpy as np cimport numpy as np from libc.math cimport abs, fabs, sqrt, ceil from libc.stdlib cimport rand - +from skimage.draw import circle_perimeter cdef double PI_2 = 1.5707963267948966 cdef double NEG_PI_2 = -PI_2 @@ -17,6 +17,90 @@ cdef inline Py_ssize_t round(double r): return ((r + 0.5) if (r > 0.0) else (r - 0.5)) +@cython.boundscheck(False) +def _hough_circle(np.ndarray img, \ + np.ndarray[ndim=1, dtype=np.npy_intp] radius, \ + normalize=True): + + if img.ndim != 2: + raise ValueError('The input image must be 2D.') + + # compute the nonzero indexes + cdef np.ndarray[ndim=1, dtype=np.npy_intp] x, y + y, x = np.PyArray_Nonzero(img) + + # Offset the image + max_radius = radius.max() + x = x + max_radius + y = y + max_radius + + cdef list H = list() + + for rad in radius: + # Accumulator + out = np.zeros((img.shape[0] + 2 * max_radius, img.shape[1] + 2 * max_radius)) + + # Store in memory the circle of given radius + # centered at (0,0) + circle_x, circle_y = circle_perimeter(0, 0, rad) + + # For each non zero pixel + for (px, py) in zip(x, y): + # Plug the circle at (px, py), + # its coordinates are (tx, ty) + tx = circle_x + px + ty = circle_y + py + out[tx, ty] += 1 + + if normalize: + out = out / len(circle_x) + + H.append(out) + return np.array(H) + + +@cython.boundscheck(False) +def _hough_circle(np.ndarray img, \ + np.ndarray[ndim=1, dtype=np.npy_intp] radius, \ + normalize=True): + + if img.ndim != 2: + raise ValueError('The input image must be 2D.') + + # compute the nonzero indexes + cdef np.ndarray[ndim=1, dtype=np.npy_intp] x, y + y, x = np.PyArray_Nonzero(img) + + # Offset the image + max_radius = radius.max() + x = x + max_radius + y = y + max_radius + + cdef list H = list() + + for rad in radius: + # Accumulator + out = np.zeros((img.shape[0] + 2 * max_radius, img.shape[1] + 2 * max_radius)) + + # Store in memory the circle of given radius + # centered at (0,0) + circle_x, circle_y = circle_perimeter(0, 0, rad) + + # For each non zero pixel + for (px, py) in zip(x, y): + # Plug the circle at (px, py), + # its coordinates are (tx, ty) + tx = circle_x + px + ty = circle_y + py + out[tx, ty] += 1 + + if normalize: + out = out / len(circle_x) + + H.append(out) + return np.array(H) + + def _hough(np.ndarray img, np.ndarray[ndim=1, dtype=np.double_t] theta=None): if img.ndim != 2: diff --git a/skimage/transform/hough_transform.py b/skimage/transform/hough_transform.py index 05d5cfbe..88ef4b83 100644 --- a/skimage/transform/hough_transform.py +++ b/skimage/transform/hough_transform.py @@ -1,4 +1,4 @@ -__all__ = ['hough', 'hough_peaks', 'probabilistic_hough'] +__all__ = ['hough', 'hough_line', 'hough_circle', 'hough_peaks', 'probabilistic_hough'] from itertools import izip as zip @@ -96,8 +96,15 @@ def probabilistic_hough(img, threshold=10, line_length=50, line_gap=10, """ return _probabilistic_hough(img, threshold, line_length, line_gap, theta) +from skimage._shared.utils import deprecated +@deprecated('hough_line') def hough(img, theta=None): + return hough_line(img, theta) + +from ._hough_transform import _hough_circle + +def hough_line(img, theta=None): """Perform a straight line Hough transform. Parameters @@ -138,6 +145,29 @@ def hough(img, theta=None): """ return _hough(img, theta) +def hough_circle(img, radius, normalize=True): + """Perform a circle Hough transform. + + Parameters + ---------- + img : (M, N) ndarray + Input image with nonzero values representing edges. + radius : ndarray + Radii at which to compute the Hough transform. + normalize : boolean + Normalize the accumulator with the number + of pixels used to draw the radius + + Returns + ------- + H : 3D ndarray (radius index, (M, N) ndarray) + Hough transform accumulator for each radius + + Examples + -------- + + """ + return _hough_circle(img, radius, normalize) def hough_peaks(hspace, angles, dists, min_distance=10, min_angle=10, threshold=None, num_peaks=np.inf): From 12c1bf8883153fa2b75469210b450050f795d695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 9 Feb 2013 20:00:41 +0100 Subject: [PATCH 02/14] add unittest for hough circle --- .../transform/tests/test_hough_transform.py | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index a38e6e76..f829b1bb 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -4,6 +4,7 @@ from numpy.testing import * import skimage.transform as tf import skimage.transform.hough_transform as ht from skimage.transform import probabilistic_hough +from skimage.draw import circle_perimeter def append_desc(func, description): @@ -14,8 +15,6 @@ def append_desc(func, description): return func -from skimage.transform import * - def test_hough(): # Generate a test image @@ -23,7 +22,7 @@ def test_hough(): for i in range(25, 75): img[100 - i, i] = 1 - out, angles, d = tf.hough(img) + out, angles, d = tf.hough_line(img) y, x = np.where(out == out.max()) dist = d[y[0]] @@ -37,7 +36,7 @@ def test_hough_angles(): img = np.zeros((10, 10)) img[0, 0] = 1 - out, angles, d = tf.hough(img, np.linspace(0, 360, 10)) + out, angles, d = tf.hough_line(img, np.linspace(0, 360, 10)) assert_equal(len(angles), 10) @@ -76,7 +75,7 @@ def test_hough_peaks_dist(): img = np.zeros((100, 100), dtype=np.bool_) img[:, 30] = True img[:, 40] = True - hspace, angles, dists = tf.hough(img) + hspace, angles, dists = tf.hough_line(img) assert len(tf.hough_peaks(hspace, angles, dists, min_distance=5)[0]) == 2 assert len(tf.hough_peaks(hspace, angles, dists, min_distance=15)[0]) == 1 @@ -86,17 +85,17 @@ def test_hough_peaks_angle(): img[:, 0] = True img[0, :] = True - hspace, angles, dists = tf.hough(img) + hspace, angles, dists = tf.hough_line(img) assert len(tf.hough_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 assert len(tf.hough_peaks(hspace, angles, dists, min_angle=90)[0]) == 1 theta = np.linspace(0, np.pi, 100) - hspace, angles, dists = tf.hough(img, theta) + hspace, angles, dists = tf.hough_line(img, theta) assert len(tf.hough_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 assert len(tf.hough_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(img, theta) + hspace, angles, dists = tf.hough_line(img, theta) assert len(tf.hough_peaks(hspace, angles, dists, min_angle=45)[0]) == 2 assert len(tf.hough_peaks(hspace, angles, dists, min_angle=90)[0]) == 1 @@ -105,10 +104,25 @@ def test_hough_peaks_num(): img = np.zeros((100, 100), dtype=np.bool_) img[:, 30] = True img[:, 40] = True - hspace, angles, dists = tf.hough(img) + hspace, angles, dists = tf.hough_line(img) assert len(tf.hough_peaks(hspace, angles, dists, min_distance=0, min_angle=0, num_peaks=1)[0]) == 1 +def test_houghcircle(): + # Prepare picture + img = np.zeros((100, 100), dtype=int) + radius = 20 + x_0, y_0 = (50, 50) + x, y = circle_perimeter(y_0, x_0, radius) + img[y, x] = 1 + + out = tf.hough_circle(img, np.array([radius])) + + y, x = np.where(out[0] == out[0].max()) + # Offset for x_0, y_0 + assert_equal(x[0], x_0 + radius) + assert_equal(y[0], y_0 + radius) + if __name__ == "__main__": run_module_suite() From ffeb64572f9f3bc432aa4b540128abd8ae256bf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 9 Feb 2013 20:01:00 +0100 Subject: [PATCH 03/14] update contrib --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 38e2ea32..80cb92f0 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -132,6 +132,7 @@ - François Boulogne Andres Method for circle perimeter, ellipse perimeter drawing. + Hough transform for circles - Thouis Jones Vectorized operators for arrays of 16-bit ints. From 88eaaad9c797765fddac76eb5c5ae87ae4cda64f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Mon, 11 Feb 2013 17:31:23 +0100 Subject: [PATCH 04/14] add blank line --- skimage/transform/_hough_transform.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 6164f304..3c39d815 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -7,6 +7,7 @@ import numpy as np cimport numpy as np from libc.math cimport abs, fabs, sqrt, ceil from libc.stdlib cimport rand + from skimage.draw import circle_perimeter cdef double PI_2 = 1.5707963267948966 From 5161687730be0222ae9448697bd9a11dc6ef999b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 16 Feb 2013 08:11:30 +0100 Subject: [PATCH 05/14] various minor fixes --- skimage/transform/_hough_transform.pyx | 61 ++++++++------------------ skimage/transform/hough_transform.py | 7 +-- 2 files changed, 20 insertions(+), 48 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 3c39d815..c274ed0d 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -22,55 +22,30 @@ cdef inline Py_ssize_t round(double r): def _hough_circle(np.ndarray img, \ np.ndarray[ndim=1, dtype=np.npy_intp] radius, \ normalize=True): + """Perform a circular Hough transform. + Parameters + ---------- + img : (M, N) ndarray + Input image with nonzero values representing edges. + radius : ndarray + Radii at which to compute the Hough transform. + normalize : boolean, optional + Normalize the accumulator with the number + of pixels used to draw the radius + + Returns + ------- + H : 3D ndarray (radius index, (M, N) ndarray) + Hough transform accumulator for each radius + + """ if img.ndim != 2: raise ValueError('The input image must be 2D.') # compute the nonzero indexes cdef np.ndarray[ndim=1, dtype=np.npy_intp] x, y - y, x = np.PyArray_Nonzero(img) - - # Offset the image - max_radius = radius.max() - x = x + max_radius - y = y + max_radius - - cdef list H = list() - - for rad in radius: - # Accumulator - out = np.zeros((img.shape[0] + 2 * max_radius, img.shape[1] + 2 * max_radius)) - - # Store in memory the circle of given radius - # centered at (0,0) - circle_x, circle_y = circle_perimeter(0, 0, rad) - - # For each non zero pixel - for (px, py) in zip(x, y): - # Plug the circle at (px, py), - # its coordinates are (tx, ty) - tx = circle_x + px - ty = circle_y + py - out[tx, ty] += 1 - - if normalize: - out = out / len(circle_x) - - H.append(out) - return np.array(H) - - -@cython.boundscheck(False) -def _hough_circle(np.ndarray img, \ - np.ndarray[ndim=1, dtype=np.npy_intp] radius, \ - normalize=True): - - if img.ndim != 2: - raise ValueError('The input image must be 2D.') - - # compute the nonzero indexes - cdef np.ndarray[ndim=1, dtype=np.npy_intp] x, y - y, x = np.PyArray_Nonzero(img) + y, x = np.nonzero(img) # Offset the image max_radius = radius.max() diff --git a/skimage/transform/hough_transform.py b/skimage/transform/hough_transform.py index 88ef4b83..17b76826 100644 --- a/skimage/transform/hough_transform.py +++ b/skimage/transform/hough_transform.py @@ -146,7 +146,7 @@ def hough_line(img, theta=None): return _hough(img, theta) def hough_circle(img, radius, normalize=True): - """Perform a circle Hough transform. + """Perform a circular Hough transform. Parameters ---------- @@ -154,7 +154,7 @@ def hough_circle(img, radius, normalize=True): Input image with nonzero values representing edges. radius : ndarray Radii at which to compute the Hough transform. - normalize : boolean + normalize : boolean, optional Normalize the accumulator with the number of pixels used to draw the radius @@ -163,9 +163,6 @@ def hough_circle(img, radius, normalize=True): H : 3D ndarray (radius index, (M, N) ndarray) Hough transform accumulator for each radius - Examples - -------- - """ return _hough_circle(img, radius, normalize) From 99e29973379b9351e1b71b8314400123ea3947fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 16 Feb 2013 08:33:41 +0100 Subject: [PATCH 06/14] declare cython variables --- skimage/transform/_hough_transform.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index c274ed0d..98ba1bff 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -48,11 +48,13 @@ def _hough_circle(np.ndarray img, \ y, x = np.nonzero(img) # Offset the image - max_radius = radius.max() + cdef int max_radius = radius.max() x = x + max_radius y = y + max_radius cdef list H = list() + cdef int px, py + cdef np.ndarray[ndim=1, dtype=np.npy_intp] tx, ty for rad in radius: # Accumulator From de6e63de2e711af178787c4ef78f00996f30257f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 16 Feb 2013 09:30:26 +0100 Subject: [PATCH 07/14] use a single array --- skimage/transform/_hough_transform.pyx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 98ba1bff..fb36c231 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -52,13 +52,13 @@ def _hough_circle(np.ndarray img, \ x = x + max_radius y = y + max_radius - cdef list H = list() cdef int px, py cdef np.ndarray[ndim=1, dtype=np.npy_intp] tx, ty + cdef np.ndarray acc = np.zeros((radius.size, + img.shape[0] + 2 * max_radius, + img.shape[1] + 2 * max_radius)) - for rad in radius: - # Accumulator - out = np.zeros((img.shape[0] + 2 * max_radius, img.shape[1] + 2 * max_radius)) + for i, rad in enumerate(radius): # Store in memory the circle of given radius # centered at (0,0) @@ -70,13 +70,12 @@ def _hough_circle(np.ndarray img, \ # its coordinates are (tx, ty) tx = circle_x + px ty = circle_y + py - out[tx, ty] += 1 + acc[i, tx, ty] += 1 if normalize: - out = out / len(circle_x) + acc[i] = acc[i] / len(circle_x) - H.append(out) - return np.array(H) + return acc def _hough(np.ndarray img, np.ndarray[ndim=1, dtype=np.double_t] theta=None): From 928468b999df891116f3258d0058ab38b798dba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Mon, 18 Feb 2013 19:23:08 +0100 Subject: [PATCH 08/14] variable in cdef --- 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 fb36c231..83be34ad 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -53,7 +53,7 @@ def _hough_circle(np.ndarray img, \ y = y + max_radius cdef int px, py - cdef np.ndarray[ndim=1, dtype=np.npy_intp] tx, ty + cdef np.ndarray[ndim=1, dtype=np.npy_intp] tx, ty, circle_x, circle_y cdef np.ndarray acc = np.zeros((radius.size, img.shape[0] + 2 * max_radius, img.shape[1] + 2 * max_radius)) From ecb762c9e41ddf17a22c531235711ed2a1e435ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 23 Feb 2013 11:08:23 +0100 Subject: [PATCH 09/14] bugfix dim + unittest --- skimage/transform/_hough_transform.pyx | 2 +- skimage/transform/tests/test_hough_transform.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 83be34ad..35426cad 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -45,7 +45,7 @@ def _hough_circle(np.ndarray img, \ # compute the nonzero indexes cdef np.ndarray[ndim=1, dtype=np.npy_intp] x, y - y, x = np.nonzero(img) + x, y = np.nonzero(img) # Offset the image cdef int max_radius = radius.max() diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index f829b1bb..00427332 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -111,15 +111,15 @@ def test_hough_peaks_num(): def test_houghcircle(): # Prepare picture - img = np.zeros((100, 100), dtype=int) + img = np.zeros((120, 100), dtype=int) radius = 20 - x_0, y_0 = (50, 50) + x_0, y_0 = (99, 50) x, y = circle_perimeter(y_0, x_0, radius) img[y, x] = 1 out = tf.hough_circle(img, np.array([radius])) - y, x = np.where(out[0] == out[0].max()) + x, y = np.where(out[0] == out[0].max()) # Offset for x_0, y_0 assert_equal(x[0], x_0 + radius) assert_equal(y[0], y_0 + radius) From 5572d16cb221f08fcbd97880447653af77f0aefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 23 Feb 2013 13:49:21 +0100 Subject: [PATCH 10/14] add example for cicular hough transform --- doc/examples/plot_circular_hough_transform.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 doc/examples/plot_circular_hough_transform.py diff --git a/doc/examples/plot_circular_hough_transform.py b/doc/examples/plot_circular_hough_transform.py new file mode 100755 index 00000000..0caee3c4 --- /dev/null +++ b/doc/examples/plot_circular_hough_transform.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +# Author: Francois Boulogne +# License: GPL + +""" +======================== +Circular Hough Transform +======================== + + + +""" + + +import numpy as np +import matplotlib.pyplot as plt +import matplotlib.patches as mpatches + +from skimage import data, filter +from skimage.transform import hough_circle +from skimage.feature import peak_local_max + +# Load picture and detect edges +image = data.coins()[0:95, 70:370] +edges = filter.canny(filter.sobel(image), sigma=2.8) + + +fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6)) +ax.imshow(image, cmap=plt.cm.gray) + +# Detect two radii +radii = np.array([21, 25]) +hough_res = hough_circle(edges, radii) + +for radius, h in zip(radii, hough_res): + # For each radius, keep two circles + maxima = peak_local_max(h, num_peaks=2) + for maximum in maxima: + center_x, center_y = maximum - radii.max() + circ = mpatches.Circle((center_y, center_x), radius, + fill=False, edgecolor='red', linewidth=2) + ax.add_patch(circ) + +plt.show() From de2fa28b77591bbebe7369b43c8901a39cc878f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 24 Feb 2013 10:47:12 +0100 Subject: [PATCH 11/14] Docstring + J.S. modifications --- CONTRIBUTORS.txt | 2 +- doc/examples/plot_circular_hough_transform.py | 61 +++++++++++++------ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 80cb92f0..fd0656db 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -132,7 +132,7 @@ - François Boulogne Andres Method for circle perimeter, ellipse perimeter drawing. - Hough transform for circles + Circular Hough Transform - Thouis Jones Vectorized operators for arrays of 16-bit ints. diff --git a/doc/examples/plot_circular_hough_transform.py b/doc/examples/plot_circular_hough_transform.py index 0caee3c4..cc4ea664 100755 --- a/doc/examples/plot_circular_hough_transform.py +++ b/doc/examples/plot_circular_hough_transform.py @@ -1,14 +1,32 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -# Author: Francois Boulogne -# License: GPL - """ ======================== Circular Hough Transform ======================== +The Hough transform in its simplest form is a `method to detect +straight lines `__ +but it can also be used to detect circles. +In the following example, the Hough transform is used to detect +coin positions and match their edges. We provide a range of +plausible radii. For each radius, two circles are extracted and +we finally keep the five most prominent candidates. +The result shows that coin positions are well-detected. + + +Algorithm overview +------------------ + +Given a black circle on a white background, we first guess its +radius (or a range of radii) to construct a new circle. +This circle is applied on each black pixel of the original picture +and the coordinates of this circle are voting in an accumulator. +From this geometrical construction, the original circle center +position receives the highest score. + +Note that the accumulator size is built to be larger than the +original picture in order to detect centers outside the frame. +Its size is extended by two times the larger radius. """ @@ -23,23 +41,32 @@ from skimage.feature import peak_local_max # Load picture and detect edges image = data.coins()[0:95, 70:370] -edges = filter.canny(filter.sobel(image), sigma=2.8) - +edges = filter.canny(image, sigma=3, low_threshold=10, high_threshold=50) fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6)) ax.imshow(image, cmap=plt.cm.gray) # Detect two radii -radii = np.array([21, 25]) -hough_res = hough_circle(edges, radii) +hough_radii = np.arange(15, 30, 2) +hough_res = hough_circle(edges, hough_radii) -for radius, h in zip(radii, hough_res): - # For each radius, keep two circles - maxima = peak_local_max(h, num_peaks=2) - for maximum in maxima: - center_x, center_y = maximum - radii.max() - circ = mpatches.Circle((center_y, center_x), radius, - fill=False, edgecolor='red', linewidth=2) - ax.add_patch(circ) +centers = [] +accums = [] +radii = [] + +for radius, h in zip(hough_radii, hough_res): + # For each radius, extract two circles + peaks = peak_local_max(h, num_peaks=2) + centers.extend(peaks - hough_radii.max()) + accums.extend(h[peaks[:, 0], peaks[:, 1]]) + radii.extend([radius, radius]) + +# Draw the most prominent 5 circles +for idx in np.argsort(accums)[::-1][:5]: + center_x, center_y = centers[idx] + radius = radii[idx] + circ = mpatches.Circle((center_y, center_x), radius, + fill=False, edgecolor='red', linewidth=2) + ax.add_patch(circ) plt.show() From 86f777ae3de257c1eca82a2edc9a079a63fd9455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sat, 9 Feb 2013 20:00:19 +0100 Subject: [PATCH 12/14] hough transform for circles --- skimage/transform/_hough_transform.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/transform/_hough_transform.pyx b/skimage/transform/_hough_transform.pyx index 35426cad..e1c06d77 100644 --- a/skimage/transform/_hough_transform.pyx +++ b/skimage/transform/_hough_transform.pyx @@ -59,7 +59,6 @@ def _hough_circle(np.ndarray img, \ img.shape[1] + 2 * max_radius)) for i, rad in enumerate(radius): - # Store in memory the circle of given radius # centered at (0,0) circle_x, circle_y = circle_perimeter(0, 0, rad) From 0451a2e17b775183e8a547c2a1125d20d45c0331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 24 Feb 2013 16:27:40 +0100 Subject: [PATCH 13/14] use draw instead of mpl patches --- doc/examples/plot_circular_hough_transform.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/examples/plot_circular_hough_transform.py b/doc/examples/plot_circular_hough_transform.py index cc4ea664..f7490051 100755 --- a/doc/examples/plot_circular_hough_transform.py +++ b/doc/examples/plot_circular_hough_transform.py @@ -38,13 +38,13 @@ import matplotlib.patches as mpatches from skimage import data, filter from skimage.transform import hough_circle from skimage.feature import peak_local_max +from skimage.draw import circle_perimeter # Load picture and detect edges image = data.coins()[0:95, 70:370] edges = filter.canny(image, sigma=3, low_threshold=10, high_threshold=50) fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(6, 6)) -ax.imshow(image, cmap=plt.cm.gray) # Detect two radii hough_radii = np.arange(15, 30, 2) @@ -65,8 +65,8 @@ for radius, h in zip(hough_radii, hough_res): for idx in np.argsort(accums)[::-1][:5]: center_x, center_y = centers[idx] radius = radii[idx] - circ = mpatches.Circle((center_y, center_x), radius, - fill=False, edgecolor='red', linewidth=2) - ax.add_patch(circ) + cx, cy = circle_perimeter(center_y, center_x, radius) + image[cy, cx] = 0 +ax.imshow(image, cmap=plt.cm.gray) plt.show() From 609ce322d9ee9f1eda9f22dbdb457fca9d7d0d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 24 Feb 2013 16:45:28 +0100 Subject: [PATCH 14/14] remove import mpl patches --- doc/examples/plot_circular_hough_transform.py | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/examples/plot_circular_hough_transform.py b/doc/examples/plot_circular_hough_transform.py index f7490051..2574a1ac 100755 --- a/doc/examples/plot_circular_hough_transform.py +++ b/doc/examples/plot_circular_hough_transform.py @@ -33,7 +33,6 @@ Its size is extended by two times the larger radius. import numpy as np import matplotlib.pyplot as plt -import matplotlib.patches as mpatches from skimage import data, filter from skimage.transform import hough_circle