From 4565a49cfa310f4e8ad39eb12f47b5367aa472ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Thu, 19 Apr 2012 23:37:35 +0200 Subject: [PATCH 01/14] added draw.polygon function with test cases --- skimage/draw/_draw.pyx | 99 +++++++++++++++++++++++++++++++-- skimage/draw/draw.py | 2 +- skimage/draw/tests/test_draw.py | 78 ++++++++++++++++++++++++-- 3 files changed, 168 insertions(+), 11 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index a586ae03..df59fadc 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -1,16 +1,18 @@ import numpy as np +import math +from libc.stdlib cimport malloc, free cimport numpy as np cimport cython -cdef extern from "math.h": - int abs(int i) +cdef extern from "../morphology/_pnpoly.h": + int pnpoly(int nr_verts, double *xp, double *yp, + double x, double y) @cython.boundscheck(False) @cython.wraparound(False) def bresenham(int y, int x, int y2, int x2): - """ - Generate line pixel coordinates. - + """Generate line pixel coordinates. + Parameters ---------- y, x : int @@ -46,7 +48,7 @@ def bresenham(int y, int x, int y2, int x2): rr = np.zeros(int(dx) + 1, dtype=np.int32) cc = np.zeros(int(dx) + 1, dtype=np.int32) - + for i in range(dx): if steep: rr[i] = x @@ -64,3 +66,88 @@ def bresenham(int y, int x, int y2, int x2): cc[dx] = x2 return rr, cc + +@cython.boundscheck(False) +@cython.wraparound(False) +def _polygon_area(np.ndarray[np.double_t, ndim=1] x, np.ndarray[np.double_t, ndim=1] y): + """Calculate area of polygon. + + Parameters + ---------- + x : ndarray + X coordinates of polygon + y : ndarray + Y coordinates of polygon + + Returns + ------- + area : double + area of polygon + """ + cdef double area + cdef int i + cdef int j = x.shape[0]-1 + for i in xrange(x.shape[0]): + area += (x[j]+x[i])*(y[j]-y[i]) + j = i + return abs(0.5*area) + +@cython.boundscheck(False) +@cython.wraparound(False) +def polygon(verts, shape=None): + """Generate coordinates of pixels within polygon. + + Parameters + ---------- + verts : Nx2 ndarray + (row, col) coordinates + shape : tuple, optional + image shape which is used to determine maximum extents of output pixel + coordinates. This is useful for polygons which exceed the image size, + default None + + Returns + ------- + rr, cc : ndarray of int + Pixel coordinates of polygon. + May be used to directly index into an array, e.g. + ``img[rr, cc] = 1``. + """ + cdef int nr_verts = verts.shape[0] + cdef int minr = max(0, verts[:,0].min()) + cdef int maxr = math.ceil(verts[:,0].max()) + cdef int minc = max(0, verts[:,1].min()) + cdef int maxc = math.ceil(verts[:,1].max()) + + # 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 int r, c + cdef int i = 0 + + #: make contigous arrays for r, c coordinates + verts = verts.astype('double') + cdef np.ndarray contiguous_rdata = verts[:,0].copy(order='C') + cdef np.ndarray contiguous_cdata = verts[:,1].copy(order='C') + cdef np.double_t* rptr = contiguous_rdata.data + cdef np.double_t* cptr = contiguous_cdata.data + + # use area of polygon to determine the rough size of the output arrays + cdef double area = _polygon_area(contiguous_cdata, contiguous_rdata) + + #: output coordinate arrays + cdef np.ndarray[np.int32_t, ndim=1, mode="c"] rr, cc + rr = np.zeros(int(area), dtype=np.int32) + cc = np.zeros(int(area), dtype=np.int32) + + for r in range(minr, maxr+1): + for c in range(minc, maxc+1): + if pnpoly(nr_verts, cptr, rptr, c, r): + rr[i] = r + cc[i] = c + i += 1 + + # area >= number of points in polygon, so crop actual points + return rr[:i], cc[:i] diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index e66faf1c..5a7f9339 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -3,4 +3,4 @@ Methods to draw on arrays. """ -from ._draw import bresenham +from ._draw import bresenham, polygon diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index ee323d83..80873981 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,7 +1,8 @@ from numpy.testing import assert_array_equal import numpy as np -from skimage.draw import bresenham +from skimage.draw import bresenham, polygon + def test_bresenham_horizontal(): img = np.zeros((10, 10)) @@ -25,7 +26,7 @@ def test_bresenham_vertical(): assert_array_equal(img, img_) -def test_reverse(): +def test_bresenham_reverse(): img = np.zeros((10, 10)) rr, cc = bresenham(0, 9, 0, 0) @@ -36,7 +37,7 @@ def test_reverse(): assert_array_equal(img, img_) -def test_diag(): +def test_bresenham_diag(): img = np.zeros((5, 5)) rr, cc = bresenham(0, 0, 4, 4) @@ -47,6 +48,75 @@ def test_diag(): assert_array_equal(img, img_) +def test_polygon_rectangle(): + img = np.zeros((10, 10), 'uint8') + poly = np.array(((1, 1), (4, 1), (4, 4), (1, 4), (1, 1))) + + rr, cc = polygon(poly) + img[rr,cc] = 1 + + img_ = np.zeros((10, 10)) + img_[1:4,1:4] = 1 + + assert_array_equal(img, img_) + +def test_polygon_rectangle_angular(): + img = np.zeros((10, 10), 'uint8') + poly = np.array(((0, 3), (4, 7), (7, 4), (3, 0), (0, 3))) + + rr, cc = polygon(poly) + img[rr,cc] = 1 + + img_ = np.array( + [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 1, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 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]] + ) + + assert_array_equal(img, img_) + +def test_polygon_parallelogram(): + img = np.zeros((10, 10), 'uint8') + poly = np.array(((1, 1), (5, 1), (7, 6), (3, 6), (1, 1))) + + rr, cc = polygon(poly) + 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, 1, 1, 1, 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, 1, 1, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 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_polygon_exceed(): + img = np.zeros((10, 10), 'uint8') + poly = np.array(((1, -1), (100, -1), (100, 100), (1, 100), (1, 1))) + + rr, cc = polygon(poly, img.shape) + img[rr,cc] = 1 + + img_ = np.zeros((10, 10)) + img_[1:,:] = 1 + + assert_array_equal(img, img_) + + if __name__ == "__main__": from numpy.testing import run_module_suite - + run_module_suite() From 0aa008712ac83188d9586de54a5137b3f0ce8c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Sat, 21 Apr 2012 16:56:14 +0200 Subject: [PATCH 02/14] replaced fixed output array size with dynamic lists area of polygon is not suitable to determine the size of the number of filled pixels for output array --- skimage/draw/_draw.pyx | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index df59fadc..01b6b17d 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -1,6 +1,5 @@ import numpy as np import math -from libc.stdlib cimport malloc, free cimport numpy as np cimport cython @@ -125,7 +124,6 @@ def polygon(verts, shape=None): maxc = min(shape[1]-1, maxc) cdef int r, c - cdef int i = 0 #: make contigous arrays for r, c coordinates verts = verts.astype('double') @@ -134,20 +132,15 @@ def polygon(verts, shape=None): cdef np.double_t* rptr = contiguous_rdata.data cdef np.double_t* cptr = contiguous_cdata.data - # use area of polygon to determine the rough size of the output arrays - cdef double area = _polygon_area(contiguous_cdata, contiguous_rdata) - #: output coordinate arrays - cdef np.ndarray[np.int32_t, ndim=1, mode="c"] rr, cc - rr = np.zeros(int(area), dtype=np.int32) - cc = np.zeros(int(area), dtype=np.int32) + rr = list() + cc = list() for r in range(minr, maxr+1): for c in range(minc, maxc+1): if pnpoly(nr_verts, cptr, rptr, c, r): - rr[i] = r - cc[i] = c - i += 1 + rr.append(r) + cc.append(c) # area >= number of points in polygon, so crop actual points - return rr[:i], cc[:i] + return np.array(rr), np.array(cc) From 649d912071d82d0622ef4be86c41ed9270f349ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Sat, 21 Apr 2012 16:59:29 +0200 Subject: [PATCH 03/14] improved doc string --- skimage/draw/_draw.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 01b6b17d..efe1897d 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -102,8 +102,8 @@ def polygon(verts, shape=None): (row, col) coordinates shape : tuple, optional image shape which is used to determine maximum extents of output pixel - coordinates. This is useful for polygons which exceed the image size, - default None + coordinates. This is useful for polygons which exceed the image size. + By default the full extents of the polygon are used. Returns ------- From 0db612724da4c24acdb73e7d4cf5e643e488cd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Sun, 22 Apr 2012 10:46:37 +0200 Subject: [PATCH 04/14] removed area function and removed old comment --- skimage/draw/_draw.pyx | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index efe1897d..44bfea55 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -3,10 +3,12 @@ import math cimport numpy as np cimport cython + cdef extern from "../morphology/_pnpoly.h": int pnpoly(int nr_verts, double *xp, double *yp, double x, double y) + @cython.boundscheck(False) @cython.wraparound(False) def bresenham(int y, int x, int y2, int x2): @@ -68,31 +70,7 @@ def bresenham(int y, int x, int y2, int x2): @cython.boundscheck(False) @cython.wraparound(False) -def _polygon_area(np.ndarray[np.double_t, ndim=1] x, np.ndarray[np.double_t, ndim=1] y): - """Calculate area of polygon. - - Parameters - ---------- - x : ndarray - X coordinates of polygon - y : ndarray - Y coordinates of polygon - - Returns - ------- - area : double - area of polygon - """ - cdef double area - cdef int i - cdef int j = x.shape[0]-1 - for i in xrange(x.shape[0]): - area += (x[j]+x[i])*(y[j]-y[i]) - j = i - return abs(0.5*area) - -@cython.boundscheck(False) -@cython.wraparound(False) +@cython.nonecheck(False) def polygon(verts, shape=None): """Generate coordinates of pixels within polygon. @@ -142,5 +120,4 @@ def polygon(verts, shape=None): rr.append(r) cc.append(c) - # area >= number of points in polygon, so crop actual points return np.array(rr), np.array(cc) From 4834d7eb4d4d61487dde9f4d68c885d8355e5d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 15:37:07 +0200 Subject: [PATCH 05/14] added example script for draw.polygon function --- doc/examples/plot_polygon.py | 39 ++++++++++++++++++++++++++++++++++++ skimage/setup.py | 1 + 2 files changed, 40 insertions(+) create mode 100644 doc/examples/plot_polygon.py diff --git a/doc/examples/plot_polygon.py b/doc/examples/plot_polygon.py new file mode 100644 index 00000000..6fa58906 --- /dev/null +++ b/doc/examples/plot_polygon.py @@ -0,0 +1,39 @@ +""" +============ +Fill polygon +============ + +This example shows how to fill polygons in images. +""" + +import matplotlib.pyplot as plt + +from skimage.draw import polygon +import numpy as np + + +img = np.zeros((500, 500), 'uint8') +polygon1 = np.array(( + (50, 50), + (150, 30), + (400, 100), + (300, 200), + (480, 400), + (100, 420), + (50, 50), +)) +polygon2 = np.array(( + (300, 300), + (480, 320), + (380, 430), + (220, 590), + (300, 300), +)) +rr, cc = polygon(polygon1, img.shape) +img[rr,cc] = 127 +rr, cc = polygon(polygon2, img.shape) +img[rr,cc] = 255 + +plt.gray() +plt.imshow(img) +plt.show() \ No newline at end of file diff --git a/skimage/setup.py b/skimage/setup.py index c26014f8..f555c89a 100644 --- a/skimage/setup.py +++ b/skimage/setup.py @@ -16,6 +16,7 @@ def configuration(parent_package='', top_path=None): config.add_subpackage('draw') config.add_subpackage('feature') config.add_subpackage('measure') + config.add_subpackage('geometry') def add_test_directories(arg, dirname, fnames): if dirname.split(os.path.sep)[-1] == 'tests': From 7191eaf05897cdd8f3276dea97df5bfb7cb35d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 15:39:26 +0200 Subject: [PATCH 06/14] removed geometry package from setup script --- skimage/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/skimage/setup.py b/skimage/setup.py index f555c89a..c26014f8 100644 --- a/skimage/setup.py +++ b/skimage/setup.py @@ -16,7 +16,6 @@ def configuration(parent_package='', top_path=None): config.add_subpackage('draw') config.add_subpackage('feature') config.add_subpackage('measure') - config.add_subpackage('geometry') def add_test_directories(arg, dirname, fnames): if dirname.split(os.path.sep)[-1] == 'tests': From de8369be50208f94db5f90ebe33474ebafd7a8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 16:20:49 +0200 Subject: [PATCH 07/14] added draw circle and ellipse functions --- skimage/draw/_draw.pyx | 65 +++++++++++++++++++++++++++++++++ skimage/draw/draw.py | 2 +- skimage/draw/tests/test_draw.py | 54 ++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 44bfea55..6f6c955f 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -1,5 +1,6 @@ import numpy as np import math +from libc.math cimport sqrt cimport numpy as np cimport cython @@ -121,3 +122,67 @@ def polygon(verts, shape=None): cc.append(c) return np.array(rr), np.array(cc) + +@cython.boundscheck(False) +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +def ellipse(double cy, double cx, double b, double a, shape=None): + """Generate coordinates of pixels within ellipse. + + Parameters + ---------- + cy, cx : double + centre coordinate of ellipse + b, a: double + minor and major semi-axes. (x/a)**2 + (y/b)**2 = 1 + + 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 int minr = max(0, cy-b) + cdef int maxr = math.ceil(cy+b) + cdef int minc = max(0, cx-a) + cdef int maxc = math.ceil(cx+a) + + # 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 int r, c + + #: output coordinate arrays + rr = list() + cc = list() + + for r in range(minr, maxr+1): + for c in range(minc, maxc+1): + if sqrt(((r - cy)/b)**2 + ((c - cx)/a)**2) < 1: + rr.append(r) + cc.append(c) + + return np.array(rr), np.array(cc) + +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 + + 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``. + """ + return ellipse(cy, cx, radius, radius, shape) diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index 5a7f9339..a18cdeb6 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -3,4 +3,4 @@ Methods to draw on arrays. """ -from ._draw import bresenham, polygon +from ._draw import bresenham, polygon, ellipse, circle diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 80873981..ed309cf6 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,7 +1,7 @@ from numpy.testing import assert_array_equal import numpy as np -from skimage.draw import bresenham, polygon +from skimage.draw import bresenham, polygon, circle, ellipse def test_bresenham_horizontal(): @@ -116,6 +116,58 @@ def test_polygon_exceed(): assert_array_equal(img, img_) +def test_circle(): + img = np.zeros((15, 15), 'uint8') + + rr, cc = circle(7, 7, 6) + 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, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], + [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 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') + + rr, cc = ellipse(7, 7, 3, 7) + 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, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 1, 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, 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_) + if __name__ == "__main__": from numpy.testing import run_module_suite From d83592cc055c5fcc76243ecc199b00e70daed393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 16:22:37 +0200 Subject: [PATCH 08/14] improved doc string --- 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 6f6c955f..38e8f941 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -78,7 +78,7 @@ def polygon(verts, shape=None): Parameters ---------- verts : Nx2 ndarray - (row, col) coordinates + (row, col) vertices of polygon shape : tuple, optional image shape which is used to determine maximum extents of output pixel coordinates. This is useful for polygons which exceed the image size. From 53ac34a8d000bec2fc24c9c7b5c5dd8ab31a094b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 17:07:27 +0200 Subject: [PATCH 09/14] renamed function bresenham to line --- skimage/draw/_draw.pyx | 2 +- skimage/draw/draw.py | 2 +- skimage/draw/tests/test_draw.py | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index 38e8f941..ac4b078c 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -12,7 +12,7 @@ cdef extern from "../morphology/_pnpoly.h": @cython.boundscheck(False) @cython.wraparound(False) -def bresenham(int y, int x, int y2, int x2): +def line(int y, int x, int y2, int x2): """Generate line pixel coordinates. Parameters diff --git a/skimage/draw/draw.py b/skimage/draw/draw.py index a18cdeb6..b7bb5b88 100644 --- a/skimage/draw/draw.py +++ b/skimage/draw/draw.py @@ -3,4 +3,4 @@ Methods to draw on arrays. """ -from ._draw import bresenham, polygon, ellipse, circle +from ._draw import line, polygon, ellipse, circle diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index ed309cf6..42bbafc9 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -1,13 +1,13 @@ from numpy.testing import assert_array_equal import numpy as np -from skimage.draw import bresenham, polygon, circle, ellipse +from skimage.draw import line, polygon, circle, ellipse -def test_bresenham_horizontal(): +def test_line_horizontal(): img = np.zeros((10, 10)) - rr, cc = bresenham(0, 0, 0, 9) + rr, cc = line(0, 0, 0, 9) img[rr, cc] = 1 img_ = np.zeros((10, 10)) @@ -15,10 +15,10 @@ def test_bresenham_horizontal(): assert_array_equal(img, img_) -def test_bresenham_vertical(): +def test_line_vertical(): img = np.zeros((10, 10)) - rr, cc = bresenham(0, 0, 9, 0) + rr, cc = line(0, 0, 9, 0) img[rr, cc] = 1 img_ = np.zeros((10, 10)) @@ -26,10 +26,10 @@ def test_bresenham_vertical(): assert_array_equal(img, img_) -def test_bresenham_reverse(): +def test_line_reverse(): img = np.zeros((10, 10)) - rr, cc = bresenham(0, 9, 0, 0) + rr, cc = line(0, 9, 0, 0) img[rr, cc] = 1 img_ = np.zeros((10, 10)) @@ -37,10 +37,10 @@ def test_bresenham_reverse(): assert_array_equal(img, img_) -def test_bresenham_diag(): +def test_line_diag(): img = np.zeros((5, 5)) - rr, cc = bresenham(0, 0, 4, 4) + rr, cc = line(0, 0, 4, 4) img[rr, cc] = 1 img_ = np.eye(5) From 48328cda8afb954f647355d616d00813efc0006c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 17:07:36 +0200 Subject: [PATCH 10/14] combined all draw functions in one example script --- doc/examples/plot_polygon.py | 39 ----------------------------- doc/examples/plot_shapes.py | 48 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 39 deletions(-) delete mode 100644 doc/examples/plot_polygon.py create mode 100644 doc/examples/plot_shapes.py diff --git a/doc/examples/plot_polygon.py b/doc/examples/plot_polygon.py deleted file mode 100644 index 6fa58906..00000000 --- a/doc/examples/plot_polygon.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -============ -Fill polygon -============ - -This example shows how to fill polygons in images. -""" - -import matplotlib.pyplot as plt - -from skimage.draw import polygon -import numpy as np - - -img = np.zeros((500, 500), 'uint8') -polygon1 = np.array(( - (50, 50), - (150, 30), - (400, 100), - (300, 200), - (480, 400), - (100, 420), - (50, 50), -)) -polygon2 = np.array(( - (300, 300), - (480, 320), - (380, 430), - (220, 590), - (300, 300), -)) -rr, cc = polygon(polygon1, img.shape) -img[rr,cc] = 127 -rr, cc = polygon(polygon2, img.shape) -img[rr,cc] = 255 - -plt.gray() -plt.imshow(img) -plt.show() \ No newline at end of file diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py new file mode 100644 index 00000000..806049ad --- /dev/null +++ b/doc/examples/plot_shapes.py @@ -0,0 +1,48 @@ +""" +=========== +Fill shapes +=========== + +This example shows how to fill several different shapes: +* line +* polygon +* circle +* ellipse + +""" + +import matplotlib.pyplot as plt + +from skimage.draw import line, polygon, circle, ellipse +import numpy as np + + +img = np.zeros((500, 500, 3), 'uint8') + +#: draw line +rr, cc = line(120, 123, 20, 400) +img[rr,cc,0] = 255 + +#: fill polygon +poly = np.array(( + (300, 300), + (480, 320), + (380, 430), + (220, 590), + (300, 300), +)) +rr, cc = polygon(poly, img.shape) +img[rr,cc,1] = 255 + +#: fill circle +rr, cc = circle(200, 200, 100, img.shape) +img[rr,cc,0] = 255 +rr, cc = circle(200, 200, 100, img.shape) +img[rr,cc,1] = 255 + +#: fill ellipse +rr, cc = ellipse(300, 300, 100, 200, img.shape) +img[rr,cc,2] = 255 + +plt.imshow(img) +plt.show() \ No newline at end of file From 5b854d9b3c425cf104e1ab20bac78b738aa8ab57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 18:38:20 +0200 Subject: [PATCH 11/14] changed interface of draw.polygon function for a more consistent usage --- doc/examples/plot_shapes.py | 2 +- skimage/draw/_draw.pyx | 32 +++++++++++++++++++++----------- skimage/draw/tests/test_draw.py | 8 ++++---- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 806049ad..55569016 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -31,7 +31,7 @@ poly = np.array(( (220, 590), (300, 300), )) -rr, cc = polygon(poly, img.shape) +rr, cc = polygon(poly[:,0], poly[:,1], img.shape) img[rr,cc,1] = 255 #: fill circle diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index ac4b078c..d124f75d 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -72,13 +72,15 @@ def line(int y, int x, int y2, int x2): @cython.boundscheck(False) @cython.wraparound(False) @cython.nonecheck(False) -def polygon(verts, shape=None): +def polygon(y, x, shape=None): """Generate coordinates of pixels within polygon. Parameters ---------- - verts : Nx2 ndarray - (row, col) vertices of polygon + y : (N,) ndarray + y coordinates of vertices of polygon + x : (N,) ndarray + x coordinates of vertices of polygon shape : tuple, optional image shape which is used to determine maximum extents of output pixel coordinates. This is useful for polygons which exceed the image size. @@ -91,11 +93,11 @@ def polygon(verts, shape=None): May be used to directly index into an array, e.g. ``img[rr, cc] = 1``. """ - cdef int nr_verts = verts.shape[0] - cdef int minr = max(0, verts[:,0].min()) - cdef int maxr = math.ceil(verts[:,0].max()) - cdef int minc = max(0, verts[:,1].min()) - cdef int maxc = math.ceil(verts[:,1].max()) + cdef int nr_verts = x.shape[0] + cdef int minr = max(0, y.min()) + cdef int maxr = math.ceil(y.max()) + cdef int minc = max(0, x.min()) + cdef int maxc = math.ceil(x.max()) # make sure output coordinates do not exceed image size if shape is not None: @@ -105,9 +107,17 @@ def polygon(verts, shape=None): cdef int r, c #: make contigous arrays for r, c coordinates - verts = verts.astype('double') - cdef np.ndarray contiguous_rdata = verts[:,0].copy(order='C') - cdef np.ndarray contiguous_cdata = verts[:,1].copy(order='C') + y = y.astype('double') + x = x.astype('double') + cdef np.ndarray contiguous_rdata, contiguous_cdata + if not y.flags['C_CONTIGUOUS']: + contiguous_rdata = y.copy(order='C') + else: + contiguous_rdata = y + if not y.flags['C_CONTIGUOUS']: + contiguous_cdata = x.copy(order='C') + else: + contiguous_cdata = x cdef np.double_t* rptr = contiguous_rdata.data cdef np.double_t* cptr = contiguous_cdata.data diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index 42bbafc9..dd1c81e2 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -52,7 +52,7 @@ def test_polygon_rectangle(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, 1), (4, 1), (4, 4), (1, 4), (1, 1))) - rr, cc = polygon(poly) + rr, cc = polygon(poly[:,0], poly[:,1]) img[rr,cc] = 1 img_ = np.zeros((10, 10)) @@ -64,7 +64,7 @@ def test_polygon_rectangle_angular(): img = np.zeros((10, 10), 'uint8') poly = np.array(((0, 3), (4, 7), (7, 4), (3, 0), (0, 3))) - rr, cc = polygon(poly) + rr, cc = polygon(poly[:,0], poly[:,1]) img[rr,cc] = 1 img_ = np.array( @@ -86,7 +86,7 @@ def test_polygon_parallelogram(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, 1), (5, 1), (7, 6), (3, 6), (1, 1))) - rr, cc = polygon(poly) + rr, cc = polygon(poly[:,0], poly[:,1]) img[rr,cc] = 1 img_ = np.array( @@ -108,7 +108,7 @@ def test_polygon_exceed(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, -1), (100, -1), (100, 100), (1, 100), (1, 1))) - rr, cc = polygon(poly, img.shape) + rr, cc = polygon(poly[:,0], poly[:,1], img.shape) img[rr,cc] = 1 img_ = np.zeros((10, 10)) From af345f1a3238797d61aeefb24585b39b4cd9ebe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 18:45:11 +0200 Subject: [PATCH 12/14] improved color assignment of circle in shapes example script --- doc/examples/plot_shapes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/examples/plot_shapes.py b/doc/examples/plot_shapes.py index 55569016..8107fc80 100644 --- a/doc/examples/plot_shapes.py +++ b/doc/examples/plot_shapes.py @@ -36,9 +36,7 @@ img[rr,cc,1] = 255 #: fill circle rr, cc = circle(200, 200, 100, img.shape) -img[rr,cc,0] = 255 -rr, cc = circle(200, 200, 100, img.shape) -img[rr,cc,1] = 255 +img[rr,cc,:] = (255, 255, 0) #: fill ellipse rr, cc = ellipse(300, 300, 100, 200, img.shape) From 9fdfdc9708d43c3ad65fbb7c608fb1850039058f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 22:06:26 +0200 Subject: [PATCH 13/14] added explicit cython list type and changed creation of contiguous arrays --- skimage/draw/_draw.pyx | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/skimage/draw/_draw.pyx b/skimage/draw/_draw.pyx index d124f75d..9dc1d307 100644 --- a/skimage/draw/_draw.pyx +++ b/skimage/draw/_draw.pyx @@ -107,23 +107,15 @@ def polygon(y, x, shape=None): cdef int r, c #: make contigous arrays for r, c coordinates - y = y.astype('double') - x = x.astype('double') cdef np.ndarray contiguous_rdata, contiguous_cdata - if not y.flags['C_CONTIGUOUS']: - contiguous_rdata = y.copy(order='C') - else: - contiguous_rdata = y - if not y.flags['C_CONTIGUOUS']: - contiguous_cdata = x.copy(order='C') - else: - contiguous_cdata = x + contiguous_rdata = np.ascontiguousarray(y, 'double') + contiguous_cdata = np.ascontiguousarray(x, 'double') cdef np.double_t* rptr = contiguous_rdata.data cdef np.double_t* cptr = contiguous_cdata.data #: output coordinate arrays - rr = list() - cc = list() + cdef list rr = list() + cdef list cc = list() for r in range(minr, maxr+1): for c in range(minc, maxc+1): @@ -167,8 +159,8 @@ def ellipse(double cy, double cx, double b, double a, shape=None): cdef int r, c #: output coordinate arrays - rr = list() - cc = list() + cdef list rr = list() + cdef list cc = list() for r in range(minr, maxr+1): for c in range(minc, maxc+1): From c7d06951a8191598bbd0bc677b13156b7bc4a575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20Scho=CC=88nberger?= Date: Mon, 23 Apr 2012 22:16:59 +0200 Subject: [PATCH 14/14] DOC: update contributors --- CONTRIBUTORS.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 5082c508..8258eada 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -98,3 +98,6 @@ - Nicolas Poilvert Shape views: ``util.shape.view_as_windows`` and ``util.shape.view_as_blocks`` + +- Johannes Schönberger + Polygon, circle and ellipse drawing functions