The ultimate guide to make your own GIF on GNU/Linux

Intro

There are already a lot of GIF available on the internet but often it's hard to get the one we're looking for, also, some are bad quality. So here a complete tutorial to see how we can make our own GIF on GNU/Linux.

Install tools

root@host:~# apt install imagemagick ffmpeg

Make a GIF

Extract a video to png files

user@host:~$ ffmpeg -ss 2:14 -t 6 -i /movies/1988.Rambo.III.MULTI.x264.1080p.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Remove unwanted frames

Remove unwanted frames

Convert png files to one GIF file

user@host:~$ convert -loop 0 *.png myimage.gif

Enjoy the result

Sylvester Stallone in Rambo III movie putting his red ruban

Remove black bars

Some movies have black bars that you may want to remove.

Extract a video to png files

user@host:~$ ffmpeg -ss 8:26 -t 9 -i /movies/2000.American.Psycho.MULTI.AC3.1080p.x264.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Crop files

user@host:~$ identify 00039.png
00039.png PNG 320x180 320x180+0+0 8-bit sRGB 79528B 0.000u 0:00.000
user@host:~$ convert -crop 320x130+0+25 00039.png +repage Cropped_00039.png
user@host:~$ for i in 000*; do convert -crop 320x130+0+25 "$i" +repage Cropped_"$i"; done

Make your GIF

user@host:~$ convert -loop 0 Cropped*.png myimage.gif

Enjoy the result

Christian Bale in American Psycho movie smiling

Add text to your GIF

Extract a video to png files

user@host:~$ ffmpeg -ss 19:24 -t 5 -i /movies/1998.The.Big.Lebowski.MULTI.1080p.Bluray.x264.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Add text to a frame

user@host:~$ convert 00016.png -font Cantarell-Extra-Bold -gravity south -pointsize 30 -stroke black -fill white -strokewidth 1 -annotate +0+0 'YOU'\''RE\nAN ASSHOLE!!!' Text_00016.png
Jeff Bridges in The Big Lebowski movie, you're an asshole!

Add text to multiple frames

user@host:~$ for i in 000*; do cp "$i" Text_"$i"; done
user@host:~$ for i in 0001[6-9]*; do convert "$i" -font Cantarell-Extra-Bold -gravity south -pointsize 30 -stroke black -fill white -strokewidth 1 -annotate +0+0 'YOU'\''RE\nAN ASSHOLE!!!' Text_"$i"; done
user@host:~$ for i in 0002[0-7]*; do convert "$i" -font Cantarell-Extra-Bold -gravity south -pointsize 30 -stroke black -fill white -strokewidth 1 -annotate +0+0 'YOU'\''RE\nAN ASSHOLE!!!' Text_"$i"; done

Make your GIF

user@host:~$ convert -loop 0 Text_*.png myimage.gif

Enjoy the result

Jeff Bridges in The Big Lebowski movie, you're an asshole!

Impact font

If you want to add text with the classical GIF font you need to install impact font from a Windows system to your GNU/Linux system.

Windows Fonts Directory Impact
root@host:~# mkdir /usr/share/fonts/WindowsFonts
root@host:~# mv impact.ttf /usr/share/fonts/WindowsFonts/
user@host:~$ fc-cache --force
user@host:~$ ffmpeg -ss 10:48 -t 2 -i /movies/1997.Batman.and.Robin.MULTi.1080p.AC3.mkv -vf fps=10,scale=320:-1 $filename%05d.png
user@host:~$ for i in 000*; do cp "$i" Text_"$i"; done
user@host:~$ for i in 0000[2-7]*; do convert "$i" -font Impact -gravity south -pointsize "55" -stroke black -fill white -strokewidth 1 -annotate +0+0 'FREEZE...'  Text_"$i"; done
user@host:~$ for i in 0001[0-4]*; do convert "$i" -font Impact -gravity south -pointsize "55" -stroke black -fill white -strokewidth 1 -annotate +0+0 'YOU'\''RE MAD!'  Text_"$i"; done
user@host:~$ convert -loop 0 Text_000* myimage.gif

Enjoy the result

Batman : Freeze, you're mad!

Play with text

Text motion

We can simulate text motion

Extract a video to png files

user@host:~$ ffmpeg -ss 16:43 -t 1 -i /movies/1993.groundhog.day.MULTI.1080p.bluray.x264.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Add text

user@host:~$ a=-250;for i in 000*; do convert "$i" -font Cantarell-Extra-Bold -gravity south -pointsize 35 -stroke black -fill white -strokewidth 1 -annotate -"$a"+0 'I'\''M THINKING...' Texted_${i}; ((a+=60)); echo "$a"; done
a=-250
for i in 000*
do 
	convert "$i" -font Cantarell-Extra-Bold -gravity south -pointsize 35 -stroke black -fill white -strokewidth 1 -annotate -"$a"+0 'I'\''M THINKING...' Texted_${i}
	a=$((a+60))
	echo "$a"
done

Make your GIF

user@host:~$ convert -loop 0 Texted_*.png myimage.gif

Enjoy the result

Bill Murray in Groundhog Day movie, I'm thinking

Text shaking

We can simulate text shaking

Extract a video to png files

user@host:~$ ffmpeg -ss 45:02 -t 2 -i /movies/1995.ace.ventura.when.nature.calls.MULTI.1080p.x264.ac3.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Add text

