rss logo

How to Create GIFs from the Command Line on Linux

Intro

There are many GIFs already available on the Internet, but it's often difficult to get the one you're looking for, and some of them are of poor quality. So here's a complete tutorial on how to create your own GIFs under GNU/Linux. Everything will be done on the command line with the imagemagick and ffmpeg tools.

Tool installation

  • Example on Debian:
root@host:~# apt install imagemagick ffmpeg

Make a GIF

Extracting video to png files

  • Frames: 10 per second
  • Scale: 320
  • Start time: 2:14
  • Duration: 6 seconds
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

The previous extraction cannot be perfectly precise. This is not a problem, since we can delete the images we don't want to appear in our destination GIF file.

Remove unwanted frames

Convert png files to one GIF file

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

Enjoy the result

Sylvester Stallone in Rambo III movie putting his red ruban

Removing black bars

Some films feature black bars at the top and bottom of the image. Here's how to remove them.

Extracting 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

  • Get format information:
user@host:~$ identify 00039.png
00039.png PNG 320x180 320x180+0+0 8-bit sRGB 79528B 0.000u 0:00.000
  • Which allows us to deduce:
american psycho frame with Patrick Bateman with the pixel size of the image
  • Crop the frame:
user@host:~$ magick -crop 320x130+0+25 00039.png +repage Cropped_00039.png
  • Which gives:
American Psycho image with Patrick Bateman and picture size in pixels
  • To crop all frames enter the command:
user@host:~$ for i in 000*; do magick -crop 320x130+0+25 "$i" +repage Cropped_"$i"; done

Make your GIF

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

Enjoy the result

Christian Bale in American Psycho movie smiling

Add text to your GIF

Extracting 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

  • -font: use convert -list font to view availaible fonts
  • -pointsize: text size
  • -annotate +0+0: text position
user@host:~$ magick 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
  • Which gives:
Jeff Bridges in The Big Lebowski movie, you're an asshole!

Add text to multiple frames

  • First, copy all frames into new files:
user@host:~$ for i in 000*; do cp "$i" Text_"$i"; done
  • Then add text to all the frames you wish (example here with frames from 00016.png to 00027.png):
user@host:~$ for i in 0001[6-9]*; do magick "$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 magick "$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:~$ magick -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 classic GIF font, you'll need to install the Impact font from a Windows system to your GNU/Linux system.

  • From a Windows Computer get the Impact Font (C:\Windows\Fonts\impact.ttf):
Windows Fonts Directory Impact
  • Create a new directory for Windows fonts:
root@host:~# mkdir /usr/share/fonts/WindowsFonts
  • Copy impact.ttf to the new folder /usr/share/fonts/WindowsFonts:
root@host:~# mv impact.ttf /usr/share/fonts/WindowsFonts/
  • Now, regenerate the fontconfig cache:
user@host:~$ fc-cache --force
  • Let's give it a try:
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
  • Note that we now use -font Impact argument:
user@host:~$ for i in 0000[2-7]*; do magick "$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 magick "$i" -font Impact -gravity south -pointsize "55" -stroke black -fill white -strokewidth 1 -annotate +0+0 'YOU'\''RE MAD!' Text_"$i"; done user@host:~$ magick -loop 0 Text_000* myimage.gif

Enjoy the result

Batman : Freeze, you're mad!

Play with the text

Text movement

We can simulate text movement.

Extracting 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

  • $a: text position
  • a=-250: text start position
  • ((a+=60)): text speed
user@host:~$ a=-250;for i in 000*; do magick "$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
  • The same thing, but easier to read:
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:~$ magick -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.

Extracting 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

  • $posX: text horizontal position
  • $posY: text vertical position
user@host:~$ for i in 000*; do posX=$RANDOM;let 'posX %= 5'; posY=$RANDOM;let 'posY %= 15'; magick "$i" -font Impact -gravity south -pointsize 40 -stroke black -fill white -strokewidth 1 -annotate +"$posX"+"$posY" 'WARRRMMM!' Texted_${i}; done
  • The same thing, but easier to read:
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:~$ magick -loop 0 Texted_*.png myimage.gif

Enjoy the result

Ace Ventura, Jim Carrey, Warm!!

Text zooming

Zoom in on the text.

Extracting 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

  • a=20: text start size
  • ((a+=3)): text growing speed
user@host:~$ a=20; for i in 000*; do magick "$i" -font Impact -gravity south -pointsize "$a" -stroke black -fill white -strokewidth 1 -annotate +0+0 'OK!' Texted_${i}; ((a+=3)); done
  • The same thing, but easier to read:
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:~$ magick -loop 0 Texted_*.png myimage.gif

Enjoy the result

Armour of God II, Jackie Chan, OK!

Simulate a Zoom

Intro

Imagemagick's tool allows you to do many things, such as simulate a zoom. Let's give it a try.

Extracting 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

  • To find out more, click here.
user@host:~$ for i in 000*; do cp "$i" Text_"$i"; done user@host:~$ for i in 0000[7-9]*; do magick "$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 magick "$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 magick Text_00018.png -resize "$a"% Text_000"$i"_.png; magick -gravity center -crop 320x133+$b+0 +repage Text_000"$i"_.png Text_000"$i".png ;((a+=30)); ((b+=25)); done; rm *_.png
  • The same thing, but easier to read:
    • Text_00018.png: frame to be zoomed in
    • a=110: first Zoom value
    • b=10: value of the first shift to the right of the abscissa
    • $(seq 20 30): number of frames to zoom in on (here from Text_00020.png to Text_00030.png)
    • ((a+=30)): zoom increment value for each frame
    • ((b+=25)): right-shift increment value for each zoomed frame
    • rm *_.png: delete temporary files
a=110 b=10 for i in $(seq 20 30); do convert magick 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:~$ magick -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 send a GIF by MMS while maintaining decent quality, the size needs to be less than 600KB.

Let's see differents way to reduce our GIF.

Size without compression

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:~$ magick -loop 0 00* myimage.gif user@host:~$ du -sh myimage.gif 2,3M myimage.gif Samuel L. Jackson in Pulp Fiction movie, drinking soda

Using 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

Compression with fuzz parameter

This option allows you to reduce the size of the GIF while degrading quality. This gives a good level of compression when there is little movement.

user@host:~$ magick 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:~$ magick 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:~$ magick -loop 0 out000* myimage.gif; du -sh myimage.gif user@host:~$ magick 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:~$ magick 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 can also reduce the size of our GIF by lowering the resolution of the frames.

  • Reduce scale to 240:
user@host:~$ for i in 00*; do magick "$i" -resize 240x Scalled_$i; done user@host:~$ magick -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

Even tougher, you can even try droping frames, always with the aim of reducing size as much as possible.

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:~$ magick -delay 22 -loop 0 $(ls out000* | grep -E "*[02468].png") myimage.gif; du -sh myimage.gif 1,1M myimage.gif user@host:~$ magick 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 :

contact mail address