Thursday, May 2, 2013

Trimming Transparency from Images

When you export images from some programs, you wind up in some cases with the actual content as a small area of coloured pixels in a sea of alpha transparent nothing.

This happens to me when I'm animating my character around and I'm doing so against a background that is say 1024x768 where the actual character's image is a tiny percentage of that size.

A 1024x768 png from my animation program
This is completely to be expected - the designers of your other programs always have to assume that your export is intended to be the size of your original canvas; but when you have dozens or maybe even hundreds of frames of animation all with wasted transparent pixels, removing it all by hand using GiMP or Photoshop is agonizing.

When you're using TexturePacker or ZwopTex your transparent pixels are probably removed nicely for you when the sprites are stored, but still they can mess up your hit testing and on-screen positioning for your game.

The image content I want with extra transparent pixels cropped away
Here is a way to crop those transparent pixels automatically in large batches, and the program to do it is free.

OK, this is what you need - the ImageMagick program from ImageMagick.org - a fantastic and very powerful set of command line tools.

I'm on MacOSX Mountain Lion and found this great installer which the guys at Cactus Labs have put together - it was just case of clicking through and it all just worked!

Once you have ImageMagick installed check that it is working by opening up a command line - on MacOSX, just do <command>-<space> to access Spotlight and type "Term" and you should be able to hit enter on the first match to get the Terminal.app program running.  Then at the command prompt type:   convert -version


~/MyArtWork sez$ convert -version
Version: ImageMagick 6.8.4-8 2013-04-08 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2013 ImageMagick Studio LLC
Features: DPC OpenMP
Delegates: bzlib jng jp2 jpeg lcms png tiff xml zlib

That shows that the ImageMagick command line tool called "convert" is up and running. The script I'm about to show you won't work if this command is not available.  If you had a Terminal running when you installed ImageMagick you may need to close and re-open it to get this to work.

Now the basic technique is to use two commands that convert knows about:
From the command line the trim command looks like this:

convert my_image.png -trim +repage my_image_trimmed.png

The trim command is what does all the work - it samples the image at the corners and assumes those pixels are what the background of your image is.  Then it crops in the image as small as it can on all four sides - providing that the only pixels it is throwing away are that background color it sampled.  It works with all colours of background, not just alpha transparent (which ImageMagick calls the colour name "none").

Now there's a trick here - what if one some of your frames the sprite just happens to be touching in the corner where the sample is made?  Oops - that will mess up ImageMagick's trim!  To counter this problem, first we add a border of transparent pixels around the image so that when it comes time to run the trim, we can be sure no valuable pixels will get sampled by mistake.  That is where the border command to convert comes in.

The whole script looks like this:
!/bin/bash

# Copyright Sarah Smith
# http://indiegamecodingconfessions.blogspot.com
# ImageMagick is from http://www.imagemagick.org
# Back up your work before running this script!
# This script is placed in the public domain
# Feel free to use it how you want, though a 
# link to this blog would be nice.  :-)

if [ $# != 1 ]; then
    echo "Usage: $0 <dirname>"
    exit 1
fi

# Halt execution the second a command fails
set -e

# Uncomment this line to get debugging info
# set -x

UNIQ_NM=$(date "+%s")
DESTDIR="$1-converted-$UNIQ_NM"
mkdir $DESTDIR

for fn in $1/*; do
    TMPDESTNAME=$DESTDIR/tmp-$(basename $fn)
    DESTNAME=$DESTDIR/$(basename $fn)
    convert $fn -bordercolor none -border 3x3 $TMPDESTNAME
    convert $TMPDESTNAME -trim +repage $DESTNAME
    rm -f $TMPDESTNAME
    echo "   $fn    -- trimmed to -->   $DESTNAME"
done

How to use the script.
  • First save all your work somewhere, make sure its backed up!
  • In a terminal, cd into the directory where your artwork is
    • you backed up this directory didn't you?
  • Download the script trimdir.sh as a file to your computer.  Try this drop box link.
    • save it into that same directory where your art is
  • Make the script executable, and run it like this:
~/Documents/my_art$ trimdir.sh my_directory_of_sprites

...where "my_directory_of_sprites" is the name of a directory containing sprites you want to trim.  The script will create an output directory and put the trimmed versions of all the files in there.

Word of warning - this script is just my own hacking so use at your own risk - and back up before you start!  See my earlier post about backups!  The script tries to avoid doing anything destructive, and never replaces the files you specify, only placing the cropped versions into a new directory.  But like all scripts with great power comes great responsibility!

Also click the topic label "command line" below for more posts about the command line, or search for tutorials on it on the web if you're not comfortable with the command line or Terminal usage.

Here's an example of me running the script on a directory called "static-poses":

~/Documents/AG-Art/erin-animations sez$ ./trimdir.sh static-poses

   static-poses/back-quarter.png    -- trimmed to -->   static-poses-converted-1367477342/back-quarter.png

   static-poses/back.png    -- trimmed to -->   static-poses-converted-1367477342/back.png

   static-poses/front-quarter.png    -- trimmed to -->   static-poses-converted-1367477342/front-quarter.png

   static-poses/front.png    -- trimmed to -->   static-poses-converted-1367477342/front.png

   static-poses/side.png    -- trimmed to -->   static-poses-converted-1367477342/side.png

Of course ImageMagick is capable of so much more than this. Please let me know if you use this technique, or my script and how it worked out for you. Have fun hacking your sprites!

Update:  I realised that this post could confuse people into thinking that your sprite sheets of animations could be processed  this way.  This won't work.  I know I said these images are coming out of my animation program but that is because they are stills that I use for the various static poses, and since I use AnimeStudio for all my character art I have to get the sprites looking consistent, so that means whether animated or still I need to use the same program.

Why not?  When you trim off the transparency close to the colored pixel content that means the position of the character in the frame will change from frame to frame giving a jittery effect.  Definitely not what you want for your animations.  But for static poses this technique works just fine.