Some tips on using ImageMagick (HPR Show 1822)

Dave Morriss


Table of Contents

I like to use images in HPR shows if I can. I have experimented with various ways of preparing them since I first started contributing, but I'm particularly impressed with what I am able to do using ImageMagick.

The ImageMagick system contains an enormous range of capabilities, enough for a whole series of shows. I though I would talk about some of the features I use when preparing episodes to give you a flavour of what can be done.

I'm the rawest amateur when it comes to this kind of image manipulation. Just reading some of the ImageMagick documentation (see links) will show you what an enormous number of possibilities there are. I am only using a few in this episode.

Processing photographs

Stripping EXIF metadata

I often take pictures on my digital camera and prefer to remove the EXIF data from them before uploading. Unfortunately ImageMagick doesn't have a feature designed for doing this. It is possible to use:

convert -strip before.jpg after.jpg

This is not recommended. However, there is another way of doing this using exiftool:

exiftool -all= image.jpg

This saves the original image by appending _original to the filename.

Cropping the image

I often want to crop the images I produce because there is extraneous stuff in the edges (due to poor photographic technique mostly). It is possible to use ImageMagick to crop but often I need to use an interactive method.

I used to use the GIMP program to do this, but lately I have found that Krita is a little easier.

Reducing the image size

When preparing an HPR episode I create a directory for all of the files copied off my camera. I often create an images sub-directory drop the pictures there. I use the ImageMagick convert command with the -resize option:

convert bigpic.jpg -resize 640 smallpic.png

This reduces the picture dimensions to 640 pixels wide which fits the HPR web-page better (the aspect ratio is not changed by this operation). The resulting file is also smaller which helps with upload time and server space. The command uses the extension of the output file to determine the resulting format.

Typically the images directory contains the pictures from my camera which have been converted with exiftool. They have the extension .JPG. I run the following command to convert them all:

for f in images/*.JPG; do t=${f##*/}; convert $f -resize 640 ${t%.JPG}.png; done

This traverses all images. The variable t contains the filename without the directory because I want the new files to be saved in the parent directory. In the convert command the output file is specified with variable t which has had the .JPG stripped from the end and replaced with .png.

It's also possible to reduce the image by a percentage rather than trying to reduce to specific dimensions. In this case, the value after -resize would be a percentage such as 50%.

I should really write a script to do this stage of image processing but I have not yet done so.

Making thumbnails

Sometimes, if there are many pictures, I generate thumbnail images for the notes which I can set up to be clickable to get to the bigger image. I would usually make a directory thumbs to hold the thumbnails. The command to make a single thumbnail is:

mogrify -format png -path thumbs -thumbnail 100x100 image.png

I have a script for doing this:

#!/bin/bash
#
# Simple script to generate thumbnail images
#

#
# We expect there to be a file called 'manifest' containing the names of the
# images we want to build thumbnails for.
#
if [[ ! -e manifest ]]; then
    echo "Expected a manifest file, but none exists"
    exit
fi

#
# We might need to create the sub-directory
#
if [[ ! -e thumbs ]]; then
    mkdir thumbs
fi

#
# Process the files in the manifest, making thumbnails
#
for f in $(cat manifest); do
    mogrify -format png -path thumbs -thumbnail 100x100 $f
done

exit

You can use the mogrify command to do the whole thing without the script with a command such as the following:

mogrify -format png -path thumbs -thumbnail 100x100 *.png

This assumes you want to generate thumbnails for all images in the current directory, but this is not always what I want to do.

Doing stuff to thumbnails

In an HPR episode I've been creating recently I added a border and a watermark to my thumbnails. I did it this way:

#!/bin/bash
#
# Simple script to add a number "watermark" and a border to a collection of
# thumbnails. Run it in the parent directory.
#

#
# We expect there to be a file called 'manifest' containing the names of the
# thumbnails. These are the same names as the main images but in the 'thumbs'
# directory
#
if [[ ! -e manifest ]]; then
    echo "Expected a manifest file, but none exists"
    exit 1
fi

#
# Check there are thumbnails
#
if [[ ! -e thumbs ]]; then
    echo "No 'thumbs' directory, can't continue"
    exit 1
fi

i=1

#
# Process the thumbnails
#
for f in $(cat manifest); do
    #
    # Save the original file
    #
    o="${f%%.*}_orig.png"
    mv thumbs/$f thumbs/$o

    #
    # Convert the original adding a numeric "watermark" and creating the
    # original name again
    #
    convert thumbs/$o -font Courier -pointsize 20 \
        -draw "gravity center \
        fill black text 0,12 '$i' \
        fill white text 1,11 '$i'" \
        thumbs/$f

    #
    # Add a border into the same file
    #
    convert thumbs/$f -shave 1x1 -bordercolor black -border 1 thumbs/$f

    ((i++))
done

exit

Adding captions to images

Also, in an HPR show I'm currently putting together I decided to try adding captions to pictures. I made a file containing the names of the image files followed by the caption.

Here's the rough and ready script I made to do this:

#!/bin/bash

#
# Rudimentary script to add captions to images
#

#
# The captions are in the file 'captions' so check it exists
#
if [[ ! -e captions ]]; then
    echo "Missing 'captions' file"
    exit 1
fi

#
# Read lines from the captions file (use 'read' to prevent Bash treating
# spaces as argument delimiters)
#
while read l; do
    #
    # Split the line into filename and caption on the comma
    #
    f=${l%%,*}
    c=${l##*,}

    #
    # Save the original file
    #
    o="${f%%.*}_orig.png"
    mv $f $o

    #
    # Add the caption making the new file have the original name
    #
    convert $o -background Khaki  label:"$c" -gravity Center -append $f

done < captions

exit

The captions file has lines like this:

Flours_used.png,Flours used in the demonstration
Kenwood_Chef.png,Kenwood Chef and accessories

This is not elegant or very robust but it did the job. Feel free to develop this further if you want.

Joining images together

Again, while preparing an HPR show I wanted to do some unusual image manipulation. This time I wanted to shrink two images and join them together side by side to make a final image. The shrinking was no problem, as we have already seen, but I searched for an answer to the join question and found the following:

convert -background '#FFF9E3' xc:none -resize 200x1\! left.png -append right.png -gravity south +append +repage joined.png

However, a better method is to use montage such as:

montage -background '#000000' -geometry 1x1\<+1+1 left.png right.png montage.png

This tiles the two images on a black background. The -geometry option defines how big the tiles are and how much border space to leave. The special 1x1\< sequence makes ImageMagick find the best fit - it keeps the images the same size as the originals.

I don't really understand how the convert example works. I found it at https://stackoverflow.com/questions/12076293/combine-2-images-side-by-side-into-1-with-imagemagick-php. The montage example is a little more straightforward.