LJ Archive

Work the Shell

Image Manipulation with ImageMagick

Dave Taylor

Issue #275, March 2017

Dave switches gears this month and begins delving into the more functional topic of image manipulation.

In my last article, I had some fun looking at the children's game of rock, paper, scissors, writing a simple simulator and finding out that some strategies are better than others. Yes, I used “strategy” and “rock, paper, scissors” in the same sentence!

So for this article, I thought it would be interesting to delve into something more functional and pragmatic: image manipulation. Ordinary shell scripts don't tend to do much with images because you can't display anything from the command line.

But let's be honest here. The chance that you're running Linux or a similar command-line interface raw on a computer terminal is pretty darn low. More likely, you've got a terminal window open on your X11 system or, like I often have, you're running a command-line interface app within a modern OS like Mac OS X. And this means, yes, you do have the ability to display graphics, just not within the terminal app itself.

Get Yourself a Copy of ImageMagick

The first step is to download and install a copy of the ImageMagick suite of graphics-related commands. You already might have it installed if you're lucky: Just type convert -version, and if you have it installed, you'll see something similar to this:

$ convert -version
Version: ImageMagick 6.9.6-6 Q16 x86_64 2016-12-31 
 ↪http://www.imagemagick.org
Copyright: Copyright (C) 1999-2016 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules 
Delegates (built-in): bzlib djvu fftw fontconfig freetype gslib 
 ↪jbig jng jp2 jpeg lcms ltdl lzma openexr png ps tiff 
 ↪webp x xml zlib

If you don't have it installed, it can be quite a task to get it all up and running. Everything lives at www.imagemagick.org, which is where you want to get started.

On a Linux system, you can use the package manager of choice for your distro. You can grab a compressed tar image from the site, or you can use rpm, like this:

rpm -Uvh ImageMagick-7.0.4-1.x86_64.rpm

Of course, there's a bit more to it, but that'll get you started.

On a Mac, you'll want to start by installing MacPorts (www.macports.org), which you can't do until you install Xcode (free from Apple, get it through the App Store). Once you've installed Xcode and MacPorts, you can install ImageMagick, and you're good to go.

You know you're good to go when the test command convert -version returns something meaningful. As always, when you install new software, you'll want to log out and log in again for the PATH changes and shell command-line hash to include all the newest programs.

Converting Image Formats

One of the most useful tasks ImageMagick can help you with is converting image file formats. It's a remarkably well built suite of programs and can read or write more than 200 different formats. Don't believe me? Try this command:

convert -list format | more

Among the most common formats that you'll actually encounter in your day-to-day computer usage are the following:

  • BMP: MS Windows bitmapped image.

  • GIF: Graphics Interchange Format.

  • JPG: JPEG image format.

  • PNG: Progressive Network Graphic format.

  • TIFF: Tagged Image File Format.

ImageMagick knows oodles of other formats too, including all the major video formats (MKV, MP4, AVI, MOV). It also can convert things like EPSF (Encapsulated Postscript) and even PDF (Portable Document Format), which can be useful in specific instances.

Armed with that knowledge, conversion between image file formats is really ridiculously simple. Let's say you want to convert an image from JPEG to PNG. It's as simple as:

convert image.jpeg image.png

Since the ImageMagick utilities are glob-aware (that is, you can use wild cards and specify multiple filenames), you also can convert a group of GIF images to JPG with the convert command or, more easily, with its cousin mogrify:

mogrify -format jpg *.gif

Let's give it a whirl with a folder that contains a half-dozen GIF images, using ls to show the folder contents before and after the mogrification (is that a word?):

$ ls -s
total 272
  8 add-to-google-reader.gif	 24 blogger-1.gif		  
  8 dave.gif                    8 add-to-newsgator.gif	 
 24 blogger-2.gif		     176 manga.gif
 16 aw-logo.gif			  8 blogger-3.gif
$ mogrify -format jpg *gif
$ ls -s
total 752
  8 add-to-google-reader.gif	 24 blogger-1.gif
  8 dave.gif                    8 add-to-google-reader.jpg
112 blogger-1.jpg		       8 dave.jpg
  8 add-to-newsgator.gif	      24 blogger-2.gif
176 manga.gif                   8 add-to-newsgator.jpg	
128 blogger-2.jpg		     168 manga.jpg
 16 aw-logo.gif			  8 blogger-3.gif
 24 aw-logo.jpg			 24 blogger-3.jpg

Simple enough. Use convert for individual images and mogrify for bulk conversions. It'd be an easy script to differentiate between these two cases and invoke the correct command with the correct arguments too. I'll leave that up to you!

Checking Image Sizes

Another useful feature of the ImageMagick suite is to be able to identify the dimensions of a graphic image. The latest version of the file command can offer this information on some systems:

$ file manga*
manga.gif: GIF image data, version 89a, 358 x 313
manga.jpg: JPEG image data, JFIF standard 1.01, 
 ↪aspect ratio, density 1x1, segment length 16, 
 ↪baseline, precision 8, 358x313, frames 3
manga.png: PNG image data, 358 x 313, 8-bit/color RGB, 
 ↪non-interlaced

But on most Linux systems, one or more of these would exclude the actual dimensions. Further, look closely at the above output, and you'll see it's quite inconsistent, making it difficult to parse out the dimensions if you don't encode specific rules for each format—which is, uh, lame.

Instead, you can glean image size with the identify command, as shown here:

manga.gif GIF 358x313 358x313+0+0 8-bit sRGB 256c 88.5KB 
 ↪0.000u 0:00.000
manga.jpg JPEG 358x313 358x313+0+0 8-bit sRGB 85.4KB 0.000u 
 ↪0:00.000
manga.png PNG 358x313 358x313+0+0 8-bit sRGB 266KB 0.000u 
 ↪0:00.000

That's better. It's consistently the third parameter, which means that a simple script can strip out everything but the image dimensions:

$ for image in manga*; do   identify $image | cut -f1,3 -d\  ; done
manga.gif 358x313
manga.jpg 358x313
manga.png 358x313

Easy enough, and notice that the cut command is invoked both with a space as the default field delimiter and specifying that you want field 1 and 3 but none of the others.

And Next Month...

Okay, ImageMagick is complicated. In fact, I didn't really get much into scripting this month. But, come back next month; I'll explain how to turn all this knowledge of convert, mogrify and identify into some pretty sick scripts. See you then!

Dave Taylor has been hacking shell scripts on UNIX and Linux systems for a really long time. He's the author of Learning Unix for Mac OS X and Wicked Cool Shell Scripts. You can find him on Twitter as @DaveTaylor or reach him through his tech Q&A site: www.AskDaveTaylor.com.

LJ Archive