ImageMagick

These notes draw heavily from the ImageMagick usage examples but have a top-down focus for specific tasks that come my way.

Mini How-to

To convert an image from color to grayscale:

-> convert -colorspace gray color.jpg gray.jpg
-> convert -colorspace gray *.jpg gray%d.jpg
-> mogrify -colorspace gray *.jpg

To make a fav icon from an image file, for example:

-> convert -resize 32x32 logo.png favicon.ico

Identifying Fonts

Use informational option "-list font" with identify (or another IM command) to list the fonts that IM finds on your system:

-> identify -list font

Path: /etc/ImageMagick/type-ghostscript.xml
  Font: AvantGarde-Book
    family: AvantGarde
    style: Normal
    stretch: Normal
    weight: 400
    glyphs: /usr/share/fonts/default/Type1/a010013l.pfb
...
Path: System Fonts
  Font: Accanthis-ADF-Std-Bold
    family: Accanthis ADF Std
    style: Normal
    stretch: Normal
    weight: 700
    glyphs: /usr/share/fonts/adf-accanthis/AccanthisADFStd-Bold.otf
...

Each "Path" line shows the source of the subsequent fonts. Here, IM consulted a configuration file of its own (type-ghostscript.xml) in addition to the system's fonts. For each font, the "Font" entry gives the name used in IM, and the "glyphs" entry gives the file implementing that font.

What resources IM checks for fonts depends on how IM is configured and how it was built. On my system, IM first consults /etc/ImageMagick/type.xml, which simply includes the file type-ghostscript.xml reported above. It also checks a few other locations for files named type.xml and merges the contents of what it finds, but my system does not have any more of these files. IM then confers with Fontconfig to locate system fonts. Use of Fontconfig is a build-time option:

-> identify -list configure | grep DELEGATES
DELEGATES     ... fontconfig ...

Alternatively:

 -> ldd `which identify` | grep font
	libfontconfig.so.1 => /lib/libfontconfig.so.1 (0x41d0d000)

Fontconcig has its own notions about where to look for fonts; for example:

-> grep -oP "<dir>.+?</dir>" /etc/fonts/fonts.conf
<dir>/usr/share/fonts</dir>
<dir>/usr/share/X11/fonts/Type1</dir>
<dir>/usr/share/X11/fonts/TTF</dir>
<dir>/usr/local/share/fonts</dir>
<dir>~/.fonts</dir>

See the fonts-conf manual for additional details and options.

Since IM uses system fonts, the set of fonts available to IM will vary by host. On my system, IM finds 225 font names corresponding to 191 glyph files:

-> identify -list font | grep Font:   | sort | uniq | wc -l
225
-> identify -list font | grep glyphs: | sort | uniq | wc -l
191

The extra 34 font names come from the file type-ghostscript.xml, which adds aliases associating standard PostScript font names with glyph files for similar fonts. In this way IM provides the familiar Postscript typefaces--Avant Garde, Bookman, Courier, Helvetica, New Century Schoolbook, Palatino, Times, and Symbol.

Postscript Fonts

IM supports the standard PostScript printer fonts by aliasing their names to free versions of these fonts donated by URW++ Design and Development (package urw-fonts) and distributed under the GPL. PostScript's AvantGarde family becomes URW Gothic L; Bookman becomes URW Bookman L; Courier becomes Nimbus Mono L; Helvetica becomes Nimbus Sans L; New Century Schoolbook becomes Century Schoolbook L; Palatino becomes URW Palladio L; Times becomes Nimbus Roman No9 L; and Symbol becomes Standard Symbols L.

Configuration file /etc/ImageMagick/type-ghostscript.xml defines 34 aliases:

-> grep "name=" /etc/ImageMagick/type-ghostscript.xml | wc -l
34
-> perl -ne 'm<\bname="(.+?)"> and print "$1\n"' /etc/ImageMagick/type-ghostscript.xml
AvantGarde-Book
AvantGarde-BookOblique
...
Times-BoldItalic
Symbol

It ties each PostScript font name to a URW glyph file, which embeds the URW font name. For example, the Times-Roman entry ties that name to file n021003l.pfb, which implements Nimbus Roman No9 L Regular:

-> grep Times-Roman /etc/ImageMagick/type-ghostscript.xml 
  <type name="Times-Roman" ... glyphs="/usr/share/fonts/default/Type1/n021003l.pfb"/>
