Making Gifs from PNGs with ImageMagick
Posted on 2019, June 27 by Marcos.
Posted on 2019, June 27 by Marcos.
It all started when I need a rotation animation for a simple image. Since I suck at image manipulation with GIMP and this seems to be the kind of problem where’s there’s no need to fiddle with Javascript (or download 5000 node dependencies).
In the end it was just a matter of using imagemagick and writing a shell script. To give you an idea on how this works, take your original image and rotate it for, like, 15 degrees. Here’s how it’s done in imagemagick
:
$ convert img.png -background 'rgba(0,0,0,0)' -rotate 15 img2.png
Since we’re dealing with transparent pngs make sure to use the -background 'rgba(0,0,0,0)'
option. Now how do we repeat this? Well, just use a for loop. Here’s a snipped, we’re iterating with 10 degrees for now (just look at the rot
variable).
for i in {00..11}; do
rot=`expr $i \* 10`
convert img.png -background 'rgba(0,0,0,0)' -rotate $rot img-$i.png
echo img-$i.png
done
This will create 11 files named “img-{%d}.png” in the current folder. The next step consists of combining our rotating images into an animation, a quick read at the docs on animations gave me something like this:
#!/usr/bin/env bash
for i in {00..11}; do
rot=`expr $i \* 5`
convert img.png -background 'rgba(0,0,0,0)' -rotate $rot img-$i.png
echo "img-$i.png, ROT: $rot"
done
convert -delay 5 -size 400x458^ -dispose Background \
-page +0+0 img-00.png \
-page +0+0 img-01.png \
-page +0+0 img-02.png \
-page +0+0 img-03.png \
-page +0+0 img-04.png \
-page +0+0 img-05.png \
-page +0+0 img-06.png \
-page +0+0 img-07.png \
-page +0+0 img-08.png \
-page +0+0 img-09.png \
-page +0+0 img-10.png \
-page +0+0 img-11.png \
-loop 0 animation.gif
Notice the -dispose Background
option, without it the buffer won’t be redrawn once a image is composed. But there’s two problems, if you run this you’ll probably get no animation at all. Firt, add this before the for loop:
convert -border 50x50 -bordercolor 'rgba(0,0,0,0)' "$IMG" "b_${IMG}"
It just so happens that our image is not square, which means rotating around its center loses some parts of it. Now, whenever we had references the original image dimentions must be changed to the padded one, since we did a (50,50) padding this will add 100px on both width and heigth. Now our script looks like this:
#!/usr/bin/env bash
convert -border 50x50 -bordercolor 'rgba(0,0,0,0)' "img.png" "b_img.png"
for i in {00..11}; do
rot=`expr $i \* 5`
convert b_img.png -background 'rgba(0,0,0,0)' -rotate $rot -crop 500x558+0+0 img-$i.png
echo "img-$i.png"
done
convert -delay 5 -size 500x558^ -dispose Background \
-page +0+0 img-00.png \
-page +0+0 img-01.png \
-page +0+0 img-02.png \
-page +0+0 img-03.png \
-page +0+0 img-04.png \
-page +0+0 img-05.png \
-page +0+0 img-06.png \
-page +0+0 img-07.png \
-page +0+0 img-08.png \
-page +0+0 img-09.png \
-page +0+0 img-10.png -loop 0 animation.gif
Now… what if need to this in another image? You’ll have to change your source code again and that’s really boooring. Well, no need to fret, here’s a gif made the old way and the correponding shell script:
Here it is, a gif made in the old ways:
$ ./create-gif.sh -i img.png -f 10 -d 5 -w 5
#!/usr/bin/env bash
usage()
{
echo "Usage: $0 -i PATH_TO_IMG
[ -d DEGREES ]
[ -w ANIMATION_DELAY]
[ -f NUM_FRAMES]"
exit 2
}
build_cmd_list()
{
for i in img-*.png; do
CMD_LST="$CMD_LST -page +0+0 $i"
done
}
IMG=""
FRAMES=11
DEG=10
DELAY=10
CMD_LST=""
CMD=""
while getopts ":d:i:f:w:" opt; do
case $opt in
d)
DEG="$OPTARG"
;;
f)
FRAMES="$OPTARG"
;;
i)
IMG="$OPTARG"
;;
w)
DELAY="$OPTARG"
;;
h|\?)
usage
;;
esac
done
[ -z $IMG ] && usage
# --------------------------------------
# Main Script Starts Here
convert -border 50x50 -bordercolor 'rgba(0,0,0,0)' "$IMG" "b_${IMG}"
echo "Padding original image in b_$IMG"
r_og=$(identify $IMG | cut -d " " -f 3)
rx_og=$(echo $r_og | cut -d "x" -f 1)
ry_og=$(echo $r_og | cut -d "x" -f 2)
res=$(identify "b_${IMG}" | cut -d " " -f 3)
rx=$(echo $res | cut -d "x" -f 1)
ry=$(echo $res | cut -d "x" -f 2)
echo "Image Selected: $IMG - ($rx_og, $ry_og)"
echo "Padded Version: b_$IMG - ($rx, $ry)"
echo "Rotations: $DEG, Frames: $FRAMES"
for i in `seq -w 0 $FRAMES`; do
rot=`expr $i \* $DEG`
convert "b_$IMG" -background 'rgba(0,0,0,0)' -rotate $rot -crop ${rx}x${ry}+0+0 img-$i.png
echo "img-$i.png created"
done
build_cmd_list
CMD="convert -delay $DELAY -size ${rx}x${ry}^ -dispose Background $CMD_LST -loop 0 animation.gif"
echo $CMD
eval $CMD
echo "Removing tmp images"
rm img-*.png "b_${IMG}"