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


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

No comments: