How do I convert a video to GIF using ffmpeg, with reasonable quality?
Solution 1:
ffmpeg
example
183k
ffmpeg
can output high quality GIF. Before you start it is always recommended to use a recent version: download or compile.
ffmpeg -ss 30 -t 3 -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 output.gif
- This example will skip the first 30 seconds (
-ss 30
) of the input and create a 3 second output (-t 3
). - fps filter sets the frame rate. A rate of 10 frames per second is used in the example.
- scale filter will resize the output to 320 pixels wide and automatically determine the height while preserving the aspect ratio. The lanczos scaling algorithm is used in this example.
- palettegen and paletteuse filters will generate and use a custom palette generated from your input. These filters have many options, so refer to the links for a list of all available options and values. Also see the Advanced options section below.
- split filter will allow everything to be done in one command and avoids having to create a temporary PNG file of the palette.
- Control looping with
-loop
output option but the values are confusing. A value of0
is infinite looping,-1
is no looping, and1
will loop once meaning it will play twice. So a value of 10 will cause the GIF to play 11 times.
Advanced options
The palettegen and paletteuse filters have many additional options. The most important are:
stats_mode
(palettegen). You can force the filters to focus the palette on the general picture (full
which is the default), only the moving parts (diff
), or each individual frame (single
). For example, to generate a palette for each individual frame usepalettegen=stats_mode=single
&paletteuse=new=1
.dither
(paletteuse). Choose the dithering algorithm. There are three main types: deterministic (bayer
), error diffusion (all the others including the defaultsierra2_4a
), and none. Your GIF may look better using a particular dithering algorithm, or no dithering at all. If you want to trybayer
be sure to test thebayer_scale
option too.
See High quality GIF with FFmpeg for explanations, example images, and more detailed info for advanced usage.
Also see the palettegen and paletteuse documentation for all available options and values.
ImageMagick convert
example
227k
Another command-line method is to pipe from ffmpeg
to convert
(or magick
) from ImageMagick.
ffmpeg -i input.mp4 -vf "fps=10,scale=320:-1:flags=lanczos" -c:v pam -f image2pipe - | convert -delay 10 - -loop 0 -layers optimize output.gif
ffmpeg
options:
-vf "fps=10,scale=320:-1:flags=lanczos"
a filtergraph using the fps and scale filters. fps sets frame rate to 10, and scale sets the size to 320 pixels wide and height is automatically determined and uses a value that preserves the aspect ratio. The lanczos scaling algorithm is used in this example.-c:v pam
Chooses the pam image encoder. The example outputs the PAM (Portable AnyMap) image format which is a simple, lossless RGB format that supports transparency (alpha) and is supported byconvert
. It is faster to encode than PNG.-f image2pipe
chooses the image2pipe muxer because when outputting to a pipeffmpeg
needs to be told which muxer to use.
convert
options:
-delay
See Setting frame rate section below.-loop 0
makes infinite loop.-layers optimize
Will enable the general purpose GIF optimizer. See ImageMagick Animation Optimization for more details. It is not guaranteed that it will produce a smaller output, so it is worth trying without-layers optimize
and comparing results.
Setting frame rate
Set frame rate with a combination of the fps filter in ffmpeg
and -delay
in convert
. This can get complicated because convert
just gets a raw stream of images so no fps is preserved. Secondly, the -delay
value in convert
is in ticks (there are 100 ticks per second), not in frames per second. For example, with fps=12.5
= 100/12.5 = 8 = -delay 8
.
convert
rounds the -delay
value to a whole number, so 8.4 results in 8 and 8.5 results in 9. This effectively means that only some frame rates are supported when setting a uniform delay over all frames (a specific delay can be set per frame but that is beyond this answer).
-delay
appears to be ignored if used as an output option, so it has to be used before -
as shown in the example.
Lastly, browsers and image viewers may implement a minimum delay, so your -delay
may get ignored anyway.
Video courtesy of U.S. Fish & Wildlife Service National Conservation Training Center.
Solution 2:
If you would prefer to avoid intermediate image files, the commands provided by LordNeckBeard can be piped between ffmpeg
and ImageMagick's convert
so that no intermediate files are required:
ffmpeg -i input.flv -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - output.gif
The -f image2pipe
tells ffmpeg to split the video into images and make it suitable to be piped out, and -vcodec ppm
specifies the output format to be ppm (for some reason if the format is png, either convert
does not read all the images from the pipe, or ffmpeg does not output them all). The -
for both commands specifies that a pipe will be used for output and input respectively.
To optimize the result without saving a file, you can pipe the output from convert
to a second convert
command:
ffmpeg -i input.flv -vf scale=320:-1 -r 10 -f image2pipe -vcodec ppm - | convert -delay 10 -loop 0 - gif:- | convert -layers Optimize - output.gif
The gif:-
tells convert
to pipe its output as gif formatted data and -layers Optimize
tells the second convert
to perform optimize-frame
and optimize-transparancy
methods (see the ImageMagick Introduction to Animation Optimization). Note that the output from the -layers Optimize
may not always provide a smaller file size, so you may want to try converting to a gif without optimization first to be sure.
Remember that during this whole process everything is in memory so you may need sufficient memory if the images are quite large.
Solution 3:
As of ffmpeg 2.6, we can do even better. Based on High quality GIF with FFmpeg:
palette="/tmp/palette.png"
filters="fps=15,scale=320:-1:flags=lanczos"
ffmpeg -i input.flv -vf "$filters,palettegen" -y $palette
ffmpeg -i input.flv -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y output.gif
Solution 4:
I made my own version of this script, which parameterizes the output resolution and frame rate as well.
Running ./gifenc.sh input.mov output.gif 720 10
will output 720p wide 10fps GIF from the movie you gave it. You might need to do chmod +x gifenc.sh
for the file.
#!/bin/sh
palette="/tmp/palette.png"
filters="fps=$4,scale=$3:-1:flags=lanczos"
ffmpeg -v warning -i "$1" -vf "$filters,palettegen" -y "$palette"
ffmpeg -v warning -i "$1" -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y "$2"
You can read the details on my Github
Assumptions: ffmpeg is installed, and the script is in the same folder as the other files.
Solution 5:
The answer from @Stephane is very good. But it will get a warning like Buffer queue overflow, dropping.
for some video, and the generated gif
has some frame dropped.
Here is a better version with fifo
filter to avoid Buffer queue overflow
when using paletteuse
filter. By using split
filter to avoid the creation of intermediate palette PNG file.
ffmpeg -i input.mp4 -filter_complex 'fps=10,scale=320:-1:flags=lanczos,split [o1] [o2];[o1] palettegen [p]; [o2] fifo [o3];[o3] [p] paletteuse' out.gif