Wednesday 27 March 2019

Hairy Portraits

Using computers to create images that look like they've have been painted with a brush and oils is along standing ambition, with some very realistic results possible in recent years using very sophisticated algorithms.

Here we'll look at a very simple idea that gives surprisingly good results.


Image Based On Another Image

The basic idea is to create an image that uses another image as a source. That source could be a photo, or could even be a painting itself.


Like all digital images, that image is made of tiny coloured pixels.

We build our new image by making marks on a blank canvas. Those marks are coloured according to the colour on the source image at that same location. The following diagram shows this.


You can see that at the bottom left of our new image we've drawn a black square. It's black because on the source image, that same area is coloured black too. The red square is red because it lands on the area where the source image has red lips.

We can draw these squares where our mouse is, creating the illusion of manual painting. If we use circles instead of squares we can create images like this one:


The circles are actually translucent to allow a bit of colour mixing, and also 30 of varying small sizes are drawn at a time, also randomly displaced around the mouse pointer.

The code for this sketch is online:



Just for comparison, here's an image made of squares.



Brush Strokes

What we've done so far is particularly simple and fairly effective in creating moderately interesting. The images look like they've been made with dabbed sponges of paint rather than the stroke of a bristled brush.

Let's see if we can create a more textured brush stroke effect. Brush strokes seem to be made of a group of lines rather than a group of circles or squares.


We could draw a bunch of lines at the mouse position pointing in roughly random directions. Here's the result of a simple implementation of this idea.


That doesn't look like paint brush strokes - it looks more like stars or sprinkles.

A key flaw in that approach is that the brush lines are going in all directions. Let's try an experiment with the strokes moving only in one direction, diagonally down and right.


That's a bit better. The fact that the brush lines move together better reflects what real brush strokes do. However real brush strokes don't all fall exactly and perfectly in a diagonal down and to the right. There's more variety.


Two-Dimensional Noise

We've already seen the the challenge of finding a mathematical function that is random but not too random:

  • Creative Uses for Not Quite Random Noise (link)
  • Randomness and Perlin Noise (link)


To recap, noise across a large scale looks random but at a small scale, its values vary smoothly. It also has a 2-dimensional form which we can use to provide a smoothly changing direction for our brush lines.

Let's first look at this noise. The following shows lines that start at random places on the canvas, but move according to this 2-dimensional noise.


We can see two good things. The overall patten looks broadly random, but looking closely, the lines do roughly follow each other. That gives it a more realistic brush stroke feel.

Here is another portrait rendered following this pattern. The lines are coloured according to the underlying image, which is black and white in this case


Let's see what happens if we go back to using the mouse to drive the rendering:


That's much better. The image has the dynamism of a rapid paint-brushed composition.

Let's introduce colour back into our method.


That's really rather effective, given how simple the idea is.

The clusters of brush strokes, each made of a clump of lines moving roughly together (but random when considered at a large scale), does give the impression of a painting built up from dabs of an artist's brush.

You can try it yourself, and explore the code here:




More Experiments

This simple method can be refined or taken in different directions easily.

This example controls the thickness of the lines (stroke weight) using the luminance of the underlying source image at that point. The dark areas have thin strokes, and the lighter areas, covering most of the subject, have thicker strokes. The results are rather pleasing.


We don't have to have sophisticated calculations. The next image is the result of a constant stroke weight that's moderately broad, and shorter line lengths.


A final example uses curves rather than straight lines to build up a brush stroke. As a further refinement, the darker areas of the source image are given a high translucency so they don't dominate the image.


For me this has the most realistic brush strokes. You can explore the code here:




Potential Refinements

We've implemented a very simple idea - which has proven very effective.

In thinking about potential refinements, the following are clear:

  • the direction of the brush strokes are set by 2-d noise, and not the direction of the mouse
  • the brush strokes themselves could have their texture enhanced by adding higher contrast lines, perhaps black and white at random, which some translucency


As a concluding thought, it is clear that Perlin noise is incredibly versatile and useful.