Wednesday, 2 September 2009

Making Noise with ImageMagick

The Case for Noise

At the @media conference earlier this year, Dan Rubin presented a talk entitled Designing Virtual Realism where he explained that we should try to reproduce real life textures when designing web sites to produce the feel bit in look & feel. If you are as useless with Photoshop or GIMP as I am, it is quite a difficult feat. However, Dan also had advice for visual design challenged people like me: if you do nothing else, at the very least, add noise. What he meant by that is to replace flat background colours with something that is not quite flat, adding subtle noise to the colour. As usual, ImageMagick is a great tool for that sort of things and gives you the opportunity to automate it.

Creating Noise

ImageMagick has an option to create random noise, which makes it very easy to start. Enter the following in a terminal window:

$ convert -size 100x100 xc: +noise Random noise.png

This will produce a PNG image 100 pixels wide by 100 pixels tall that will look something like this:

noise.png

Noise image

Note that because the pixels in this image are completely random, it can be tiled, which is important if we want to use the final image as a tiled background.

Blurring the Noise

Next we need to blur the noise and desaturate it so that we can use it as a mask.

$ convert noise.png -virtual-pixel tile \
 -blur 0x1 -fx intensity -normalize noise-mask.png

Let's decompose this command to understand all the options:

-virtual-pixel tile
tells ImageMagick to use a tile of the picture for virtual pixels that are outside of the boundaries of the original. This is necessary because the blur algorithm uses neighbouring pixels to calculate a given colour. Using that option ensures that the result of the blur can also be used as a tile.
-blur 0x1
applies a fine blur to the image.
-fx intensity
selects the intensity channel only thus desaturating the image.
-normalize
stretches the contrast to ensure we go from pure black to pure white rather than average greys.

And here is the result:

noise-mask.png

Plain Colour Tiles

We then need two plain colour tiles that will be combined into the final image. Both colours should be very close to one another so that the noise is perceivable but not distracting. In this example, I will use two shades of red:

$ convert -size 100x100 xc:white -fill '#cc0000' -opaque white red1.png
$ convert -size 100x100 xc:white -fill '#bb0000' -opaque white red2.png

This is a rather weird syntax so let's explain it:

-size 100x100
creates an image that is 100x100 pixels.
xc:white
uses a pseudo-image that is completely white as a source image.
-fill '#cc0000'
selects the fill colour to be used by any following option, in this case a shade of red.
-opaque white
replaces all white pixels in the image (in this case the whole image) with the fill colour (red).

That's a bit convoluted but I haven't found a way to specify a hex colour value directly after xc:. If anybody knows better, please tell me.

Applying the Mask

The last step is to apply the mask created earlier to mix the two colour tiles:

$ convert red1.png red2.png noise-mask.png -composite red-noise.png

Et voilĂ ! Some noise added to our plain red. And here is how the two compare, plain red on the left, noisy red on the right:

Plain Red vs Noisy Red

The difference is subtle but enough to give an impression of texture.

Script It

Last but not least, let's transform all of the above in a small shell script to be re-used later. Note that I am using bash here, which will work fine on Ubuntu or Mac OS-X but you may need to adapt it if you are using a different operating system:

noise.sh

#!/bin/bash

# Retrieve parameters and assign defaults if not found
size=${1:-50x50}
col1=${2:-#000000}
col2=${3:-#111111}
ofile=${4:-noise.png}

# Set the name of the temporary mask file
mask=/tmp/$$-mask.png

# Create temporary mask
convert -size $size xc: +noise Random -virtual-pixel tile\
        -blur 0x1 -fx intensity -normalize $mask

# Create both colours and compose with mask
convert -size $size xc:white -fill $col1 -opaque white\
        -size $size xc:white -fill $col2 -opaque white\
        $mask -composite $ofile
 
# Delete temporary file
rm $mask

To use it, you need to make it executable first:

$ chmod +x noise.sh

Then it's just a case of giving it a size, two colours and an output file name as parameters:

$ ./noise.sh 100x100 '#00cc00' '#00bb00' green-noise.png

Now, go make some noise and banish those boring plain backgrounds!