-> fc-list :file=/usr/share/fonts/default/Type1/n021003l.pfb
Nimbus Roman No9 L:style=Regular

The upshot is that both of the IM font names Times-Roman and Nimbus-Roman-No9-Regular yield the same font (via "-font").

Package urw-fonts installs 35 PostScript Type1 fonts (to /usr/share/fonts/default/Type1), and IM finds all of these through Fontconfig. In addition, type-ghostscript.xml associates 34 aliases with 33 of these URW fonts. It omits Dingbats Regular (d050000l.pfb) and URW Chancery L Medium Italic (z003034l.pfb), and it defines two aliases ("fixed" and Helvetica) for Nimbus Sans L Regular (n019003l.pfb). For the record, here's the accounting--poetically verbose and terse at the same time:

-> rpm --query --list urw-fonts | grep '.pfb' | tee pfb-urw | wc -l
35
-> perl -pe 's{/\w{8}\.pfb$}{}' pfb-urw | uniq   # Pluck the paths.
/usr/share/fonts/default/Type1
-> fc-list : file | grep Type1 | perl -pe "s/: $//" | sort | tee pfb-fc-type1 | wc -l
35
-> diff --report-identical pfb-urw pfb-fc-type1 
Files pfb-urw and pfb-fc-type1 are identical
-> grep -oP '[\w/]+\.pfb' /etc/ImageMagick/type-ghostscript.xml | sort | tee pfb-imps | wc -l
34
-> uniq pfb-imps | wc -l
33
-> grep --count -E 'd050000l|z003034l' pfb-imps 
0
-> uniq --count --repeated pfb-imps 
      2 /usr/share/fonts/default/Type1/n019003l.pfb
-> perl -ne 'm/n019003l/ and m/ name="(.+?)"/ and print "$1\n"' /etc/ImageMagick/type-ghostscript.xml
fixed
Helvetica
-> uniq pfb-imps > a.cmp
-> grep --invert-match -E 'd050000l|z003034l' pfb-urw > b.cmp
-> diff --report-identical-files a.cmp b.cmp
Files a.cmp and b.cmp are identical

Showing Font Samples

To see what some fonts actually look like, you can have IM create an image of sample text. The label pseudo-image makes this easy for a simple exhibit of font names in their namesake fonts:

-> convert -size x18 -pointsize 14 -gravity west \
   -font AvantGarde-Book              label:AvantGarde-Book              \
   -font AvantGarde-BookOblique       label:AvantGarde-BookOblique       \
   -font AvantGarde-Demi              label:AvantGarde-Demi              \
   -font AvantGarde-DemiOblique       label:AvantGarde-DemiOblique       \
   -font Liberation-Serif-Regular     label:Liberation-Serif-Regular     \
   -font Liberation-Serif-Bold        label:Liberation-Serif-Bold        \
   -font Liberation-Serif-Italic      label:Liberation-Serif-Italic      \
   -font Liberation-Serif-Bold-Italic label:Liberation-Serif-Bold-Italic \
   -append im-fonts-x1.png

It's perhaps more useful to compare candidate fonts by looking at the same text rendered in them; modify the above example like so:

   label:'"Hello World!" in AvantGarde-DemiOblique'

The next, slightly more-involved example taps the canvas pseudo image and the -annotate command to create an alternative exhibit. Just for fun, it also uses -border to imitate rules on paper.

-> convert -size 200x18 -pointsize 14 -gravity west \
   \( canvas: -font Helvetica      -annotate 0  Helvetica      -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Tuffy-Regular  -annotate 0  Tuffy-Regular  -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Palatino-Roman -annotate 0  Palatino-Roman -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Times-Roman    -annotate 0  Times-Roman    -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Utopia-Regular -annotate 0  Utopia-Regular -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Steve-Hand     -annotate 0  Steve-Hand     -annotate +110+0 'Hello World!' \) \
   \( canvas: -font Courier        -annotate 0  Courier        -annotate +110+0 'Hello World!' \) \
   -bordercolor LightSteelBlue1 -border 0x1 -append im-fonts-x2.png

These examples easily generalize, but manually concocting the image stack for convert gets tedious in a hurry. A little Perl code will sweeten the job.

Image Formats and Built-in Images

ImageMagick supports over one-hundred major image formats and even more sub-formats. The actual number depends on how IM was configured and built. To see the (likely long) list of image formats supported by your installation:

