From 559b0a0562913a8658c54d1b6548a06eee671bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:00:09 +0200 Subject: [PATCH 1/9] Implement faster version of circle and ellipse drawing --- skimage/draw/__init__.py | 7 ++- skimage/draw/_draw.pyx | 108 --------------------------------------- skimage/draw/draw.py | 101 ++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 112 deletions(-) create mode 100644 skimage/draw/draw.py diff --git a/skimage/draw/__init__.py b/skimage/draw/__init__.py index a5ba8869..bffb5847 100644 --- a/skimage/draw/__init__.py +++ b/skimage/draw/__init__.py @@ -1,5 +1,5 @@ -from ._draw import line, polygon, ellipse, ellipse_perimeter, \ - circle, circle_perimeter, set_color, bresenham +from .draw import circle, ellipse, set_color +from ._draw import line, polygon, ellipse_perimeter, circle_perimeter __all__ = ['line', 'polygon', @@ -7,5 +7,4 @@ __all__ = ['line', 'ellipse_perimeter', 'circle', 'circle_perimeter', - 'set_color', - 'bresenham'] + 'set_color'] diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 9dbcd914..b873a8d5 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -8,7 +8,6 @@ import numpy as np cimport numpy as cnp from libc.math cimport sqrt from skimage._shared.geometry cimport point_in_polygon -from skimage._shared.utils import deprecated def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2): @@ -74,9 +73,6 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2): return rr, cc -bresenham = deprecated('skimage.draw.line')(line) - - def polygon(y, x, shape=None): """Generate coordinates of pixels within polygon. @@ -133,82 +129,6 @@ def polygon(y, x, shape=None): return np.array(rr, dtype=np.intp), np.array(cc, dtype=np.intp) -def ellipse(double cy, double cx, double yradius, double xradius, shape=None): - """Generate coordinates of pixels within ellipse. - - Parameters - ---------- - cy, cx : double - Centre coordinate of ellipse. - yradius, xradius : double - Minor and major semi-axes. ``(x/xradius)**2 + (y/yradius)**2 = 1``. - shape : tuple, optional - image shape which is used to determine maximum extents of output pixel - coordinates. This is useful for ellipses which exceed the image size. - By default the full extents of the ellipse are used. - - Returns - ------- - rr, cc : ndarray of int - Pixel coordinates of ellipse. - May be used to directly index into an array, e.g. - ``img[rr, cc] = 1``. - - """ - - cdef Py_ssize_t minr = int(max(0, cy - yradius)) - cdef Py_ssize_t maxr = int(math.ceil(cy + yradius)) - cdef Py_ssize_t minc = int(max(0, cx - xradius)) - cdef Py_ssize_t maxc = int(math.ceil(cx + xradius)) - - # make sure output coordinates do not exceed image size - if shape is not None: - maxr = min(shape[0] - 1, maxr) - maxc = min(shape[1] - 1, maxc) - - cdef Py_ssize_t r, c - - # output coordinate arrays - cdef list rr = list() - cdef list cc = list() - - for r in range(minr, maxr+1): - for c in range(minc, maxc+1): - if sqrt(((r - cy) / yradius)**2 + ((c - cx) / xradius)**2) < 1: - rr.append(r) - cc.append(c) - - return np.array(rr, dtype=np.intp), np.array(cc, dtype=np.intp) - - -def circle(double cy, double cx, double radius, shape=None): - """Generate coordinates of pixels within circle. - - Parameters - ---------- - cy, cx : double - Centre coordinate of circle. - radius: double - Radius of circle. - shape : tuple, optional - image shape which is used to determine maximum extents of output pixel - coordinates. This is useful for circles which exceed the image size. - By default the full extents of the circle are used. - - Returns - ------- - rr, cc : ndarray of int - Pixel coordinates of circle. - May be used to directly index into an array, e.g. - ``img[rr, cc] = 1``. - Notes - ----- - This function is a wrapper for skimage.draw.ellipse() - """ - - return ellipse(cy, cx, radius, radius, shape) - - def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, method='bresenham'): """Generate circle perimeter coordinates. @@ -373,31 +293,3 @@ def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, ychange += twobsquared return np.array(py, dtype=np.intp) + cy, np.array(px, dtype=np.intp) + cx - - -def set_color(img, coords, color): - """Set pixel color in the image at the given coordinates. - - Coordinates that exceed the shape of the image will be ignored. - - Parameters - ---------- - img : (M, N, D) ndarray - Image - coords : ((P,) ndarray, (P,) ndarray) - Coordinates of pixels to be colored. - color : (D,) ndarray - Color to be assigned to coordinates in the image. - - Returns - ------- - img : (M, N, D) ndarray - The updated image. - - """ - - rr, cc = coords - rr_inside = np.logical_and(rr >= 0, rr < img.shape[0]) - cc_inside = np.logical_and(cc >= 0, cc < img.shape[1]) - inside = np.logical_and(rr_inside, cc_inside) - img[rr[inside], cc[inside]] = color diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py new file mode 100644 index 00000000..6391e06c --- /dev/null +++ b/skimage/draw/draw.py @@ -0,0 +1,101 @@ +# coding: utf-8 +import numpy as np + + +def _coords_inside_image(rr, cc, shape): + mask = (rr >= 0) & (rr < shape[0]) & (cc >= 0) & (cc < shape[1]) + return rr[mask], cc[mask] + + +def ellipse(cy, cx, yradius, xradius, shape=None): + """Generate coordinates of pixels within ellipse. + + Parameters + ---------- + cy, cx : double + Centre coordinate of ellipse. + yradius, xradius : double + Minor and major semi-axes. ``(x/xradius)**2 + (y/yradius)**2 = 1``. + shape : tuple, optional + image shape which is used to determine maximum extents of output pixel + coordinates. This is useful for ellipses which exceed the image size. + By default the full extents of the ellipse are used. + + Returns + ------- + rr, cc : ndarray of int + Pixel coordinates of ellipse. + May be used to directly index into an array, e.g. + ``img[rr, cc] = 1``. + + """ + + dr = 1 / float(yradius) + dc = 1 / float(xradius) + + r, c = np.ogrid[-1:1:dr, -1:1:dc] + rr, cc = np.nonzero(r ** 2 + c ** 2 < 1) + + rr.flags.writeable = True + cc.flags.writeable = True + rr += cy - yradius + cc += cx - xradius + + if shape is not None: + _coords_inside_image(rr, cc, shape) + + return rr, cc + + +def circle(cy, cx, radius, shape=None): + """Generate coordinates of pixels within circle. + + Parameters + ---------- + cy, cx : double + Centre coordinate of circle. + radius: double + Radius of circle. + shape : tuple, optional + image shape which is used to determine maximum extents of output pixel + coordinates. This is useful for circles which exceed the image size. + By default the full extents of the circle are used. + + Returns + ------- + rr, cc : ndarray of int + Pixel coordinates of circle. + May be used to directly index into an array, e.g. + ``img[rr, cc] = 1``. + Notes + ----- + This function is a wrapper for skimage.draw.ellipse() + """ + + return ellipse(cy, cx, radius, radius, shape) + + +def set_color(img, coords, color): + """Set pixel color in the image at the given coordinates. + + Coordinates that exceed the shape of the image will be ignored. + + Parameters + ---------- + img : (M, N, D) ndarray + Image + coords : ((P,) ndarray, (P,) ndarray) + Coordinates of pixels to be colored. + color : (D,) ndarray + Color to be assigned to coordinates in the image. + + Returns + ------- + img : (M, N, D) ndarray + The updated image. + + """ + + rr, cc = coords + rr, cc = _coords_inside_image(rr, cc, img.shape) + img[rr, cc] = color From 4cf6edc756f4e89188a8fa105e265b5fc50f1852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:00:59 +0200 Subject: [PATCH 2/9] Capitalize parameter description --- skimage/draw/_draw.pyx | 2 +- skimage/draw/draw.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index b873a8d5..d3ed0fd0 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -83,7 +83,7 @@ def polygon(y, x, shape=None): x : (N,) ndarray X-coordinates of vertices of polygon. shape : tuple, optional - image shape which is used to determine maximum extents of output pixel + Image shape which is used to determine maximum extents of output pixel coordinates. This is useful for polygons which exceed the image size. By default the full extents of the polygon are used. diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index 6391e06c..2d525a89 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -17,7 +17,7 @@ def ellipse(cy, cx, yradius, xradius, shape=None): yradius, xradius : double Minor and major semi-axes. ``(x/xradius)**2 + (y/yradius)**2 = 1``. shape : tuple, optional - image shape which is used to determine maximum extents of output pixel + Image shape which is used to determine maximum extents of output pixel coordinates. This is useful for ellipses which exceed the image size. By default the full extents of the ellipse are used. @@ -57,7 +57,7 @@ def circle(cy, cx, radius, shape=None): radius: double Radius of circle. shape : tuple, optional - image shape which is used to determine maximum extents of output pixel + Image shape which is used to determine maximum extents of output pixel coordinates. This is useful for circles which exceed the image size. By default the full extents of the circle are used. From 748be5db22fb08ef7161ee0c323194b504625117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:09:49 +0200 Subject: [PATCH 3/9] Add example to line drawing function --- skimage/draw/_draw.pyx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index d3ed0fd0..192fb0a2 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -27,6 +27,24 @@ 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``. + Examples + -------- + >>> from skimage.draw import line + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = line(1, 1, 8, 8) + >>> img[rr, cc] = 1 + >>> img + 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, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) + """ cdef cnp.ndarray[cnp.intp_t, ndim=1, mode="c"] rr, cc From 3c8337a3ac1fb00ac6d408e49d922ff3ff8b889b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:11:57 +0200 Subject: [PATCH 4/9] Add example to polygon drawing function --- skimage/draw/_draw.pyx | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 192fb0a2..94abe627 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -112,6 +112,26 @@ def polygon(y, x, shape=None): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. + Examples + -------- + >>> from skimage.draw import polygon + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> x = np.array([1, 7, 4, 1]) + >>> y = np.array([1, 2, 8, 1]) + >>> rr, cc = polygon(y, x) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 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, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) + """ cdef Py_ssize_t nr_verts = x.shape[0] From 5bae2046df8090bd2ad324c2a1625bc89267e9dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:15:36 +0200 Subject: [PATCH 5/9] Add example to circle_perimeter drawing function --- skimage/draw/_draw.pyx | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 94abe627..e1ae4a59 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -181,7 +181,6 @@ def circle_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t radius, bresenham : Bresenham method andres : Andres method - Returns ------- rr, cc : (N,) ndarray of int @@ -199,8 +198,27 @@ 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. + plotter", 4 (1965) 25-30. + .. [2] E. Andres, "Discrete circles, rings and spheres", + 18 (1994) 695-706. + + Examples + -------- + >>> from skimage.draw import circle_perimeter + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = circle_perimeter(4, 4, 3) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 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]], dtype=uint8) """ From be7a92004196b1776af67589a9a3d48eb3ac74e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:15:50 +0200 Subject: [PATCH 6/9] Add example to ellipse_perimeter drawing function --- skimage/draw/_draw.pyx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index e1ae4a59..72376b7a 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -287,6 +287,24 @@ def ellipse_perimeter(Py_ssize_t cy, Py_ssize_t cx, Py_ssize_t yradius, .. [1] J. Kennedy "A fast Bresenham type algorithm for drawing ellipses". + Examples + -------- + >>> from skimage.draw import ellipse_perimeter + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = ellipse_perimeter(5, 5, 3, 4) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 1, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) + """ # If both radii == 0, return the center to avoid infinite loop in 2nd set From bc3a8040939b78718d68ea5ed619ce42ed37c4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:17:26 +0200 Subject: [PATCH 7/9] Add example to ellipse drawing function --- skimage/draw/draw.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index 2d525a89..f262b1c2 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -28,6 +28,25 @@ def ellipse(cy, cx, yradius, xradius, shape=None): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. + + Examples + -------- + >>> from skimage.draw import ellipse + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = ellipse(5, 5, 3, 4) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 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]], dtype=uint8) + """ dr = 1 / float(yradius) From f31193c099205198ece607c59bd61dede56d8348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:19:25 +0200 Subject: [PATCH 8/9] Add example to circle drawing function --- skimage/draw/draw.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index f262b1c2..72093d7b 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -28,7 +28,6 @@ def ellipse(cy, cx, yradius, xradius, shape=None): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. - Examples -------- >>> from skimage.draw import ellipse @@ -89,6 +88,25 @@ def circle(cy, cx, radius, shape=None): Notes ----- This function is a wrapper for skimage.draw.ellipse() + + Examples + -------- + >>> from skimage.draw import circle + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = circle(4, 4, 5) + >>> img[rr, cc] = 1 + >>> img + array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) + """ return ellipse(cy, cx, radius, radius, shape) From a12e55784929896ad635446cda93965c0693aee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Sch=C3=B6nberger?= Date: Mon, 27 May 2013 21:20:59 +0200 Subject: [PATCH 9/9] Add example to set_color drawing function --- skimage/draw/draw.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index 72093d7b..d01bc2b0 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -131,6 +131,24 @@ def set_color(img, coords, color): img : (M, N, D) ndarray The updated image. + Examples + -------- + >>> from skimage.draw import line, set_color + >>> img = np.zeros((10, 10), dtype=np.uint8) + >>> rr, cc = line(1, 1, 20, 20) + >>> set_color(img, (rr, cc), 1) + >>> img + 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, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]], dtype=uint8) + """ rr, cc = coords