diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 509796bb..7c77b423 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -189,7 +189,7 @@ def circle(double cy, double cx, double radius, shape=None): return ellipse(cy, cx, radius, radius, shape) -def circle_perimeter(int cy, int cx, int radius): +def circle_perimeter(int cy, int cx, int radius, method='bresenham'): """Generate circle perimeter coordinates. Parameters @@ -198,6 +198,10 @@ def circle_perimeter(int cy, int cx, int radius): Centre coordinate of circle. radius: int Radius of circle. + method : {'bresenham', 'andres'}, optional + bresenham : Bresenham method + andres : Andres method + Returns ------- @@ -206,6 +210,19 @@ def circle_perimeter(int cy, int cx, int radius): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. + Notes + ----- + Andres method presents the advantage that concentric + circles create a disc whereas Bresenham can make holes. There + is also less distortions when Andres circles are rotated. + Bresenham method is also known as midpoint circle algorithm. + + References + ---------- + .. [1] J.E. Bresenham, "Algorithm for computer control of a digital + plotter", 4 (1965) 25-30. + .. [2] E. Andres, "Discrete circles, rings and spheres", 18 (1994) 695-706. + """ cdef list rr = list() @@ -213,17 +230,36 @@ def circle_perimeter(int cy, int cx, int radius): cdef int x = 0 cdef int y = radius - cdef int d = 3 - 2 * radius + cdef int d = 0 + if method == 'bresenham': + d = 3 - 2 * radius + elif method == 'andres': + d = radius - 1 + else: + raise ValueError('Wrong method') while y >= x: rr.extend([y, -y, y, -y, x, -x, x, -x]) cc.extend([x, x, -x, -x, y, y, -y, -y]) - if d < 0: - d += 4 * x + 6 - else: - d += 4 * (x - y) + 10 - y -= 1 - x += 1 + + if method == 'bresenham': + if d < 0: + d += 4 * x + 6 + else: + d += 4 * (x - y) + 10 + y -= 1 + x += 1 + elif method == 'andres': + if d >= 2 * (x - 1): + d = d - 2 * x + x = x + 1 + elif d <= 2 * (radius - y): + d = d + 2 * y - 1 + y = y - 1 + else: + d = d + 2 * (y - x - 1) + y = y - 1 + x = x + 1 return np.array(rr) + cy, np.array(cc) + cx diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 04cbae92..b2325015 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -150,14 +150,14 @@ def test_circle(): assert_array_equal(img, img_) -def test_circle_perimeter(): +def test_circle_perimeter_bresenham(): img = np.zeros((15, 15), 'uint8') - rr, cc = circle_perimeter(7, 7, 0) + rr, cc = circle_perimeter(7, 7, 0, method='bresenham') img[rr, cc] = 1 assert(np.sum(img) == 1) img = np.zeros((17, 15), 'uint8') - rr, cc = circle_perimeter(7, 7, 7) + rr, cc = circle_perimeter(7, 7, 7, method='bresenham') img[rr, cc] = 1 img_ = np.array( [[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], @@ -180,6 +180,36 @@ def test_circle_perimeter(): ) assert_array_equal(img, img_) +def test_circle_perimeter_andres(): + img = np.zeros((15, 15), 'uint8') + rr, cc = circle_perimeter(7, 7, 0, method='andres') + img[rr, cc] = 1 + assert(np.sum(img) == 1) + + img = np.zeros((17, 15), 'uint8') + rr, cc = circle_perimeter(7, 7, 7, method='andres') + img[rr, cc] = 1 + img_ = np.array( + [[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], + [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0]] + ) + assert_array_equal(img, img_) + def test_ellipse(): img = np.zeros((15, 15), 'uint8')