gifcrop.sh

Recently I had to crop a lot of animated gifs down for a project. This isn't hard to do with ImageMagick

$ convert in.gif -coalesce -repage 0x0 -crop WxH+X+y +repage out.gif

…but it does require some repetitive typing and mental arithmetic and rather mysterious incantations if you don't grok what all the coalescing and repaging is about. (I don't.) So I put together this bash script to handle that for me. Didn't Paul Graham say that if your coding is repetitive then the problem isn't your project, it's that you're doing it wrong? Specifically you're operating at the wrong level of abstraction.

Because I found myself repetitively wrangling these ImageMagick commands into shape, I decided it was time to sidestep that problem and make a script to do the drudgery for me. Plus this way I can get things like the aspect ratio before and after the changes computed for me.

#!/bin/bash

if [ -z "$1" ]; then 
  echo "usage: gifcrop.sh infile.gif left right [top] [bottom] [suffix]"
  exit
fi

echo -e "  opening \t ${1}" 1>&2

# There are several ways to extract the name of the file that comes before
# '.gif' so we can rename it with a suffix:
# BASE=${1%.gif}
BASE=$(basename ${1} .gif)
#BASE=$(echo ${1} | sed 's/.gif/\1/')

# Set the margins to be cropped off (Left, Right, Top, Bottom):
L=$2
R=$3
T=${4:-0} #use argv[4] (or 0, if argv[4] is undef or empty)
B=${5:-0} #use argv[5] (or 0, if argv[5] is undef or empty)

SUFFIX=${6:-crop} #use argv[6] (or "crop", if argv[6] is undef or empty)

# Get the original size of the image
W0=$(identify ${1} | head -1 | awk '{print $3}' | cut -d 'x' -f 1)
H0=$(identify ${1} | head -1 | awk '{print $3}' | cut -d 'x' -f 2)

aspectOld=$(printf "%4.3f" $(echo $W0/$H0 | bc -l))
echo -e "  current size \t ${W0}x${H0}\t($aspectOld)" 1>&2

# Calculate the new size of the image
let "W1 = $W0 - ($L + $R)"
let "H1 = $H0 - ($T + $B)"
aspectNew=$(printf "%4.3f" $(echo $W1/$H1 | bc -l))
echo -e "  new size \t ${W1}x${H1}\t($aspectNew)" 1>&2

NEWNAME=${BASE}${SUFFIX}.gif
echo -e "  saving to \t ${NEWNAME}" 1>&2

convert ${1} -coalesce -repage 0x0 -crop ${W1}x${H1}+${L}+${T} +repage ${NEWNAME}

Simply save this as something like gifcrop.sh, and then run it like so:

$ gifcrop.sh coolpic.gif 10 20 30 40 _small

That will take 10 pixels off the left, 20 off the right, 30 from the top and 40 from the bottom. The result gets saved as coolpic_small.gif. The new version is saved as a second file with a suffixed name instead of over-writing because I found that I had to iterate many times to get the correct dimensions, so I wanted both new and original versions available for comparison.

The final three arguments are optional, since most of the time I found myself adjusting the width but leaving the height alone, and I never encountered a situation in which I needed an alternative suffix besides "crop". So these two commands are identical:

$ gifcrop.sh in.gif 10 20 0 0 crop
$ gifcrop.sh in.gif 10 20

This all depends on the format of the results that ImageMagick gives you from the identify command, which is used to get the current size of the input image. You may need to adjust these two lines:

W0=$(identify ${1} | head -1 | awk '{print $3}' | cut -d 'x' -f 1)
H0=$(identify ${1} | head -1 | awk '{print $3}' | cut -d 'x' -f 2)

On my machine, identify foo.gif | head -1 gives me this output:

foo.gif[0] GIF 250x286 250x286+0+0 8-bit sRGB 128c 492KB 0.000u 0:00.030

The awk command isolates the 250x286 part, and the cut command pulls out the two dimensions from that.

I should probably put in --quiet or --verbose options to suppress the output, but honestly I like seeing it as an error/sanity check and adding more optional args would put me in a place where I should really re-write this to take in key/value pairs instead of positionals. As an alternative to a --quiet option, you can just pipe the output of stderr to /dev/null to make it go away if it really bothers you, e.g.:

gifcrop.sh foo.gif 100 100 2> /dev/null

I suppose I can't have a blog post about animated gifs without including at least one animated gif. So here's the most recent use I've had for gifcrop.sh, editing a gif of the "Datasaurus Dozen" for use in the Data Science class I'm teaching.

Datasaurus Dozen
Original gif of twelve very different datasets, all with equivalent summary statistics.
Datasaurus Dozen, cropped.
The same scatter plots, but with the statistics cropped out so as not to ruin the punchline in class.
This entry was posted in CS / Science / Tech / Coding and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *