From ed8521175bbdc2d82f782d5aeb78d1b8e6deb556 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 10:04:20 +0200 Subject: [PATCH 01/22] import Fedor's contribution --- CONTRIBUTORS.txt | 3 ++ doc/examples/plot_shapes.py | 4 ++ skimage/draw/_draw.pyx | 82 +++++++++++++++++++++++++++---------- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 689f552b..37e619fe 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -154,3 +154,6 @@ - Riaan van den Dool skimage.io plugin: GDAL + +- Fedor Morozov + Drawing: Wu's anti-aliased circle diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 507452ef..ec385640 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -48,6 +48,10 @@ img[rr,cc,2] = 255 rr, cc = circle_perimeter(120, 400, 15) img[rr, cc, :] = (255, 0, 0) +# anti-aliased circle +rr, cc, val = circle_perimeter(120, 400, 70, 'wu') +img[rr, cc, 1] = val * 255 + # ellipses rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 4.) img[rr, cc, :] = (255, 0, 255) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 132ba1d4..e3dab793 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -175,9 +175,10 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, Centre coordinate of circle. radius: int Radius of circle. - method : {'bresenham', 'andres'}, optional + method : {'bresenham', 'andres', 'wu'}, optional bresenham : Bresenham method (default) andres : Andres method + wu : Wu's method Returns ------- @@ -192,6 +193,8 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, 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. + Wu's method draws anti-aliased circle. This implementation doesn't use + lookup table optimization. References ---------- @@ -199,6 +202,7 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, plotter", 4 (1965) 25-30. .. [2] E. Andres, "Discrete circles, rings and spheres", 18 (1994) 695-706. + .. [3] X. Wu, "Fast anti-aliased circle generation", 2 (1995) 446-450. Examples -------- @@ -222,10 +226,15 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, cdef list rr = list() cdef list cc = list() + cdef list val = list() cdef Py_ssize_t x = 0 cdef Py_ssize_t y = radius cdef Py_ssize_t d = 0 + + cdef double dceil = 0 + cdef double dceil_prev = 0 + cdef char cmethod if method == 'bresenham': d = 3 - 2 * radius @@ -233,33 +242,62 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, elif method == 'andres': d = radius - 1 cmethod = 'a' + elif method == 'wu': + cmethod = 'w' 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 cmethod == 'a' or cmethod == 'b': + while y >= x: + rr.extend([y, -y, y, -y, x, -x, x, -x]) + cc.extend([x, x, -x, -x, y, y, -y, -y]) - if cmethod == 'b': - if d < 0: - d += 4 * x + 6 - else: - d += 4 * (x - y) + 10 - y -= 1 + if cmethod == 'b': + if d < 0: + d += 4 * x + 6 + else: + d += 4 * (x - y) + 10 + y -= 1 + x += 1 + elif cmethod == 'a': + 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, dtype=np.intp) + cy, + np.array(cc, dtype=np.intp) + cx) + + elif cmethod == 'w': + dceil_prev = 0 + + rr.extend([y, x, y, x, -y, -x, -y, -x]) + cc.extend([x, y, -x, -y, x, y, -x, -y]) + val.extend([1] * 8) + + while y > x + 1: x += 1 - elif cmethod == 'a': - 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 + dceil = math.sqrt(radius**2 - x**2) + dceil = math.ceil(dceil) - dceil + if dceil < dceil_prev: + y -= 1 + rr.extend([y, y - 1, x, x, y, y - 1, x, x]) + cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) - return np.array(rr, dtype=np.intp) + cy, np.array(cc, dtype=np.intp) + cx + rr.extend([-y, 1 - y, -x, -x, -y, 1 - y, -x, -x]) + cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) + + val.extend([1 - dceil, dceil] * 8) + dceil_prev = dceil + + return (np.array(rr, dtype=np.intp) + cy, + np.array(cc, dtype=np.intp) + cx, + np.array(val, dtype=np.float)) def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, From 36e23b106fa9435cf2ea3abea9f1ace1d76d6894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 10:31:50 +0200 Subject: [PATCH 02/22] TEST: add unittest and check with travis --- skimage/draw/tests/test_draw.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index f15fba01..d3f42c7d 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -215,6 +215,39 @@ def test_circle_perimeter_andres(): assert_array_equal(img, img_) +def test_circle_perimeter_wu(): + img = np.zeros((15, 15), 'uint8') + rr, cc = circle_perimeter(7, 7, 0, method='wu') + img[rr, cc] = 1 + assert(np.sum(img) == 1) + assert(img[7][7] == 1) + + img = np.zeros((17, 15), 'uint8') + rr, cc = circle_perimeter(7, 7, 7, method='wu') + img[rr, cc] = 1 + print(img) + 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') From 4c14e86952a23119234d7be0778b7b95ea609b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 10:51:20 +0200 Subject: [PATCH 03/22] DOC: update return according to wu's method --- skimage/draw/_draw.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index e3dab793..75c2210c 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -183,10 +183,16 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, Returns ------- rr, cc : (N,) ndarray of int + Bresenham and Andres' method: Indices of pixels that belong to the circle perimeter. May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. + rr, cc, val : (N,) ndarray of int + Wu' method: + Indices of pixels and intensity values. + ``img[rr, cc] = val``. + Notes ----- Andres method presents the advantage that concentric From 62dbd7b9b9155c7fe63177503ee9b12900666fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 10:51:34 +0200 Subject: [PATCH 04/22] TEST: fix returned tuple --- skimage/draw/tests/test_draw.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index d3f42c7d..802bdfc0 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -217,14 +217,14 @@ def test_circle_perimeter_andres(): def test_circle_perimeter_wu(): img = np.zeros((15, 15), 'uint8') - rr, cc = circle_perimeter(7, 7, 0, method='wu') + rr, cc, val = circle_perimeter(7, 7, 0, method='wu') img[rr, cc] = 1 assert(np.sum(img) == 1) assert(img[7][7] == 1) img = np.zeros((17, 15), 'uint8') - rr, cc = circle_perimeter(7, 7, 7, method='wu') - img[rr, cc] = 1 + rr, cc, val = circle_perimeter(7, 7, 7, method='wu') + img[rr, cc] = val print(img) img_ = np.array( [[0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], From 62240c1f111afe76ed283c26ef61152747bbf11b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 11:01:10 +0200 Subject: [PATCH 05/22] TEST: fix unittest --- skimage/draw/tests/test_draw.py | 43 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 802bdfc0..8dac897a 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -222,29 +222,28 @@ def test_circle_perimeter_wu(): assert(np.sum(img) == 1) assert(img[7][7] == 1) - img = np.zeros((17, 15), 'uint8') - rr, cc, val = circle_perimeter(7, 7, 7, method='wu') - img[rr, cc] = val - print(img) + img = np.zeros((17, 17), 'uint8') + rr, cc, val = circle_perimeter(8, 8, 7, method='wu') + img[rr, cc] = val * 255 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]] - ) + [[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, 0, 0, 82, 180, 236, 255, 236, 180, 82, 0, 0, 0, 0, 0], + [ 0, 0, 0, 0, 189, 172, 74, 18, 0, 18, 74, 172, 189, 0, 0, 0, 0], + [ 0, 0, 0, 229, 25, 0, 0, 0, 0, 0, 0, 0, 25, 229, 0, 0, 0], + [ 0, 0, 189, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 189, 0, 0], + [ 0, 82, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 82, 0], + [ 0, 180, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 180, 0], + [ 0, 236, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 236, 0], + [ 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0], + [ 0, 236, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 236, 0], + [ 0, 180, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74, 180, 0], + [ 0, 82, 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 82, 0], + [ 0, 0, 189, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 189, 0, 0], + [ 0, 0, 0, 229, 25, 0, 0, 0, 0, 0, 0, 0, 25, 229, 0, 0, 0], + [ 0, 0, 0, 0, 189, 172, 74, 18, 0, 18, 74, 172, 189, 0, 0, 0, 0], + [ 0, 0, 0, 0, 0, 82, 180, 236, 255, 236, 180, 82, 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_) From dcf02235a4907599cbdd1d556d5a12a8cd166fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 20:37:13 +0200 Subject: [PATCH 06/22] DOC: fix refs --- skimage/draw/_draw.pyx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 75c2210c..90ba001c 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -189,7 +189,7 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, ``img[rr, cc] = 1``. rr, cc, val : (N,) ndarray of int - Wu' method: + Wu's method: Indices of pixels and intensity values. ``img[rr, cc] = val``. @@ -205,10 +205,11 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, 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. - .. [3] X. Wu, "Fast anti-aliased circle generation", 2 (1995) 446-450. + plotter", IBM Systems journal, 4 (1965) 25-30. + .. [2] E. Andres, "Discrete circles, rings and spheres", Computers & + Graphics, 18 (1994) 695-706. + .. [3] X. Wu, "An efficient antialiasing technique", In ACM SIGGRAPH + Computer Graphics, 25 (1991) 143-152. Examples -------- From 260649482835a01a08af0caea1c147f40596e24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Sun, 25 Aug 2013 21:13:40 +0200 Subject: [PATCH 07/22] MAINT: put AA method in draw.*_aa --- doc/examples/plot_shapes.py | 5 +- skimage/draw/__init__.py | 5 +- skimage/draw/_draw.pyx | 143 +++++++++++++++++++------------- skimage/draw/tests/test_draw.py | 12 +-- 4 files changed, 97 insertions(+), 68 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index ec385640..2d957977 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -14,7 +14,8 @@ This example shows how to fill several different shapes: import numpy as np import matplotlib.pyplot as plt -from skimage.draw import line, polygon, circle, circle_perimeter, \ +from skimage.draw import line, polygon, circle, \ + circle_perimeter, circle_perimeter_aa, \ ellipse, ellipse_perimeter import numpy as np import math @@ -49,7 +50,7 @@ rr, cc = circle_perimeter(120, 400, 15) img[rr, cc, :] = (255, 0, 0) # anti-aliased circle -rr, cc, val = circle_perimeter(120, 400, 70, 'wu') +rr, cc, val = circle_perimeter_aa(120, 400, 70) img[rr, cc, 1] = val * 255 # ellipses diff --git a/skimage/draw/__init__.py b/skimage/draw/__init__.py index 4fb222d1..285ba49a 100644 --- a/skimage/draw/__init__.py +++ b/skimage/draw/__init__.py @@ -1,6 +1,6 @@ from .draw import circle, ellipse, set_color -from ._draw import line, polygon, ellipse_perimeter, circle_perimeter, \ - bezier_segment +from ._draw import (line, polygon, ellipse_perimeter, circle_perimeter, + circle_perimeter_aa, bezier_segment) from .draw3d import ellipsoid, ellipsoid_stats __all__ = ['line', @@ -11,4 +11,5 @@ __all__ = ['line', 'ellipsoid_stats', 'circle', 'circle_perimeter', + 'circle_perimeter_aa', 'set_color'] diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 90ba001c..f68bb2f2 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -178,7 +178,6 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, method : {'bresenham', 'andres', 'wu'}, optional bresenham : Bresenham method (default) andres : Andres method - wu : Wu's method Returns ------- @@ -188,19 +187,13 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. - rr, cc, val : (N,) ndarray of int - Wu's method: - Indices of pixels and intensity values. - ``img[rr, cc] = val``. - 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. - Wu's method draws anti-aliased circle. This implementation doesn't use - lookup table optimization. + Anti-aliased circle generator is available with `circle_perimeter_aa`. References ---------- @@ -208,8 +201,6 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, plotter", IBM Systems journal, 4 (1965) 25-30. .. [2] E. Andres, "Discrete circles, rings and spheres", Computers & Graphics, 18 (1994) 695-706. - .. [3] X. Wu, "An efficient antialiasing technique", In ACM SIGGRAPH - Computer Graphics, 25 (1991) 143-152. Examples -------- @@ -233,7 +224,6 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, cdef list rr = list() cdef list cc = list() - cdef list val = list() cdef Py_ssize_t x = 0 cdef Py_ssize_t y = radius @@ -249,62 +239,97 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, elif method == 'andres': d = radius - 1 cmethod = 'a' - elif method == 'wu': - cmethod = 'w' else: raise ValueError('Wrong method') - if cmethod == 'a' or cmethod == 'b': - while y >= x: - rr.extend([y, -y, y, -y, x, -x, x, -x]) - cc.extend([x, x, -x, -x, y, y, -y, -y]) + while y >= x: + rr.extend([y, -y, y, -y, x, -x, x, -x]) + cc.extend([x, x, -x, -x, y, y, -y, -y]) - if cmethod == 'b': - if d < 0: - d += 4 * x + 6 - else: - d += 4 * (x - y) + 10 - y -= 1 - x += 1 - elif cmethod == 'a': - 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, dtype=np.intp) + cy, - np.array(cc, dtype=np.intp) + cx) - - elif cmethod == 'w': - dceil_prev = 0 - - rr.extend([y, x, y, x, -y, -x, -y, -x]) - cc.extend([x, y, -x, -y, x, y, -x, -y]) - val.extend([1] * 8) - - while y > x + 1: - x += 1 - dceil = math.sqrt(radius**2 - x**2) - dceil = math.ceil(dceil) - dceil - if dceil < dceil_prev: + if cmethod == 'b': + if d < 0: + d += 4 * x + 6 + else: + d += 4 * (x - y) + 10 y -= 1 - rr.extend([y, y - 1, x, x, y, y - 1, x, x]) - cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) + x += 1 + elif cmethod == 'a': + 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, dtype=np.intp) + cy, + np.array(cc, dtype=np.intp) + cx) - rr.extend([-y, 1 - y, -x, -x, -y, 1 - y, -x, -x]) - cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) - val.extend([1 - dceil, dceil] * 8) - dceil_prev = dceil +def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): + """Generate anti-aliased circle perimeter coordinates. - return (np.array(rr, dtype=np.intp) + cy, - np.array(cc, dtype=np.intp) + cx, - np.array(val, dtype=np.float)) + Parameters + ---------- + cy, cx : int + Centre coordinate of circle. + radius: int + Radius of circle. + + Returns + ------- + rr, cc, val : (N,) ndarray of int + Indices of pixels (`rr`, `cc`) and intensity values (`val`). + ``img[rr, cc] = val``. + + Notes + ----- + Wu's method draws anti-aliased circle. This implementation doesn't use + lookup table optimization. + + References + ---------- + .. [1] X. Wu, "An efficient antialiasing technique", In ACM SIGGRAPH + Computer Graphics, 25 (1991) 143-152. + + """ + + cdef list rr = list() + cdef list cc = list() + cdef list val = list() + + cdef Py_ssize_t x = 0 + cdef Py_ssize_t y = radius + cdef Py_ssize_t d = 0 + + cdef double dceil = 0 + + dceil_prev = 0 + + rr.extend([y, x, y, x, -y, -x, -y, -x]) + cc.extend([x, y, -x, -y, x, y, -x, -y]) + val.extend([1] * 8) + + while y > x + 1: + x += 1 + dceil = math.sqrt(radius**2 - x**2) + dceil = math.ceil(dceil) - dceil + if dceil < dceil_prev: + y -= 1 + rr.extend([y, y - 1, x, x, y, y - 1, x, x]) + cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) + + rr.extend([-y, 1 - y, -x, -x, -y, 1 - y, -x, -x]) + cc.extend([x, x, y, y - 1, -x, -x, -y, 1 - y]) + + val.extend([1 - dceil, dceil] * 8) + dceil_prev = dceil + + return (np.array(rr, dtype=np.intp) + cy, + np.array(cc, dtype=np.intp) + cx, + np.array(val, dtype=np.float)) def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 8dac897a..152797a7 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,8 +1,10 @@ from numpy.testing import assert_array_equal import numpy as np -from skimage.draw import line, polygon, circle, circle_perimeter, \ - ellipse, ellipse_perimeter, bezier_segment +from skimage.draw import (line, polygon, circle, circle_perimeter, + circle_perimeter_aa, ellipse, + ellipse_perimeter, bezier_segment, + ) def test_line_horizontal(): @@ -215,15 +217,15 @@ def test_circle_perimeter_andres(): assert_array_equal(img, img_) -def test_circle_perimeter_wu(): +def test_circle_perimeter_aa(): img = np.zeros((15, 15), 'uint8') - rr, cc, val = circle_perimeter(7, 7, 0, method='wu') + rr, cc, val = circle_perimeter_aa(7, 7, 0) img[rr, cc] = 1 assert(np.sum(img) == 1) assert(img[7][7] == 1) img = np.zeros((17, 17), 'uint8') - rr, cc, val = circle_perimeter(8, 8, 7, method='wu') + rr, cc, val = circle_perimeter_aa(8, 8, 7) img[rr, cc] = val * 255 img_ = np.array( [[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], From bf31517843636074100f0dd65193ce8b855c11d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 08:05:27 +0200 Subject: [PATCH 08/22] DOC: remove wu in methods --- skimage/draw/_draw.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index f68bb2f2..a8716983 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -175,7 +175,7 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, Centre coordinate of circle. radius: int Radius of circle. - method : {'bresenham', 'andres', 'wu'}, optional + method : {'bresenham', 'andres'}, optional bresenham : Bresenham method (default) andres : Andres method From 90335efdda23570fdb2a3c6f54b83c526fa5110f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 16:36:19 +0200 Subject: [PATCH 09/22] Implement line_aa --- skimage/draw/__init__.py | 6 ++- skimage/draw/_draw.pyx | 81 +++++++++++++++++++++++++++++++++ skimage/draw/tests/test_draw.py | 45 ++++++++++++++++-- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/skimage/draw/__init__.py b/skimage/draw/__init__.py index 285ba49a..5ca5afb1 100644 --- a/skimage/draw/__init__.py +++ b/skimage/draw/__init__.py @@ -1,9 +1,11 @@ from .draw import circle, ellipse, set_color -from ._draw import (line, polygon, ellipse_perimeter, circle_perimeter, - circle_perimeter_aa, bezier_segment) from .draw3d import ellipsoid, ellipsoid_stats +from ._draw import (line, line_aa, polygon, ellipse_perimeter, + circle_perimeter, circle_perimeter_aa, + bezier_segment) __all__ = ['line', + 'line_aa', 'polygon', 'ellipse', 'ellipse_perimeter', diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index a8716983..3e835d93 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -27,6 +27,10 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. + Notes + ----- + Anti-aliased line generator is available with `line_aa`. + Examples -------- >>> from skimage.draw import line @@ -89,6 +93,83 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2): return np.asarray(rr), np.asarray(cc) +def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): + """Generate line pixel coordinates. + + Parameters + ---------- + y1, x1 : int + Starting position (row, column). + y2, x2 : int + End position (row, column). + + Returns + ------- + rr, cc, val : (N,) ndarray of int + Indices of pixels that belong to the line. + May be used to directly index into an array, e.g. + ``img[rr, cc] = val``. + + References + ---------- + .. [1] A Rasterizing Algorithm for Drawing Curves, A. Zingl, 2012 + http://members.chello.at/easyfilter/Bresenham.pdf + + """ + cdef list rr = list() + cdef list cc = list() + cdef list val = list() + + cdef int dx = abs(x1 - x2) + cdef int dy = abs(y1 - y2) + cdef int err = dx - dy + cdef int x, y, e, ed, sign_x, sign_y + + if x1 < x2: + sign_x = 1 + else: + sign_x = -1 + + if y1 < y2: + sign_y = 1 + else: + sign_y = -1 + + if dx + dy == 0: + ed = 1 + else: + ed = (sqrt(dx*dx + dy*dy)) + + x, y = x1, y1 + while True: + cc.append(x) + rr.append(y) + val.append(255 * abs(err - dx + dy) / ed) + e = err + if 2 * e >= -dx: + if x == x2: + break + if e + dy < ed: + cc.append(x) + rr.append(y + sign_y) + val.append(255 * abs(e + dy) / ed) + err -= dy + x += sign_x + if 2 * e <= dy: + if y == y2: + break + if dx - e < ed: + cc.append(x) + rr.append(y) + val.append(255 * abs(dx - e) / ed) + err += dx + y += sign_y + + return (np.array(rr, dtype=np.intp), + np.array(cc, dtype=np.intp), + 255 - np.array(val, dtype=np.intp)) + + def polygon(y, x, shape=None): """Generate coordinates of pixels within polygon. diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 152797a7..23bbff4e 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,8 +1,10 @@ -from numpy.testing import assert_array_equal +from numpy.testing import assert_array_equal, assert_equal import numpy as np -from skimage.draw import (line, polygon, circle, circle_perimeter, - circle_perimeter_aa, ellipse, +from skimage.draw import (line, line_aa, + polygon, circle, + circle_perimeter, circle_perimeter_aa, + ellipse, ellipse_perimeter, bezier_segment, ) @@ -54,6 +56,43 @@ def test_line_diag(): assert_array_equal(img, img_) +def test_line_aa_horizontal(): + img = np.zeros((10, 10)) + + rr, cc, val = line_aa(0, 0, 0, 9) + img[rr, cc] = val + + img_ = np.zeros((10, 10)) + img_[0, :] = 255 + + assert_array_equal(img, img_) + + +def test_line_aa_vertical(): + img = np.zeros((10, 10)) + + rr, cc, val = line_aa(0, 0, 9, 0) + img[rr, cc] = val + + img_ = np.zeros((10, 10)) + img_[:, 0] = 255 + + assert_array_equal(img, img_) + + +def test_line_aa_diagonal(): + img = np.zeros((10, 10)) + + rr, cc, val = line_aa(0, 0, 9, 6) + img[rr, cc] = 1 + + # Check that each pixel belonging to line, + # also belongs to line_aa + r, c = line(0, 0, 9, 6) + for x, y in zip(r, c): + assert_equal(img[r, c], 1) + + def test_polygon_rectangle(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, 1), (4, 1), (4, 4), (1, 4), (1, 1))) From 1a0f13767955039c3432291f3ce28d6b8cd92247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 16:37:58 +0200 Subject: [PATCH 10/22] Anti-aliasing --- CONTRIBUTORS.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 37e619fe..f8f78c52 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -132,7 +132,8 @@ Dense DAISY feature description, circle perimeter drawing. - François Boulogne - Drawing: Andres Method for circle perimeter, ellipse perimeter drawing, Bezier curve. + Drawing: Andres Method for circle perimeter, ellipse perimeter drawing, + Bezier curve, anti-aliasing. Circular and elliptical Hough Transforms Various fixes From a4f1704d6e859f4e830fe73169cea540c1a8d02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 16:39:55 +0200 Subject: [PATCH 11/22] MAINT: bezier_segment is private --- skimage/draw/__init__.py | 2 +- skimage/draw/_draw.pyx | 12 ++++++------ skimage/draw/tests/test_draw.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/skimage/draw/__init__.py b/skimage/draw/__init__.py index 5ca5afb1..8e455343 100644 --- a/skimage/draw/__init__.py +++ b/skimage/draw/__init__.py @@ -2,7 +2,7 @@ from .draw import circle, ellipse, set_color from .draw3d import ellipsoid, ellipsoid_stats from ._draw import (line, line_aa, polygon, ellipse_perimeter, circle_perimeter, circle_perimeter_aa, - bezier_segment) + _bezier_segment) __all__ = ['line', 'line_aa', diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 3e835d93..dc56b2a7 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -533,23 +533,23 @@ def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, iyd = int(floor(ya * w + 0.5)) # Draw the 4 quadrants - rr, cc = bezier_segment(iy0 + iyd, ix0, iy0, ix0, iy0, ix0 + ixd, 1-w) + rr, cc = _bezier_segment(iy0 + iyd, ix0, iy0, ix0, iy0, ix0 + ixd, 1-w) py.extend(rr) px.extend(cc) - rr, cc = bezier_segment(iy0 + iyd, ix0, iy1, ix0, iy1, ix1 - ixd, w) + rr, cc = _bezier_segment(iy0 + iyd, ix0, iy1, ix0, iy1, ix1 - ixd, w) py.extend(rr) px.extend(cc) - rr, cc = bezier_segment(iy1 - iyd, ix1, iy1, ix1, iy1, ix1 - ixd, 1-w) + rr, cc = _bezier_segment(iy1 - iyd, ix1, iy1, ix1, iy1, ix1 - ixd, 1-w) py.extend(rr) px.extend(cc) - rr, cc = bezier_segment(iy1 - iyd, ix1, iy0, ix1, iy0, ix0 + ixd, w) + rr, cc = _bezier_segment(iy1 - iyd, ix1, iy0, ix1, iy0, ix0 + ixd, w) py.extend(rr) px.extend(cc) return np.array(py, dtype=np.intp), np.array(px, dtype=np.intp) -def bezier_segment(Py_ssize_t y0, Py_ssize_t x0, +def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2, double weight): @@ -643,7 +643,7 @@ def bezier_segment(Py_ssize_t y0, Py_ssize_t x0, sy = floor((y0 + 2 * weight * y1 + y2) * xy * 0.5 + 0.5) dx = floor((weight * x1 + x0) * xy + 0.5) dy = floor((y1 * weight + y0) * xy + 0.5) - return bezier_segment(y0, x0, (dy), (dx), + return _bezier_segment(y0, x0, (dy), (dx), (sy), (sx), cur) err = dx + dy - xy diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 23bbff4e..6b49e6a4 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -5,7 +5,7 @@ from skimage.draw import (line, line_aa, polygon, circle, circle_perimeter, circle_perimeter_aa, ellipse, - ellipse_perimeter, bezier_segment, + ellipse_perimeter, _bezier_segment, ) @@ -433,7 +433,7 @@ def test_bezier_segment_straight(): y1 = 50 x2 = 150 y2 = 150 - rr, cc = bezier_segment(x0, y0, x1, y1, x2, y2, 0) + rr, cc = _bezier_segment(x0, y0, x1, y1, x2, y2, 0) image [rr, cc] = 1 image2 = np.zeros((200, 200), dtype=int) @@ -444,7 +444,7 @@ def test_bezier_segment_straight(): def test_bezier_segment_curved(): img = np.zeros((25, 25), 'uint8') - rr, cc = bezier_segment(20, 20, 20, 2, 2, 2, 1) + rr, cc = _bezier_segment(20, 20, 20, 2, 2, 2, 1) img[rr, cc] = 1 img_ = 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], From d998166ede4e5ea55e0f7baa6bc32f7a79341468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 16:47:21 +0200 Subject: [PATCH 12/22] MAINT: val in [0,1] --- skimage/draw/_draw.pyx | 12 ++++++------ skimage/draw/tests/test_draw.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index dc56b2a7..a8df35c3 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -105,7 +105,7 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): Returns ------- - rr, cc, val : (N,) ndarray of int + rr, cc, val : (N,) ndarray of (int, int, float) Indices of pixels that belong to the line. May be used to directly index into an array, e.g. ``img[rr, cc] = val``. @@ -144,7 +144,7 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): while True: cc.append(x) rr.append(y) - val.append(255 * abs(err - dx + dy) / ed) + val.append(abs(err - dx + dy) / ed) e = err if 2 * e >= -dx: if x == x2: @@ -152,7 +152,7 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): if e + dy < ed: cc.append(x) rr.append(y + sign_y) - val.append(255 * abs(e + dy) / ed) + val.append(abs(e + dy) / ed) err -= dy x += sign_x if 2 * e <= dy: @@ -161,13 +161,13 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): if dx - e < ed: cc.append(x) rr.append(y) - val.append(255 * abs(dx - e) / ed) + val.append(abs(dx - e) / ed) err += dx y += sign_y return (np.array(rr, dtype=np.intp), np.array(cc, dtype=np.intp), - 255 - np.array(val, dtype=np.intp)) + 1 - np.array(val, dtype=np.float)) def polygon(y, x, shape=None): @@ -361,7 +361,7 @@ def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): Returns ------- - rr, cc, val : (N,) ndarray of int + rr, cc, val : (N,) ndarray (int, int, float) Indices of pixels (`rr`, `cc`) and intensity values (`val`). ``img[rr, cc] = val``. diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 6b49e6a4..4a79e819 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -63,7 +63,7 @@ def test_line_aa_horizontal(): img[rr, cc] = val img_ = np.zeros((10, 10)) - img_[0, :] = 255 + img_[0, :] = 1 assert_array_equal(img, img_) @@ -75,7 +75,7 @@ def test_line_aa_vertical(): img[rr, cc] = val img_ = np.zeros((10, 10)) - img_[:, 0] = 255 + img_[:, 0] = 1 assert_array_equal(img, img_) From 088b2995a99ae9870e4d9508ca8c11a75be2c627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 17:17:13 +0200 Subject: [PATCH 13/22] DOC: split non-AA/AA --- doc/examples/plot_shapes.py | 42 ++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 2d957977..04882068 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -1,9 +1,9 @@ """ -=========== -Fill shapes -=========== +====== +Shapes +====== -This example shows how to fill several different shapes: +This example shows how to draw several different shapes: * line * polygon @@ -49,10 +49,6 @@ img[rr,cc,2] = 255 rr, cc = circle_perimeter(120, 400, 15) img[rr, cc, :] = (255, 0, 0) -# anti-aliased circle -rr, cc, val = circle_perimeter_aa(120, 400, 70) -img[rr, cc, 1] = val * 255 - # ellipses rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 4.) img[rr, cc, :] = (255, 0, 255) @@ -63,3 +59,33 @@ img[rr, cc, :] = (255, 255, 255) plt.imshow(img) plt.show() + +""" + +Anti-aliasing drawing for: +* line +* circle + +""" + +import numpy as np +import matplotlib.pyplot as plt + +from skimage.draw import line_aa, \ + circle_perimeter_aa +import numpy as np + +img = np.zeros((100, 100), dtype=np.uint8) + +# anti-aliased line +rr, cc, val = line_aa(12, 12, 20, 50) +img[rr, cc] = val * 255 + +# anti-aliased circle +rr, cc, val = circle_perimeter_aa(60, 40, 30) +img[rr, cc] = val * 255 + + +plt.imshow(img, cmap=plt.cm.gray, interpolation='nearest') +plt.title('Anti-aliasing') +plt.show() From 718989edc53eb260f4e433339f517c69b0fc0154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 17:17:44 +0200 Subject: [PATCH 14/22] FIX: division for value --- skimage/draw/_draw.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index a8df35c3..dc1ae305 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -144,7 +144,7 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): while True: cc.append(x) rr.append(y) - val.append(abs(err - dx + dy) / ed) + val.append(1. * abs(err - dx + dy) / (ed)) e = err if 2 * e >= -dx: if x == x2: @@ -152,7 +152,7 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): if e + dy < ed: cc.append(x) rr.append(y + sign_y) - val.append(abs(e + dy) / ed) + val.append(1. * abs(e + dy) / (ed)) err -= dy x += sign_x if 2 * e <= dy: @@ -161,13 +161,13 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): if dx - e < ed: cc.append(x) rr.append(y) - val.append(abs(dx - e) / ed) + val.append(abs(dx - e) / (ed)) err += dx y += sign_y return (np.array(rr, dtype=np.intp), np.array(cc, dtype=np.intp), - 1 - np.array(val, dtype=np.float)) + 1. - np.array(val, dtype=np.float)) def polygon(y, x, shape=None): From 0467b920341e678c995eeb78334a028511598b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 17:22:18 +0200 Subject: [PATCH 15/22] PEP8 --- doc/examples/plot_shapes.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 04882068..995a278a 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -15,16 +15,15 @@ import numpy as np import matplotlib.pyplot as plt from skimage.draw import line, polygon, circle, \ - circle_perimeter, circle_perimeter_aa, \ + circle_perimeter, \ ellipse, ellipse_perimeter -import numpy as np import math img = np.zeros((500, 500, 3), dtype=np.uint8) # draw line rr, cc = line(120, 123, 20, 400) -img[rr,cc,0] = 255 +img[rr, cc, 0] = 255 # fill polygon poly = np.array(( @@ -34,16 +33,16 @@ poly = np.array(( (220, 590), (300, 300), )) -rr, cc = polygon(poly[:,0], poly[:,1], img.shape) -img[rr,cc,1] = 255 +rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape) +img[rr, cc, 1] = 255 # fill circle rr, cc = circle(200, 200, 100, img.shape) -img[rr,cc,:] = (255, 255, 0) +img[rr, cc, :] = (255, 255, 0) # fill ellipse rr, cc = ellipse(300, 300, 100, 200, img.shape) -img[rr,cc,2] = 255 +img[rr, cc, 2] = 255 # circle rr, cc = circle_perimeter(120, 400, 15) @@ -73,7 +72,6 @@ import matplotlib.pyplot as plt from skimage.draw import line_aa, \ circle_perimeter_aa -import numpy as np img = np.zeros((100, 100), dtype=np.uint8) From 5c423475e6df4698dffba01598dae11f81b1ebb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 17:24:23 +0200 Subject: [PATCH 16/22] PEP8 --- skimage/draw/_draw.pyx | 8 ++++---- skimage/draw/tests/test_draw.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index dc1ae305..1a42ef6b 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -550,9 +550,9 @@ def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, - Py_ssize_t y1, Py_ssize_t x1, - Py_ssize_t y2, Py_ssize_t x2, - double weight): + Py_ssize_t y1, Py_ssize_t x1, + Py_ssize_t y2, Py_ssize_t x2, + double weight): """Generate Bezier segment coordinates. Parameters @@ -644,7 +644,7 @@ def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, dx = floor((weight * x1 + x0) * xy + 0.5) dy = floor((y1 * weight + y0) * xy + 0.5) return _bezier_segment(y0, x0, (dy), (dx), - (sy), (sx), cur) + (sy), (sx), cur) err = dx + dy - xy while dy <= xy and dx >= xy: diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 4a79e819..1f30fb1d 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -434,11 +434,11 @@ def test_bezier_segment_straight(): x2 = 150 y2 = 150 rr, cc = _bezier_segment(x0, y0, x1, y1, x2, y2, 0) - image [rr, cc] = 1 + image[rr, cc] = 1 image2 = np.zeros((200, 200), dtype=int) rr, cc = line(x0, y0, x2, y2) - image2 [rr, cc] = 1 + image2[rr, cc] = 1 assert_array_equal(image, image2) From 1548364b650ab49c95476aa3595b8637522a3142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 18:24:47 +0200 Subject: [PATCH 17/22] ADD: bezier_curve --- skimage/draw/__init__.py | 3 +- skimage/draw/_draw.pyx | 112 ++++++++++++++++++++++++++++++++ skimage/draw/tests/test_draw.py | 106 ++++++++++++++++++++++++++++-- 3 files changed, 214 insertions(+), 7 deletions(-) diff --git a/skimage/draw/__init__.py b/skimage/draw/__init__.py index 8e455343..5f788ea7 100644 --- a/skimage/draw/__init__.py +++ b/skimage/draw/__init__.py @@ -2,10 +2,11 @@ from .draw import circle, ellipse, set_color from .draw3d import ellipsoid, ellipsoid_stats from ._draw import (line, line_aa, polygon, ellipse_perimeter, circle_perimeter, circle_perimeter_aa, - _bezier_segment) + _bezier_segment, bezier_curve) __all__ = ['line', 'line_aa', + 'bezier_curve', 'polygon', 'ellipse', 'ellipse_perimeter', diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 1a42ef6b..dac8e90a 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -677,6 +677,118 @@ def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, return np.array(py, dtype=np.intp), np.array(px, dtype=np.intp) +def bezier_curve(Py_ssize_t y0, Py_ssize_t x0, + Py_ssize_t y1, Py_ssize_t x1, + Py_ssize_t y2, Py_ssize_t x2, + double weight): + """Generate Bezier curve coordinates. + + Parameters + ---------- + y0, x0 : int + Coordinates of the first point + y1, x1 : int + Coordinates of the middle point + y2, x2 : int + Coordinates of the last point + weight : double + Middle point weight, it describes the line tension. + + Returns + ------- + rr, cc : (N,) ndarray of int + Indices of pixels that belong to the Bezier curve. + May be used to directly index into an array, e.g. + ``img[rr, cc] = 1``. + + Notes + ----- + The algorithm is the rational quadratic algorithm presented in + reference [1]. + + References + ---------- + .. [1] A Rasterizing Algorithm for Drawing Curves, A. Zingl, 2012 + http://members.chello.at/easyfilter/Bresenham.pdf + """ + # Pixels + cdef list px = list() + cdef list py = list() + + cdef int x, y + cdef double xx, yy, ww, t, q + x = x0 - 2 * x1 + x2 + y = y0 - 2 * y1 + y2 + + xx = x0 - x1 + yy = y0 - y1 + + if xx * (x2 - x1) > 0: + if yy * (y2 - y1): + if abs(xx * y) > abs(yy * x): + x0 = x2 + x2 = (xx + x1) + y0 = y2 + y2 = (yy + y1) + if (x0 == x2) or (weight == 1.): + t = (x0 - x1) / x + else: + q = sqrt(4. * weight * weight * (x0 - x1) * (x2 - x1) + (x2 - x0) * floor(x2 - x0)) + if (x1 < x0): + q = -q + t = (2. * weight * (x0 - x1) - x0 + x2 + q) / (2. * (1. - weight) * (x2 - x0)) + + q = 1. / (2. * t * (1. - t) * (weight - 1.) + 1.0) + xx = (t * t * (x0 - 2. * weight * x1 + x2) + 2. * t * (weight * x1 - x0) + x0) * q + yy = (t * t * (y0 - 2. * weight * y1 + y2) + 2. * t * (weight * y1 - y0) + y0) * q + ww = t * (weight - 1.) + 1. + ww *= ww * q + weight = ((1. - t) * (weight - 1.) + 1.) * sqrt(q) + x = (xx + 0.5) + y = (yy + 0.5) + yy = (xx - x0) * (y1 - y0) / (x1 - x0) + y0 + + rr, cc = _bezier_segment(y0, x0, (yy + 0.5), x, y, x, ww) + px.extend(rr) + py.extend(cc) + + yy = (xx - x2) * (y1 - y2) / (x1 - x2) + y2 + y1 = (yy + 0.5) + x0 = x1 = x + y0 = y + if (y0 - y1) * floor(y2 - y1) > 0: + if (y0 == y2) or (weight == 1): + t = (y0 - y1) / (y0 - 2. * y1 + y2) + else: + q = sqrt(4. * weight * weight * (y0 - y1) * (y2 - y1) + (y2 - y0) * floor(y2 - y0)) + if y1 < y0: + q = -q + t = (2. * weight * (y0 - y1) - y0 + y2 + q) / (2. * (1. - weight) * (y2 - y0)) + q = 1. / (2. * t * (1. - t) * (weight - 1.) + 1.) + xx = (t * t * (x0 - 2. * weight * x1 + x2) + 2. * t * (weight * x1 - x0) + x0) * q + yy = (t * t * (y0 - 2. * weight * y1 + y2) + 2. * t * (weight * y1 - y0) + y0) * q + ww = t * (weight - 1.) + 1. + ww *= ww * q + weight = ((1. - t) * (weight - 1.) + 1.) * sqrt(q) + x = (xx + 0.5) + y = (yy + 0.5) + xx = (x1 - x0) * (yy - y0) / (y1 - y0) + x0 + + rr, cc = _bezier_segment(y0, x0, y, (xx + 0.5), y, x, ww) + px.extend(rr) + py.extend(cc) + + xx = (x1 - x2) * (yy - y2) / (y1 - y2) + x2 + x1 = (xx + 0.5) + x0 = x + y0 = y1 = y + + rr, cc = _bezier_segment(y0, x0, y1, x1, y2, x2, weight * weight) + px.extend(rr) + py.extend(cc) + return np.array(px, dtype=np.intp), np.array(py, dtype=np.intp) + + def set_color(img, coords, color): """Set pixel color in the image at the given coordinates. diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 1f30fb1d..d3cab811 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,11 +1,10 @@ from numpy.testing import assert_array_equal, assert_equal import numpy as np -from skimage.draw import (line, line_aa, - polygon, circle, - circle_perimeter, circle_perimeter_aa, - ellipse, - ellipse_perimeter, _bezier_segment, +from skimage.draw import (line, line_aa, polygon, + circle, circle_perimeter, circle_perimeter_aa, + ellipse, ellipse_perimeter, + _bezier_segment, bezier_curve, ) @@ -444,7 +443,10 @@ def test_bezier_segment_straight(): def test_bezier_segment_curved(): img = np.zeros((25, 25), 'uint8') - rr, cc = _bezier_segment(20, 20, 20, 2, 2, 2, 1) + x1, y1 = 20, 20 + x2, y2 = 20, 2 + x3, y3 = 2, 2 + rr, cc = _bezier_segment(x1, y1, x2, y2, x3, y3, 1) img[rr, cc] = 1 img_ = 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], @@ -473,9 +475,101 @@ def test_bezier_segment_curved(): [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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] ) + assert_equal(img[x1, y1], 1) + assert_equal(img[x3, y3], 1) assert_array_equal(img, img_) +def test_bezier_curve_straight(): + image = np.zeros((200, 200), dtype=int) + x0 = 50 + y0 = 50 + x1 = 150 + y1 = 50 + x2 = 150 + y2 = 150 + rr, cc = bezier_curve(x0, y0, x1, y1, x2, y2, 0) + image [rr, cc] = 1 + + image2 = np.zeros((200, 200), dtype=int) + rr, cc = line(x0, y0, x2, y2) + image2 [rr, cc] = 1 + assert_array_equal(image, image2) + + +def test_bezier_curved_weight_eq_1(): + img = np.zeros((23, 8), 'uint8') + x1, y1 = (1, 1) + x2, y2 = (11, 11) + x3, y3 = (21, 1) + rr, cc = bezier_curve(x1, y1, x2, y2, x3, y3, 1) + img[rr, cc] = 1 + assert_equal(img[x1, y1], 1) + assert_equal(img[x3, y3], 1) + img_ = np.array( + [[0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0]] + ) + assert_equal(img, img_) + + +def test_bezier_curved_weight_neq_1(): + img = np.zeros((23, 10), 'uint8') + x1, y1 = (1, 1) + x2, y2 = (11, 11) + x3, y3 = (21, 1) + rr, cc = bezier_curve(x1, y1, x2, y2, x3, y3, 2) + img[rr, cc] = 1 + assert_equal(img[x1, y1], 1) + assert_equal(img[x3, y3], 1) + img_ = np.array( + [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + ) + assert_equal(img, img_) + if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() From 9ceb489ba8c92a4a5465723d5c0618ca637624b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Tue, 27 Aug 2013 18:28:32 +0200 Subject: [PATCH 18/22] DOC: add bezier_curve --- doc/examples/plot_shapes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 995a278a..197e133b 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -6,6 +6,7 @@ Shapes This example shows how to draw several different shapes: * line +* Bezier curve * polygon * circle * ellipse @@ -16,7 +17,8 @@ import matplotlib.pyplot as plt from skimage.draw import line, polygon, circle, \ circle_perimeter, \ - ellipse, ellipse_perimeter + ellipse, ellipse_perimeter, \ + bezier_curve import math img = np.zeros((500, 500, 3), dtype=np.uint8) @@ -48,6 +50,10 @@ img[rr, cc, 2] = 255 rr, cc = circle_perimeter(120, 400, 15) img[rr, cc, :] = (255, 0, 0) +# Bezier curve +rr, cc = bezier_curve(70, 100, 10, 10, 150, 100, 1) +img[rr, cc, :] = (255, 0, 0) + # ellipses rr, cc = ellipse_perimeter(120, 400, 60, 20, orientation=math.pi / 4.) img[rr, cc, :] = (255, 0, 255) From 296f8dad20ee56cafd811dbedddff1dd747e3edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 09:39:36 +0200 Subject: [PATCH 19/22] MINOR: doctrings + various improvements --- doc/examples/plot_shapes.py | 14 +++---- skimage/draw/_draw.pyx | 84 +++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 197e133b..8e082579 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -15,10 +15,10 @@ This example shows how to draw several different shapes: import numpy as np import matplotlib.pyplot as plt -from skimage.draw import line, polygon, circle, \ - circle_perimeter, \ - ellipse, ellipse_perimeter, \ - bezier_curve +from skimage.draw import (line, polygon, circle, + circle_perimeter, + ellipse, ellipse_perimeter, + bezier_curve) import math img = np.zeros((500, 500, 3), dtype=np.uint8) @@ -67,7 +67,7 @@ plt.show() """ -Anti-aliasing drawing for: +Anti-aliased drawing for: * line * circle @@ -76,8 +76,8 @@ Anti-aliasing drawing for: import numpy as np import matplotlib.pyplot as plt -from skimage.draw import line_aa, \ - circle_perimeter_aa +from skimage.draw import (line_aa, + circle_perimeter_aa) img = np.zeros((100, 100), dtype=np.uint8) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index dac8e90a..bb190b8b 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -105,9 +105,8 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): Returns ------- - rr, cc, val : (N,) ndarray of (int, int, float) - Indices of pixels that belong to the line. - May be used to directly index into an array, e.g. + rr, cc, val : (N,) ndarray (int, int, float) + Indices of pixels (`rr`, `cc`) and intensity values (`val`). ``img[rr, cc] = val``. References @@ -115,6 +114,23 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): .. [1] A Rasterizing Algorithm for Drawing Curves, A. Zingl, 2012 http://members.chello.at/easyfilter/Bresenham.pdf + Examples + -------- + >>> from skimage.draw import line_aa + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc, val = line_aa(1, 1, 8, 8) + >>> img[rr, cc] = val * 255 + >>> img + array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 255, 56, 0, 0, 0, 0, 0, 0, 0], + [ 0, 56, 255, 56, 0, 0, 0, 0, 0, 0], + [ 0, 0, 56, 255, 56, 0, 0, 0, 0, 0], + [ 0, 0, 0, 56, 255, 56, 0, 0, 0, 0], + [ 0, 0, 0, 0, 56, 255, 56, 0, 0, 0], + [ 0, 0, 0, 0, 0, 56, 255, 56, 0, 0], + [ 0, 0, 0, 0, 0, 0, 56, 255, 56, 0], + [ 0, 0, 0, 0, 0, 0, 0, 56, 255, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) """ cdef list rr = list() cdef list cc = list() @@ -375,11 +391,28 @@ def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): .. [1] X. Wu, "An efficient antialiasing technique", In ACM SIGGRAPH Computer Graphics, 25 (1991) 143-152. + Examples + -------- + >>> from skimage.draw import circle_perimeter_aa + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc, val = circle_perimeter_aa(4, 4, 3) + >>> img[rr, cc] = val * 255 + >>> img + array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 60, 211, 255, 211, 60, 0, 0, 0], + [ 0, 60, 194, 43, 0, 43, 194, 60, 0, 0], + [ 0, 211, 43, 0, 0, 0, 43, 211, 0, 0], + [ 0, 255, 0, 0, 0, 0, 0, 255, 0, 0], + [ 0, 211, 43, 0, 0, 0, 43, 211, 0, 0], + [ 0, 60, 194, 43, 0, 43, 194, 60, 0, 0], + [ 0, 0, 60, 211, 255, 211, 60, 0, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) """ - cdef list rr = list() - cdef list cc = list() - cdef list val = list() + cdef list rr = [y, x, y, x, -y, -x, -y, -x] + cdef list cc = [x, y, -x, -y, x, y, -x, -y] + cdef list val = [1] * 8 cdef Py_ssize_t x = 0 cdef Py_ssize_t y = radius @@ -389,10 +422,6 @@ def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): dceil_prev = 0 - rr.extend([y, x, y, x, -y, -x, -y, -x]) - cc.extend([x, y, -x, -y, x, y, -x, -y]) - val.extend([1] * 8) - while y > x + 1: x += 1 dceil = math.sqrt(radius**2 - x**2) @@ -558,13 +587,13 @@ def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, Parameters ---------- y0, x0 : int - Coordinates of the first point + Coordinates of the first control point. y1, x1 : int - Coordinates of the middle point + Coordinates of the middle control point. y2, x2 : int - Coordinates of the last point + Coordinates of the last control point. weight : double - Middle point weight, it describes the line tension. + Middle control point weight, it describes the line tension. Returns ------- @@ -686,13 +715,13 @@ def bezier_curve(Py_ssize_t y0, Py_ssize_t x0, Parameters ---------- y0, x0 : int - Coordinates of the first point + Coordinates of the first control point. y1, x1 : int - Coordinates of the middle point + Coordinates of the middle control point. y2, x2 : int - Coordinates of the last point + Coordinates of the last control point. weight : double - Middle point weight, it describes the line tension. + Middle control point weight, it describes the line tension. Returns ------- @@ -710,6 +739,25 @@ def bezier_curve(Py_ssize_t y0, Py_ssize_t x0, ---------- .. [1] A Rasterizing Algorithm for Drawing Curves, A. Zingl, 2012 http://members.chello.at/easyfilter/Bresenham.pdf + + Examples + -------- + >>> import numpy as np + >>> from skimage.draw import bezier_curve + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = bezier_curve(1, 5, 5, -2, 8, 8, 2) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) """ # Pixels cdef list px = list() From 7299753602df96dd67d8bd0ea967f0af62861146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 10:38:11 +0200 Subject: [PATCH 20/22] FIX: broken test (thanks unittest!) --- skimage/draw/_draw.pyx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index bb190b8b..f4814f84 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -410,17 +410,16 @@ def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) """ - cdef list rr = [y, x, y, x, -y, -x, -y, -x] - cdef list cc = [x, y, -x, -y, x, y, -x, -y] - cdef list val = [1] * 8 - cdef Py_ssize_t x = 0 cdef Py_ssize_t y = radius cdef Py_ssize_t d = 0 cdef double dceil = 0 + cdef double dceil_prev = 0 - dceil_prev = 0 + cdef list rr = [y, x, y, x, -y, -x, -y, -x] + cdef list cc = [x, y, -x, -y, x, y, -x, -y] + cdef list val = [1] * 8 while y > x + 1: x += 1 From 055e820e720d89ca6a8c8e7915fc80f52a89ffbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 17:21:35 +0200 Subject: [PATCH 21/22] MINOR: some comments --- skimage/draw/_draw.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index f4814f84..3d48ce86 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -6,7 +6,7 @@ import math import numpy as np cimport numpy as cnp -from libc.math cimport sqrt, sin, cos, floor +from libc.math cimport sqrt, sin, cos, floor, ceil from skimage._shared.geometry cimport point_in_polygon @@ -94,7 +94,7 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2): def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2): - """Generate line pixel coordinates. + """Generate anti-aliased line pixel coordinates. Parameters ---------- @@ -231,9 +231,9 @@ def polygon(y, x, shape=None): cdef Py_ssize_t nr_verts = x.shape[0] cdef Py_ssize_t minr = int(max(0, y.min())) - cdef Py_ssize_t maxr = int(math.ceil(y.max())) + cdef Py_ssize_t maxr = int(ceil(y.max())) cdef Py_ssize_t minc = int(max(0, x.min())) - cdef Py_ssize_t maxc = int(math.ceil(x.max())) + cdef Py_ssize_t maxc = int(ceil(x.max())) # make sure output coordinates do not exceed image size if shape is not None: @@ -423,8 +423,8 @@ def circle_perimeter_aa(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius): while y > x + 1: x += 1 - dceil = math.sqrt(radius**2 - x**2) - dceil = math.ceil(dceil) - dceil + dceil = sqrt(radius**2 - x**2) + dceil = ceil(dceil) - dceil if dceil < dceil_prev: y -= 1 rr.extend([y, y - 1, x, x, y, y - 1, x, x]) @@ -604,7 +604,7 @@ def _bezier_segment(Py_ssize_t y0, Py_ssize_t x0, Notes ----- The algorithm is the rational quadratic algorithm presented in - reference [1]. + reference [1]_. References ---------- @@ -732,7 +732,7 @@ def bezier_curve(Py_ssize_t y0, Py_ssize_t x0, Notes ----- The algorithm is the rational quadratic algorithm presented in - reference [1]. + reference [1]_. References ---------- From bac7ede8b2072c8ebbe86dcdc996e3986784db27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Boulogne?= Date: Wed, 2 Oct 2013 18:12:01 +0200 Subject: [PATCH 22/22] PEP8 --- skimage/draw/draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index d01bc2b0..cbf3ced2 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -52,7 +52,7 @@ def ellipse(cy, cx, yradius, xradius, shape=None): dc = 1 / float(xradius) r, c = np.ogrid[-1:1:dr, -1:1:dc] - rr, cc = np.nonzero(r ** 2 + c ** 2 < 1) + rr, cc = np.nonzero(r ** 2 + c ** 2 < 1) rr.flags.writeable = True cc.flags.writeable = True