Reputation: 65
I have the equation of a line in y=ax+b form, and a starting point, and I want to find all the pixels this line crosses over/into.
At the moment, I was just stepping the x value a bit, calculating y, truncating to find pixel index and adding it to a list if not already in the list, and continuing until reaching a destination point. Kind of as follows (python/pseudocode):
temp_x = start_x
prev_tested = None
pixel_list = []
while(not at destination):
temp_y = ... find y from x and line equation
pixel = (int(temp_y), int(temp_x))
if pixel is not the same as the prev_pixel:
pixel_list.append(pixel)
temp_x += some_step_value
But this just seems wildly inaccurate and inefficient (No need to tell me that in the answers, I understand this is a bad algo). The step value affects a lot. Too large and I will miss pixels (especially if there is a large slope). Too small and I am just wasting iterations. I figured that I can make my step value proportional to my slope, so that I try to minimize the number of iterations I have to do, while also not skipping over too much. But it is not perfect, still skipping over pixels that the line only barely enters the corner.
I feel like there has to be some kind of way to just absolutely determine which pixels a line is touching, but I have had no look finding anything myself. Is there some resource anyone could point me towards that could help with this?
Upvotes: 2
Views: 2081
Reputation: 477
Skimage has a function that solves this problem. From the documentation (https://scikit-image.org/docs/stable/api/skimage.draw.html#skimage.draw.line):
from skimage.draw import line
img = np.zeros((10, 10), dtype=np.uint8)
rr, cc = line(1, 1, 8, 8)
# When the coordinates are stored in an array
line_in_img = [x1, y1, x2, y2]
rr, cc = line(*line_in_img)
# If you want a list of tuples that contain the coordinates instead of 2 lists that hold the xs and ys
coordinates = list(zip(rr, cc))
coordinates then is a list of coordinate pairs
Upvotes: 1
Reputation:
Dx= X1 - X0
Dy= Y1 - Y0
D= Max(Abs(Dx), Abs(Dy))
for I= 0 to D
X= X0 + I * Dx / D
Y= Y0 + I * Dy / D
works in all cases (except the degenerate D=0
) to join (X0, Y0)
to (X1, Y1)
using integer coordinates.
Technical note:
You can avoid the two divisions. One by the fact that the fraction simplifies to ±1, and the other by computing the quotient and remainder incrementally.
If you believe that this is not accurate enough, you can scale all coordinates by an arbitrary integer M
, compute the points with step M
and divide the coordinates by M
.
Upvotes: 1
Reputation: 110696
Your step value should be always 1 . What to step over, however depends on your line being more on the horizontal or more on the vertical (that is "a < 1" or "a > 1". For one, step x on 1, and y will be a fraction of that, and for lines more on vertical, y will step with 1 and x will be a fraction of that.
def draw_line(a, b, start_x, destination):
result = []
x = start_x
y = a * x + b
result.append((int(x),int(y)))
while int(x) != destination[0] and int(y) != destination[1]:
if abs(a) < 1:
x += 1 if destination[0] > start_x else -1
y += (1 / a) if a!= 0 else 0
else:
y += 1 if destination[1] > y else -1
x += 1 / a
result.append((int(x), int(y)))
return result
# maybe there is some missing corner case for vertical lines.
Upvotes: 0