How do I convert a video to GIF using ffmpeg, with reasonable quality?
I'm converting a video to GIF file with ffmpeg:
ffmpeg \ -i input.flv \ -ss 00:00:00.000 \ -pix_fmt rgb24 \ -r 10 \ -s 320x240 \ -t 00:00:10.000 \ output.gifIt works great, but output gif file has a very low quality.
Any ideas how can I improve quality of converted gif?
113 Answers
ffmpeg example
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
-loopoutput option but the values are confusing. A value of0is infinite looping,-1is no looping, and1will 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 (fullwhich 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 trybayerbe sure to test thebayer_scaleoption 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
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.gifffmpeg 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 pamChooses 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 image2pipechooses the image2pipe muxer because when outputting to a pipeffmpegneeds to be told which muxer to use.
convert options:
-delaySee Setting frame rate section below.-loop 0makes infinite loop.-layers optimizeWill 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 optimizeand 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.
42If 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.gifThe -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.gifThe 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.
7As 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 1 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.
2The 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 2 Linux/Unix/macOS
Following @LordNeckbeard approach with ffmpeg command, please find the following useful Bash function which can be added into your ~/.bash_profile file:
# Convert video to gif file.
# Usage: video2gif video_file (scale) (fps)
video2gif() { ffmpeg -y -i "${1}" -vf fps=${3:-10},scale=${2:-320}:-1:flags=lanczos,palettegen "${1}.png" ffmpeg -i "${1}" -i "${1}.png" -filter_complex "fps=${3:-10},scale=${2:-320}:-1:flags=lanczos[x];[x][1:v]paletteuse" "${1}".gif rm "${1}.png"
}Once the function is loaded (manually or from . ~/.bash_profile), you should have new video2gif command.
Example usage:
video2gif input.flvor:
video2gif input.flv 320 10Scale to 320 width with 10 frames per second.
You can also specify a different video format (such as mp4).
macOS
You can try GIF Brewery app which can create GIFs from video files.
Alternatively there are several websites which are doing conversion online free of charge.
2The ffmpeg with palette method can be run in a single command, without intermediary .png file.
ffmpeg -y -ss 30 -t 3 -i input.flv -filter_complex \
"fps=10,scale=320:-1:flags=lanczos[x];[x]split[x1][x2]; \
[x1]palettegen[p];[x2][p]paletteuse" output.gifThis can be done thanks to the split filter.
The selected answer assumes you wish to scale the source video and change its fps in the gif produced. If you do not need to do this, the following works:
src="input.flv"
dest="output.gif"
palette="/tmp/palette.png"
ffmpeg -i $src -vf palettegen -y $palette
ffmpeg -i $src -i $palette -lavfi paletteuse -y $destThis came in handy when I wanted a gif that faithfully recreated the source video I was using.
made a script, tested and works.
usage:
./avi2gif.sh ./vokoscreen-2015-05-28_12-41-56.aviHAVE PHUN :)
vim avi2gif.sh
#!/bin/sh
INPUT=$1
# default settings, modify if you want.
START_AT_SECOND=0; # in seconds, if you want to skip the first 30 seconds put 30 here
LENGTH_OF_GIF_VIDEO=9999999; # in seconds, how long the gif animation should be
echo "Generate a palette:"
ffmpeg -y -ss $START_AT_SECOND -t $LENGTH_OF_GIF_VIDEO -i $INPUT -vf fps=10,scale=320:-1:flags=lanczos,palettegen palette.png
echo "Output the GIF using the palette:"
ffmpeg -ss $START_AT_SECOND -t $LENGTH_OF_GIF_VIDEO -i $INPUT -i palette.png -filter_complex "fps=10,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" $INPUT.gifbtw: vokoscreen is an EXCELLENT ScreenCapturing tool for Linux :)
THANKS A LOT Michael Kohaupt :) Rock steady.
some file size stats:
5.3M = vokoscreen-2015-04-28_15-43-17.avi -> vokoscreen-2015-05-28_12-41-56.avi.gif = 1013K
see the results here.
0For windows users:
create video2gif.bat file in windows directory with these content:
@echo off
set arg1=%1
set arg2=%arg1:~0,-4%
ffmpeg -y -i %arg1% -vf fps=10,scale=-1:-1:flags=lanczos,palettegen %TEMP%\palette.png
ffmpeg -i %arg1% -i %TEMP%\palette.png -filter_complex "fps=10,scale=-1:-1:flags=lanczos[x];[x][1:v]paletteuse" %arg2%.gif
del /f %TEMP%\palette.pngAnd then anywhere you can use it be like this example:
video2gif myvideo.mp4Then you have myvideo.gif in current directory.
If myvideo.gif there is existed, question from you for overwrite it.
EDIT:
I suggest use this batch script:
4How to add a windows 7/10 "right-click" context menu entry to convert your video file to gif
Some of the other answers mentioned the video2gif script, which I used. But, you could use any script.
To create the context-menu option, you need to edit your registry. Open a powershell command prompt, running w/ admin privs. Execute these commands:
$key = "Registry::HKEY_CLASSES_ROOT\`*\shell\Run Video2Gif"
New-Item -Path $key"\Command" -Value "C:\dev\ffmpeg\ffmpeg-3.4.2-win64-static\bin\video2gif.bat `"%1`"" -ForceNow when you right click a file you'll have a "Run Video2Gif" option!
btw I installed ffmpeg to C:\dev\ffmpeg\ffmpeg-3.4.2-win64-static\ and put the video2gif.bat script in the bin dir right next to ffmpeg.exe. I also added C:\dev\ffmpeg\ffmpeg-3.4.2-win64-static\bin to my windows PATH, but I don't think you need to.
If you want the option of being able to supply some extra command line flags / args to the script, then make a new file named video2gif-prompt.bat, and have the registry referce it instead of video2gif.bat:
@echo off
set /p inp=Enter extrta args, if desired:
C:\dev\ffmpeg\ffmpeg-3.4.2-win64-static\bin\video2gif.bat %* %inp%You can still just hit enter to quickly get the defaults.
Below is the batch file for Windows users:
gifenc.bat:
set start_time=0
set duration=60
set palette="c:\temp\palette.png"
set filters="fps=15,scale=-1:-1:flags=lanczos"
ffmpeg -v warning -ss %start_time% -t %duration% -i %1 -vf "%filters%,palettegen" -y %palette%
ffmpeg -v warning -ss %start_time% -t %duration% -i %1 -i %palette% -lavfi "%filters% [x]; [x][1:v] paletteuse" -y %2Source: High quality GIF with FFmpeg: Extracting just a sample
If you just want to use one input variable and have the output name have just the GIF (pronounced JIF) extension, then use this instead:
set start_time=0
set duration=60
set palette="c:\temp\palette.png"
set filters="fps=15,scale=-1:-1:flags=lanczos"
ffmpeg -v warning -ss %start_time% -t %duration% -i %1 -vf "%filters%,palettegen" -y %palette%
set var1=%1
set var2=%var1:~0,-4%
ffmpeg -v warning -ss %start_time% -t %duration% -i %1 -i %palette% -lavfi "%filters% [x]; [x][1:v] paletteuse" -y %var2%.gif 0 ffmpeg commands:
Run this command so that ffmpeg can figure out a good palette:
ffmpeg -y -i foo.mp4 -vf fps=30,scale=320:-1:flags=lanczos,palettegen palette.pngRun this command to convert the mp4 file into gif:
ffmpeg -y -i foo.mp4 -i palette.png -filter_complex "fps=30,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" foo.gif
You might want to tweak the fps and scale. Smaller for either will result in better file size.
Making a simple alias function
You can also create an alias function like this. I added it to my .bashrc or .bash_profile:
function makegif { ffmpeg -y -i $1 -vf fps=30,scale=320:-1:flags=lanczos,palettegen palette.png ffmpeg -y -i $1 -i palette.png -filter_complex "fps=30,scale=320:-1:flags=lanczos[x];[x][1:v]paletteuse" $1.gif
}And then just makegif foo
Note: You'll need ffmpeg of course. Get it here or brew install ffmpeg