Showing posts with label imagemagick. Show all posts
Showing posts with label imagemagick. Show all posts

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!

Wednesday, 27 May 2009

Selective Colouring with ImageMagick

Following a recent Photojojo newsletter, I thought I would do some experimentation with black and white. But rather than use Photoshop or GIMP, I decided to use ImageMagick: the command line may seem like more effort at first but it gives more control and you can also put everything in a script for later re-use.

Choosing a good photograph

There are some photographs that come out great as black and white, others that don't. Generally, the best candidates are the ones that look a bit uninteresting in colour, generally because there isn't much colour in them to start with. It's easy to get that sort of shots in London: just go out on a cloudy day and you'll get likely candidates. And so, I shot this picture on Sunday afternoon while on the South Bank:

The Globe Theatre

Although the building is nice, the picture itself is quite dull: there is little colour in it apart from the red flags so it's an ideal candidate for black and white treatment.

Separating the channels

Following the Photojojo article, rather than transform that photograph into grey scale, I started by separating the red, green and blue channels, which is very easy to do with ImageMagick:

$ convert img.jpg -separate img_rgb_%d.jpg

Assuming your original image is called img.jpg, this will produce three images called img_rgb_0.jpg, img_rgb_1.jpg and img_rgb_2.jpg that correspond to the red, green and blue channels respectively:

Red, green and blue channels

The last one, the blue channel, is the best black and white in my opinion as it's got the strongest shadows and lightest sky. Having said that, the red flags in the original photograph were the main spots of bright colour and it would be interesting to bring them back in the black and white shot through selective colouring. So let's do that.

Creating a mask

To selectively colour the flags red in the black and white picture, I am going to combine the original shot with the blue channel image. For this, I need to create a mask that will tell ImageMagick what part of the original shot to use and what part of the blue channel image to use. For the mask, I need a black and white picture that is white where the flags are and black everywhere else: this way, I can tell ImageMagick to use pixels from the original image where the mask is white and pixels from the blue channel image where the mask is black.

Because the flags are red, the idea is to find all the pixels in the original image that are red and no other colour. We can do that by combining the three channel images. Pixels that are a shade of grey, anywhere between black and white will show as the same shade of grey on all three channels but pixels that are of a specific colour, such as red, will be of a light grey in their primary channel and very dark in the other two. Indeed, if you look at the three channel images, the flags show as very light in the red channel but nearly black in the other two. So to create our mask, we can subtract the green and blue channels from the red one.

First, let's subtract the green channel from the red one:

$ convert img_rgb_1.jpg img_rgb_0.jpg -compose minus \
 -composite img_rgb_0-1.jpg

Which produces the following image:

Green channel subtracted from the red one

We can already see the flags pop out of the picture but we can also still see the outline of the theatre. So let's subtract the blue channel from the image we just obtained:

$ convert img_rgb_2.jpg img_rgb_0-1.jpg -compose minus \
 -composite img_rgb_0-1-2.jpg

Green and blue channels subtracted from the red one

That looks good, except that the flags are now light grey rather than really white. It is still a good mask but as the grey pixels will mix both images we use, it will dilute the effect. To ensure that we have a full effect, we need to stretch the contract of the mask to have the background area really black and the flags area really white.

$ convert img_rgb_0-1-2.jpg -level 10%,30% img_mask.jpg

The values given after the -level option specify the percentage of white under which a pixel is considered black first followed by the percentage of white over which a pixel is considered white. There is no real perfect value for those so you need to experiment. In this example, the second value is quite low because we really want most grey pixels to be turned white for a sharp mask.

Stretched mask

You will note that there are some small dots of white at the bottom of the mask. That's slightly unexpected but we'll see in a minute what they are.

Creating the final picture

Now that we have a mask, we need to combine the blue channel image with the original image by applying the mask. This is the simplest use of the -composite operator:

$ convert img_rgb_2.jpg img.jpg img_mask.jpg \
 -composite img_final.jpg

This produces the final image. We can now see that the small dots of white at the bottom of the mask have let out some of the colour of the red flowers on the lamppost, which is a nice extra touch and which would have been forgotten had we created the mask by hand with a drawing tool. Such side effects are typical of such a tool that works on the whole image and can be welcome or not depending on the situation. In any way, it is always possible to tweak the mask manually in a drawing tool before applying it.

Final image

Bootnote

The photo in its original size is now visible on flickr.