-> identify -list format
   Format  Module    Mode  Description
-------------------------------------------------------------------------------
      3FR  DNG       r--   Hasselblad CFV/H3D39II
        A* RAW       rw+   Raw alpha samples
      AAI* AAI       rw+   AAI Dune image
       AI  PDF       rw-   Adobe Illustrator CS2
      ART* ART       rw-   PFS: 1st Publisher Clip Art
      ARW  DNG       r--   Sony Alpha Raw Image Format
      AVI  MPEG      r--   Microsoft Audio/Visual Interleaved
      AVS* AVS       rw+   AVS X image
...
* native blob support
r read support
w write support
+ support for multiple images

Version 6.7.5-6 on my Fedora 17 system has 206 formats overall:

 -> identify -list format | grep -P ' [-r][-w][-+] ' | wc -l
206

In addition to supporting images in these formats, ImageMagick provides tools to generate images on the fly. The built-in images are handy for trying out ImageMagick. These are magick:granite, magick:rose, magick:logo, magick:netscape, and magick:wizard. Pseudo-images formats run an algorithm to insert an image into the stack. Examples include canvas, label, print, scan, and tile. There are many built-in patterns as well, like pattern:brick, pattern:checkerboard, and pattern:hexagons.

Resizing an Image Sequence

These examples demonstrate various ways to resize a sequence of images taken from a camera. They primarily address the task of reducing high-resolution originals to 640x480 versions, more suitable for online viewing. Both mogrify and convert easily do the trick. Variety stems from choices in preserving the original images or relabeling adjusted versions.

Here are the original image files:

-> ls *.JPG
IMG_0001.JPG  IMG_0005.JPG  IMG_0009.JPG  IMG_0011.JPG  IMG_0015.JPG  IMG_0017.JPG  IMG_0026.JPG
IMG_0004.JPG  IMG_0007.JPG  IMG_0010.JPG  IMG_0014.JPG  IMG_0016.JPG  IMG_0021.JPG

Note the gaps in the sequence's numbering.

You can let mogrify resize the images. To preserve the originals, however, tell mogrify to put the resized images in a separate directory:

-> mkdir IMG-640x480
-> mogrify -resize 640x480 -path IMG-640x480 *.JPG

Note that mogrify preserves the names of the originals:

-> ls IMG-640x480
IMG_0001.JPG  IMG_0005.JPG  IMG_0009.JPG  IMG_0011.JPG  IMG_0015.JPG  IMG_0017.JPG  IMG_0026.JPG
IMG_0004.JPG  IMG_0007.JPG  IMG_0010.JPG  IMG_0014.JPG  IMG_0016.JPG  IMG_0021.JPG

You can verify the new size with identify:

-> identify -format "%f: %G; %b.\n" IMG_0001.JPG IMG-640x480/IMG_0001.JPG 
IMG_0001.JPG: 1600x1200; 498878B.
IMG_0001.JPG: 640x480; 70484B.

If the uppercase file extension "JPG" from your camera offends your sensibilities, you can get lowercase file extensions and coincidentally preserve your originals with one deft option:

-> mogrify -resize 640x480 -format jpg *.JPG
-> ls IMG_*
IMG_0001.jpg  IMG_0005.jpg  IMG_0009.jpg  IMG_0011.jpg  IMG_0015.jpg  IMG_0017.jpg  IMG_0026.jpg
IMG_0001.JPG  IMG_0005.JPG  IMG_0009.JPG  IMG_0011.JPG  IMG_0015.JPG  IMG_0017.JPG  IMG_0026.JPG
...
-> identify -format "%f: %G; %b.\n" IMG_0001.JPG IMG_0001.jpg
IMG_0001.JPG: 1600x1200; 498878B.
IMG_0001.jpg: 640x480; 70484B.

Of course, you can choose simply to replace each original with its resized offspring and leave it at that:

-> identify -format "%f: %G; %b." IMG_0001.JPG
IMG_0001.JPG: 1600x1200; 498878B.
-> mogrify -resize 640x480 *.JPG
-> identify -format "%f: %G; %b." IMG_0001.JPG
IMG_0001.JPG: 640x480; 70484B.

Alternatively, use convert to relabel the sequence while resizing:

