I would like to be able to draw lines into numpy arrays to get off-line features for on-line handwriting recognition. This means I don't need the image at all, but I need for some positions in a numpy array who an image of a given size would look like.
I would like to be able to specify an image size and then draw strokes like this:
import module
im = module.new_image(width=800, height=200)
im.add_stroke(from={'x': 123, 'y': 2}, to={'x': 42, 'y': 3})
im.add_stroke(from={'x': 4, 'y': 3}, to={'x': 2, 'y': 1})
features = im.get(x_min=12, x_max=15, y_min=0, y_max=111)
Is something simple like that possible (preferably directly with numpy / scipy)?
(Please note that I want grey-scale interpolation. So features
should be a matrix of values in [0, 255].)
I've found the
val * 255
approach in the answer suboptimal, because it seems to work correctly only on black background. If the background contains darker and brighter regions, this does not seem quite right:To make it work correctly on all backgrounds, one has to take the colors of the pixels that are covered by the anti-aliased line into account.
The interesting part is
where the new color is computed from the original color of the image, and the color of the line, by linear interpolation using the values from anti-alias
weights
. Here is a result, orange line running over two kinds of background:Now the pixels that surround the line in the upper half become darker, whereas the pixels in the lower half become brighter.
Thanks to Joe Kington for the answer! I was looking for
skimage.draw.line_aa
.I stumbled on this question while looking for a solution, and the provided answer solves it quite well. However, it didn't really suit my purposes, for which I needed a "tensorizable" solution (i.e. implemented in numpy without explicit loops), and possibly with a linewidth option. I ended up implementing my own version, and since in the end it's also quite faster than line_aa, I thought I could share it.
It comes in two flavors, with and without linewidth. Actually the former is not a generalization of the latter, and neither perfectly agrees with line_aa, but for my purposes they're just fine and on plots they look okay.
I called this "naive" because it is quite similar to the naive implementation in Wikipedia, but with some anti-aliasing, although admittedly not perfect (e.g. makes very thin diagonals).
The weighted version gives much thicker line more pronounced anti-aliasing.
The weight adjustment is admittedly quite arbitrary, so anybody can adjust that to their tastes. The rmin and rmax are now needed to avoid pixels outside of the picture. A comparison:
As you can see, even with w=1, weighted_line is a bit thicker, but in a kind of homogeneous way; similarly, naive_line is homogeneously slightly thinner.
Final note about benchmarking: on my machine, running
%timeit f(1,1,100,240)
for the various functions (w=1 for weighted_line) resulted in a time of 90 µs for line_aa, 84 µs for weighted_line (although the time of course increases with the weight) and 18 µs for naive_line. Again for comparison, reimplementing line_aa in pure Python (instead of Cython as in the package) took 350 µs.