Today’s ImageMagick lesson covers how to resize images, change case on file extensions, convert file formats, construct a proof sheet of thumbnails, and search and sort photos by their Exif data.
The ImageMagick suite of image processing and manipulating commands has been around forever, and lurks in all kinds of places: it is the image-processing backend in Drupal, Lyx, OpenShot, and many more. ImageMagick is über-powerful, and because it is a command-line program you can build clever scripts with it and automate routine tasks. You can flip, mirror, resize, distort, shear, and rotate images; do special effects, edit colors, and draw lines and shapes; create thumbnails, galleries, and proof sheets. It supports a skillion image formats and has APIs for a horde of programming languages, such as MagickCore (C), MagickWand (C), Magick++ (C++), JMagick (Java), RMagick (Ruby), TclMagick (Tcl/TK), and several more.
The most worthy use of ImageMagick I have ever seen is to manipulate images for the Upside-down-Ternet, which is a slick script that uses iptables
and ImageMagick to have fun with freeloaders who poach your wi-fi. It re-directs pages to Kittenwar! and mangles images on other pages.
man imagemagick
lists all the commands. There is no imagemagick
command. The display
command opens a graphical editing interface (fig. 1).
The original image was too large, so I resized it with convert
:
$ convert fig-1.png -resize 550x fig-1-s.png
The resize
value is the width in pixels. Width always comes first, and you can make sure by putting an x after the value, with no space. When you specify only the width then your new image will be exactly that width, and the height will automatically be in proportion. When you want to specify the height, give the height value prefixed with an x, like this:
$ convert fig-1.png -resize x250 fig-1-s.png
One thing you have to watch with the various ImageMagick commands is overwriting your original images. With convert
you preserve your original and create the new file by giving it a different name.
Stop Shouting
Windows and a lot of cameras are like old people on Facebook: they shout at you in uppercase. My Canon camera writes filenames in uppercase, so they look like WP7_4117.CR2. You can easily make all file extensions lowercase with this Bash one-liner:
$ for file in *.CR2; do mv $file ${file%%.CR2}.cr2; done
Convert Formats
The convert
command converts image file formats. You can modify the upper-to-lowercase incantation to batch-convert formats, like this example that converts .png
to .jpg:
$ for file in *.png; do convert $file ${file%%.png}.jpg; done
This preserves your originals and creates new images in the new format with the same filename, like this:
$ ls bittersweet.jpg bittersweet.png juniper-berries.jpg juniper-berries.png
The default quality level for .jpg files converted from other formats is 92. You can control it with the -quality
option with values from 1 to 100, with 1 being maximum compression and crappiest quality, to 100 which is best quality and least compression. This example uses 75, which is a decent level for Web images:
$ for file in *.png; do convert -quality 75 $file ${file%%.png}.jpg; done
Proof Sheet
Now that you have a nice batch of images to admire, make a proof sheet so you can admire them all in a single .jpg:
$ montage -label '%f'-geometry 350x+7+7 '*.jpg' proof-sheet.jpg
This results in something like figure 2, with each image 350 pixels wide, borders of 7 pixels, and the filenames of each image. If you like a nice frame instead of a plain border, -frame [value in pixels]
creates a border around each image, and -mattecolor [color]
lets you select the border color if you don’t like the default meh gray. Color Names tells you the color codes.
As with all ImageMagick commands there are a squillion options. Use -pointsize 12
to set a font size of 12 points, or other sizes as you desire, and these options let you fine-tune the labels:
- %b — file size on bytes
- %m — file format
- %G — image dimension in pixels
- %Q — image compression level.
There are way more, and you can find them all at ImageMagick Escapes.
Extracting Exif
The identify
command reads the Exif data of photographs. You can easily view a basic set of information:
$ identify kitten.jpg kitten.jpg JPEG 3648x2736 3648x2736+0+0 8-bit DirectClass 4.402MB 0.000u 0:00.000
Want to see everything there is to know about your photos? Try this:
$ identify -verbose kitten.jpg
That is a deluge of data, so you can fine-tune it to look for specific information, like which photos with the .cr2 extension in the current directory were taken with a flash:
$ for file in *.cr2; do identify -format '%[exif:flash] [%f]' $file; done 16 [WP7_4272.cr2] 16 [WP7_4273.cr2] 9 [WP7_4274.cr2] 9 [WP7_4275.cr2]
So…9 and 16. Okay. What do those mean? The Exif flash tags are combinations of the following values:
0: FlashDidNotFire 1: FlashFired 2: StrobeReturnLightDetected 4: StrobeReturnLightNotDetected 8: CompulsoryFlashMode 16: AutoMode 32: NoFlashFunction 64: RedEyeReductionMode
So 16 means the camera was in auto flash mode, but the flash did not fire. 9 means compulsory flash mode, and it did fire (8 + 1). So you can add an egrep
incantation to find only the images where a flash was fired:
$ for file in *.cr2; do identify -format '%[exif:flash] [%f]' $file | egrep ^'1 |9 |17 |65 ' ; done
Note the spaces after each number in the egrep
search pattern. That limits your search to those exact numbers, and filters out larger numbers like 11 and 9897 and such.
You can dig up any Exif tag you want with identify
, and ImageMagick Escapes describes dozens of escapes to use. But it’s not a complete list, so the quickest way to see what tags you can search on is to run identify -verbose [filename]
, and then look at the output. It’s going to be different for different image file formats, and it’s going to differ according to whether the images have been edited, and the software used. Here is an abbreviated example:
$ identify -verbose WP7_4275.cr2 date:create: 2014-01-29T16:35:34-08:00 date:modify: 2014-01-29T16:35:34-08:00 dng:Aperture: F5 dng:FocalLength: 28.0 mm dng:ISOSpeed: 640 dng:Lens: Canon EF 24-105mm f/4L IS dng:Model: EOS 7D exif:DateTime: 2013:12:25 15:59:28 exif:Flash: 9
So you could extract some of these this way:
$ identify -format '%[exif:datetime] %[dng:aperture] %[dng:lens] %[dng:model]' WP7_4275.cr2 2013:12:25 15:59:28 F5 Canon EF 24-105mm f/4L IS EOS 7D
This gives you a powerful way to find photos with particular traits, such as lens, camera, flash or no flash, date, focal length, size, bit depth…if it’s in Exif you can find and sort it.
One more fun Exif tip: if you’re going to post photos online, you might want to strip the Exif data so you don’t give away too much information. You can do this with the mogrify
command:
$ mogrify -strip kitten.jog
Or strip a whole directory of photos:
$ mogrify -strip /imagesforweb/*
Weird and true Exif fact: Even though it is a widely-used standard, it is not maintained by anyone. The current version, 2.3, was released in 2010 by Japan Electronics and Information Technology Industries Association (JEITA) and Camera and Imaging Products Association (CIPA). Since then it has been orphaned.