Skip to content

Commit

Permalink
Fix and clean up anti-aliased line drawing (closes #1563)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanv committed Jul 11, 2015
1 parent 661b8e6 commit a659aa1
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 30 deletions.
69 changes: 39 additions & 30 deletions skimage/draw/_draw.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ def _coords_inside_image(rr, cc, shape, val=None):
return rr[mask], cc[mask]


def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2):
def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y1, Py_ssize_t x1):
"""Generate line pixel coordinates.
Parameters
----------
y, x : int
Starting position (row, column).
y2, x2 : int
y1, x1 : int
End position (row, column).
Returns
Expand Down Expand Up @@ -79,16 +79,16 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2):
"""

cdef char steep = 0
cdef Py_ssize_t dx = abs(x2 - x)
cdef Py_ssize_t dy = abs(y2 - y)
cdef Py_ssize_t dx = abs(x1 - x)
cdef Py_ssize_t dy = abs(y1 - y)
cdef Py_ssize_t sx, sy, d, i

with nogil:
if (x2 - x) > 0:
if (x1 - x) > 0:
sx = 1
else:
sx = -1
if (y2 - y) > 0:
if (y1 - y) > 0:
sy = 1
else:
sy = -1
Expand Down Expand Up @@ -116,20 +116,20 @@ def line(Py_ssize_t y, Py_ssize_t x, Py_ssize_t y2, Py_ssize_t x2):
x = x + sx
d = d + (2 * dy)

rr[dx] = y2
cc[dx] = x2
rr[dx] = y1
cc[dx] = x1

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):
def line_aa(Py_ssize_t y0, Py_ssize_t x0, Py_ssize_t y1, Py_ssize_t x1):
"""Generate anti-aliased line pixel coordinates.
Parameters
----------
y1, x1 : int
y0, x0 : int
Starting position (row, column).
y2, x2 : int
y1, x1 : int
End position (row, column).
Returns
Expand Down Expand Up @@ -165,48 +165,57 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2):
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
cdef int dx = abs(x0 - x1)
cdef int dx_prime

if x1 < x2:
cdef int dy = abs(y0 - y1)
cdef float err = dx - dy
cdef float err_prime

cdef int x, y, sign_x, sign_y
cdef float ed

if x0 < x1:
sign_x = 1
else:
sign_x = -1

if y1 < y2:
if y0 < y1:
sign_y = 1
else:
sign_y = -1

if dx + dy == 0:
ed = 1
else:
ed = <int>(sqrt(dx*dx + dy*dy))
ed = sqrt(dx*dx + dy*dy)

x, y = x1, y1
x, y = x0, y0
while True:
cc.append(x)
rr.append(y)
val.append(1. * fabs(err - dx + dy) / <float>(ed))
e = err
if 2 * e >= -dx:
if x == x2:
val.append(fabs(err - dx + dy) / ed)

err_prime = err
x_prime = x

if (2 * err_prime) >= -dx:
if x == x1:
break
if e + dy < ed:
if (err_prime + dy) < ed:
cc.append(x)
rr.append(y + sign_y)
val.append(1. * fabs(e + dy) / <float>(ed))
val.append(fabs(err_prime + dy) / ed)
err -= dy
x += sign_x
if 2 * e <= dy:
if y == y2:

if 2 * err_prime <= dy:
if y == y1:
break
if dx - e < ed:
cc.append(x)
if (dx - err_prime) < ed:
cc.append(x_prime + sign_x)
rr.append(y)
val.append(fabs(dx - e) / <float>(ed))
val.append(fabs(dx - err_prime) / ed)
err += dx
y += sign_y

Expand Down
15 changes: 15 additions & 0 deletions skimage/draw/tests/test_draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,21 @@ def test_line_aa_diagonal():
for x, y in zip(r, c):
assert_equal(img[r, c], 1)

def test_line_equal_aliasing_horizontally_vertically():
img0 = np.zeros((25, 25))
img1 = np.zeros((25, 25))

# Near-horizontal line
rr, cc, val = line_aa(10, 2, 12, 20)
img0[rr, cc] = val

# Near-vertical (transpose of prior)
rr, cc, val = line_aa(2, 10, 20, 12)
img1[rr, cc] = val

# Difference - should be zero
assert_array_equal(img0, img1.T)


def test_polygon_rectangle():
img = np.zeros((10, 10), 'uint8')
Expand Down

0 comments on commit a659aa1

Please sign in to comment.