Fix and clean up anti-aliased line drawing (closes #1563)

This commit is contained in:
Stefan van der Walt
2015-07-11 18:38:26 -05:00
parent 661b8e60c0
commit a659aa1acc
2 changed files with 54 additions and 30 deletions
+39 -30
View File
@@ -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
@@ -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
@@ -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
@@ -165,17 +165,22 @@ 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
@@ -183,30 +188,34 @@ def line_aa(Py_ssize_t y1, Py_ssize_t x1, Py_ssize_t y2, Py_ssize_t x2):
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
+15
View File
@@ -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')