-> convert -resize 640x480 IMG_*.JPG -scene 1 beach_%02d.jpg
-> ls  img_*.jpg
-> ls *.jpg
beach_01.jpg  beach_03.jpg  beach_05.jpg  beach_07.jpg  beach_09.jpg  beach_11.jpg  beach_13.jpg
beach_02.jpg  beach_04.jpg  beach_06.jpg  beach_08.jpg  beach_10.jpg  beach_12.jpg

Note how convert renumbers the images from 1 to 13 and, along with the "%02d" format, prepends "0" for numbers 1 through 9. You could also tell convert to put the new images in a separate directory like so:

-> mkdir web
-> convert -resize 640x480 IMG_*.JPG -scene 1 web/beach_%02d.jpg

Montage

This example aligns twenty thumbnail images into a collage suitable for printing on an 8.5 inch by 11 inch sheet in landscape mode. The montage command does all the work, both producing a tile for each source image and arranging the tiles into the final collage:

-> montage \
   -font Bookman-Demi -pointsize 18 \
   -label "Homestead"           ./pics/building.jpg    \
   -label "Garden"              ./pics/garden.jpg      \
   -label "Sunset"              ./pics/sunset.jpg      \
   -label "Zzzz"                ./pics/tent.jpg        \
   -label "Snowbound"           ./pics/needham.jpg     \
   -label "PB Winter"           ./pics/pb-winter.jpg   \
   -label "PB Spring"           ./pics/pb-spring.jpg   \
   -label "PB Summer"           ./pics/pb-summer.jpg   \
   -label "PB Fall"             ./pics/pb-fall.jpg     \
   -label "PB Shadowy Figure"   ./pics/pb-hiker.jpg    \
   -label "AT Ridgeline"        ./pics/at-ridge.jpg    \
   -label "AT Fern Carpet"      ./pics/at-ferns.jpg    \
   -label "AT Duff Carpet"      ./pics/at-creek.jpg    \
   -label "AT Valley Vista"     ./pics/at-vista.jpg    \
   -label "AT Saprophyte"       ./pics/at-fungus.jpg   \
   -label "Navesink Lighthouse" ./pics/nj-navesink.jpg \
   -label "Stone Harbor Beach"  ./pics/nj-stone-harbor.jpg \
   -label "Lake Berryessa"      ./pics/ca-berryessa.jpg    \
   -label "Rockville Hills"     ./pics/ca-rockville.jpg    \
   -label "Colophon"            ./pics/colophon.png        \
   -geometry 260x195+10+10 -shadow -tile 5x4 \
   -title "A Wandering Collage" \
   ./collage.jpg

The source images in directory pics are full size, either 1600×1200 or 640×480. The tile for each image comprises a 260×195 thumbnail with a 10 pixel border (-geometry 260x195+10+10), a caption below the thumbnail (-label), and a shadow behind the thumbnail (-shadow). Note that an image's label is given just before the image's file is listed. The aggregate image displays the tiles in four rows of five columns (-tile 5x4).

On a side note, a PNG (or MIFF) image can embed a label, and montage will automatically use that label. The next sequence recasts the previous example to demonstrate this potentially handy feature. First, label each image:

-> mogrify -label "Homestead" ./pics2/building.png 
-> mogrify -label "Garden"    ./pics2/garden.png   
...
-> mogrify -label "Colophon"  ./pics2/colophon.png 

Then let montage do the rest:

-> montage \
   -font Bookman-Demi -pointsize 18 \
   ./pics2/building.png     ./pics2/garden.png          ./pics2/sunset.png       \
   ./pics2/tent.png         ./pics2/needham.png         ./pics2/pb-winter.png    \
   ./pics2/pb-spring.png    ./pics2/pb-summer.png       ./pics2/pb-fall.png      \
   ./pics2/pb-hiker.png     ./pics2/at-ridge.png        ./pics2/at-ferns.png     \
   ./pics2/at-creek.png     ./pics2/at-vista.png        ./pics2/at-fungus.png    \
   ./pics2/nj-navesink.png  ./pics2/nj-stone-harbor.png ./pics2/ca-berryessa.png \
   ./pics2/ca-rockville.png ./pics2/colophon.png \
   -geometry 260x195+10+10 -shadow -tile 5x4 \
   -title "A Wandering Collage" \
   ./collage2.jpg

Embedding captions instead of labels yields the same effect (via command -label '%[caption]'). Of course, there's little to be gained here if you do not wish to retain labels or captions in the images. (The -label and -set commands also provide options to override automatic labeling.)

An Email Postcard

Coming, eventually ...