img2plot – Artistic Line Drawings from Images

I recently published img2plot, a Python script that generates artistic pen-plotter sketches from arbitrary images.

A lot of plotter projects have code for drawing images.  Usually they do something with line density and pixel intensities – a semi-random stippling or wave pattern, for example, with smoothly higher density for darker pixels.  These can very faithfully represent the image, but they don’t look very human-made.  Another approach is to find and draw Canny or other edge-detector results – which do faithfully represent edges, but again, don’t look very human.

With img2plot, I wanted to try to mimic what a human might do – which is draw lines on edges or perpendicular to gradients.

The approach is relatively straightforward:

  1. Preprocess the image – Desaturate, normalize, then apply CLAHE and/or a slight blur
  2. Calculate image gradient directions and magnitude (Sobel operator)
  3. Modify gradient magnitudes based on image intensities, so that
  4. Repeat the following, until the peak magnitude in the gradient image drops below a threshold:
    1. Find the peak gradient magnitude (the “strongest contrast point”).  This becomes the “starting point” for a line.
    2. Extend the ends of the line in both directions, perpendicular to the gradient vector.  If the gradient vector “turns,” follow it by low-pass filtering this new angle with the original line angle, and stepping forward in this new direction.
    3. Stop extending the line when the angle changes from the original by more than a threshold, or when the gradient magnitude at the ends of the line drops too far below the original point’s magnitude.
    4. Draw this line on the gradient image, zeroing out pixels it touches.
    5. Record this line in the SVG file.

 

This will progressively draw over the “strongest edges” in the image, until a user-specified termination point is reached.

From a computer-vision edge detection perspective, this is awful at marking edges – the lines will frequently miss real edges, especially if they curve, or are wide / nearby each other.  However, this lends itself well to the output’s artistic appearance:

  • Imprecision in edge locations and angles create messy lines – which tend to look like fast pen sketches.
  • Following edges around curves causes drawn lines to “cut corners,” but additional lines are drawn around said corners, further lending to the “sketchy” appearance.
  • Lines from one edge can extend themselves onto a nearby separate one.
  • Lines can appear at random on high-contrast points that don’t necessarily correspond to a logical edge; in aggregate, these form shaded gradients and accents.

 

I’ve found this works well on video game screenshots – especially objects with low polygon count and relatively simple textures, which makes sense given the basis is straight lines.  It makes it great for making wall art around the house with a pen plotter.

The code can be found on Github and requires relatively few dependencies – OpenCV’s Python bindings are not required here, though I might later port this algorithm to C++/OpenCV for speed.

I’m not quite finished with developing this – I’ve been experimenting with simple Bezier curves, since humans generally don’t draw perfectly straight lines.  I’ll update once I’m satisfied with how that looks.

About the Author