mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-30 09:41:23 +08:00
Merge pull request #568 from ahojnnes/circle
ENH: Faster version of circle and ellipse drawing.
This commit is contained in:
@@ -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']
|
||||
|
||||
+78
-112
@@ -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):
|
||||
@@ -28,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
|
||||
@@ -74,9 +91,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.
|
||||
|
||||
@@ -87,7 +101,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.
|
||||
|
||||
@@ -98,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]
|
||||
@@ -133,82 +167,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.
|
||||
@@ -223,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
|
||||
@@ -241,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)
|
||||
|
||||
"""
|
||||
|
||||
@@ -311,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
|
||||
@@ -373,31 +367,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
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
# 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``.
|
||||
|
||||
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)
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
rr, cc = _coords_inside_image(rr, cc, img.shape)
|
||||
img[rr, cc] = color
|
||||
Reference in New Issue
Block a user