user@host:~$ for i in 000*; do posX=$RANDOM;let 'posX %= 5'; posY=$RANDOM;let 'posY %= 15'; convert "$i" -font Impact -gravity south -pointsize 40 -stroke black -fill white -strokewidth 1 -annotate +"$posX"+"$posY" 'WARRRMMM!' Texted_${i}; done
for i in 000*
do
	posX=$RANDOM
	let 'posX %= 5'
	posY=$RANDOM;
	let 'posY %= 15'
	convert "$i" -font Impact -gravity south -pointsize 40 -stroke black -fill white -strokewidth 1 -annotate +"$posX"+"$posY" 'WARRRMMM!' Texted_${i}
done

Make your GIF

user@host:~$ convert -loop 0 Texted_*.png myimage.gif

Enjoy the result

Ace Ventura, Jim Carrey, Warm!!

Text zooming

zoom on text

Extract a video to png files

user@host:~$ ffmpeg -ss 4:09 -t 2 -i /movies/1991.Armour.of.God.2.1080p.x264.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Add text

user@host:~$ a=20; for i in 000*; do convert "$i" -font Impact -gravity south -pointsize "$a" -stroke black -fill white -strokewidth 1 -annotate +0+0 'OK!' Texted_${i}; ((a+=3)); done
a=20
for i in 000*
do
	convert "$i" -font Impact -gravity south -pointsize "$a" -stroke black -fill white -strokewidth 1 -annotate +0+0 'OK!' Texted_${i}
	((a+=3))
done

Make your GIF

user@host:~$ convert -loop 0 Texted_*.png myimage.gif

Enjoy the result

Armour of God II, Jackie Chan, OK!

Simulate Zoom

Intro

Imagemagick tools allow a lot of things as to simulate a zoom. Let's try it.

Extract a video to png files

user@host:~$ ffmpeg -ss 1:00:20 -t 2 -i /movies/1994.Street.Fighter.720P.x264.AC3.mkv -vf fps=10,scale=320:-1 $filename%05d.png

Add Text

user@host:~$ for i in 000*; do cp "$i" Text_"$i"; done
user@host:~$ for i in 0000[7-9]*; do convert "$i" -font Impact -gravity south -pointsize "35" -stroke black -fill white -strokewidth 1 -annotate +0+0 'OF COURSE!'  Text_"$i"; done
user@host:~$ for i in 0001[0-4]*; do convert "$i" -font Impact -gravity south -pointsize "35" -stroke black -fill white -strokewidth 1 -annotate +0+0 'OF COURSE!'  Text_"$i"; done

Zoom

user@host:~$ a=110; b=10; for i in $(seq 20 30); do convert Text_00018.png -resize "$a"% Text_000"$i"_.png; convert -gravity center -crop 320x133+$b+0 +repage Text_000"$i"_.png Text_000"$i".png ;((a+=30)); ((b+=25)); done; rm *_.png
a=110
b=10
for i in $(seq 20 30); do 
	convert convert Text_00018.png -resize "$a"% Text_000"$i"_.png
	convert -gravity center -crop 320x133+$b+0 +repage Text_000"$i"_.png Text_000"$i".png
	((a+=30))
	((b+=25))
done
rm *_.png

Make your GIF

user@host:~$ convert -loop 0 Text_*.png myimage.gif

Enjoy the result

Street Fighter the movie Mr Bison Raul Julia Of Course!

Reduce GIF size

Depending on where you want to publish your GIF, size could matter. For example if I want to share GIF via MMS and keep source quality, size should be lower than 600KB.

Let's see differents way to reduce our GIF.

With normal parameters

user@host:~$ ffmpeg -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -vf fps=10,scale=320:-1 $filename%05d.png
user@host:~$ convert -loop 0 00* myimage.gif
user@host:~$ du -sh myimage.gif
2,3M    myimage.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda

Use palettegen

user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -vf fps=10,scale=320:-1:flags=lanczos,palettegen palette.png
user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -i palette.png -filter_complex "fps=10,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" output.gif
user@host:~$ du -sh output.gif
1,7M    output.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda

Compress with fuzz parameter

It will reduce GIF size and output quality. Good compress rate results with limited motions.

user@host:~$ convert myimage.gif -fuzz 2% -layers Optimize result.gif; du -sh result.gif
user@host:~$ du -sh result.gif
1,5M    result.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda
user@host:~$ convert myimage.gif -fuzz 5% -layers Optimize result.gif
user@host:~$ du -sh result.gif
720K    result.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda

Fuzz + palettegen

user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -vf fps=10,scale=320:-1:flags=lanczos,palettegen palette.png
user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -i palette.png -filter_complex "fps=10,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" out%05d.png
user@host:~$ convert -loop 0 out000* myimage.gif; du -sh myimage.gif
user@host:~$ convert myimage.gif -fuzz 2% -layers Optimize result.gif; du -sh result.gif
1,1M    result.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda
user@host:~$ convert myimage.gif -fuzz 5% -layers Optimize result.gif; du -sh result.gif
536K    result.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda

Reducing scale

We could also scale down our GIF

user@host:~$ for i in 00*; do convert "$i" -resize 240x Scalled_$i; done
user@host:~$ convert -loop 0 Scalled_000* myimage.gif; du -sh myimage.gif
1,3M	myimage.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda

Drop frames + Fuzz

user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -vf fps=10,scale=320:-1:flags=lanczos,palettegen palette.png
user@host:~$ ffmpeg -y -ss 17:05 -t 8 -i /movies/1994.Pulp.Fiction.MULTI.mkv -i palette.png -filter_complex "fps=10,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" out%05d.png
user@host:~$ convert -delay 22 -loop 0 $(ls out000* | grep -E "*[02468].png") myimage.gif; du -sh myimage.gif
1,1M	myimage.gif
user@host:~$ convert myimage.gif -fuzz 5% -layers Optimize result.gif; du -sh result.gif
340K	result.gif
Samuel L. Jackson in Pulp Fiction movie, drinking soda
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Contact :