How to create an animated GIF from MP4 video via command line?
I want make an animated .gif from an .mp4 video. I would prefer to do it from the command line, so please only list command line tools.
You can use this script on Github that uses FFMPEG and generates intermediate color palette for better picture quality.
5 Answers 5
ffmpeg 3.4.4 can do it directly on Ubuntu 18.04
You likely want to use something like:
sudo apt install ffmpeg wget -O opengl-rotating-triangle.mp4 https://github.com/cirosantilli/media/blob/master/opengl-rotating-triangle.mp4?raw=true ffmpeg \ -i opengl-rotating-triangle.mp4 \ -r 15 \ -vf scale=512:-1 \ -ss 00:00:03 -to 00:00:06 \ opengl-rotating-triangle.gif
Image info: 426kB, 45 frames, 512×512 apparent size, coalesced, conversion time on a Lenovo P51: 0.5s.
The above conversion also worked after a ulimit -Sv 1000000 (DRAM usage limited to 1GB), so it does «not consume huge amounts of memory» like previous attempts I did with Imagemagick which almost killed my machine. 500MB however failed because ffmpeg failed to load its shared libraries. time to upgrade your RAM ;-)?
Test data generation procedure described on this post.
The output has a visible dotting pattern, which is not as visible in «ffmpeg + convert» method below. We can try to improve the image quality with methods described at:
ffmpeg \ -i opengl-rotating-triangle.mp4 \ -r 15 \ -vf "scale=512:-1,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \ -ss 00:00:03 -to 00:00:06 \ opengl-rotating-triangle-palettegen.gif
Image info: 979K, 45 frames, 512×512 apparent size, coalesced, conversion time on a Lenovo P51: 3.5s.
- the dotting pattern is much less visible now
- GIF size roughly doubled. TODO: why does simply choosing a palette increase the image size? Is it because now more colors so we need more bits per color? How to observe each palette?
- generation time was 7x slower, presumably because ffmpeg is first scanning through the entire video to determine an optimal palette
We could also play with documented palettegen parameters like palettegen=max_colors=16 to achieve different size quality trade-off points.
Argument breakdown
- -ss 00:00:03 -to 00:00:06 : start and end time to cut the video from. No, GIFs are not the best way to
piratedistribute videos online. See also: https://stackoverflow.com/questions/18444194/cutting-the-videos-based-on-start-and-end-time-using-ffmpeg - -vf scale=512:-1 : make the output 512 pixels in height, and adjust width to maintain the aspect ratio. This is a common use case for images for the web, which tend to have much smaller resolution than video. If you remove this option, the output GIF has the same height as the input video. The original video height can be found for example with ffprobe : https://superuser.com/questions/595177/how-to-retrieve-video-file-information-from-command-line-under-linux/1035178#1035178 and is 1024 x 1024 in our case.
- -r 15 : sampling FPS. For example, the original video was 30 FPS, so -r 15 means that ffmpeg will pick one frame in every 2 ( = 30 / 15 ). The perceived output FPS is adjusted to match the input however, so you won’t notice a speedup, only greater granularity. The input FPS can be found with ffprobe , and the total number of input frames can be found with mediainfo as explained at: https://superuser.com/questions/84631/how-do-i-get-the-number-of-frames-in-a-video-on-the-linux-command-line/1044894#1044894 I recommend this option because video formats usually have a higher framerate due to the larger resolution. With smaller GIFs, the lower framerate is less noticeable, and so we can skip some frames and make smaller GIFs.
Video camera footage example
If you want to see the result quality of a video camera video from Wikimedia Commons with a similar command:
wget https://upload.wikimedia.org/wikipedia/commons/f/f9/STS-132_Liftoff_Space_Shuttle_Atlantis.ogv ffmpeg -i STS-132_Liftoff_Space_Shuttle_Atlantis.ogv -r 15 -vf scale=512:-1 \ -ss 00:00:17 -to 00:00:22 STS-132_Liftoff_Space_Shuttle_Atlantis.gif
Image info: 1.3MB, 75 frames, 512×288 apparent size, coalesced (has minimal effect however, because footage pans slightly from the start), conversion time on a Lenovo P51: 2.3s.
Here is a version with palettegen but only 2 seconds to fit the 2MiB upload limit:
Image info: 1.5MB, 30 frames, 512×288 apparent size, conversion time on a Lenovo P51: 43s.
sudo apt-get install ffmpeg ffmpeg -i in.mp4 out.gif
also works, but the output GIF would be way larger than the input video, because video formats can compress more efficiently across frames with advanced algorithms, while GIF can only does a simple rectangular frame diff.
Before 18.04: ffmpeg + convert one-liner without intermediate files
ffmpeg could not handle GIF previously. The best I had was something along:
sudo apt-get install ffmpeg imagemagick ffmpeg -i opengl-rotating-triangle.mp4 -r 15 -vf scale=512:-1 \ -ss 00:00:03 -to 00:00:06 -f image2pipe -vcodec ppm - | convert -deconstruct -delay 5 -loop 0 - opengl-rotating-triangle-image-magick.gif
Image info: 995kB, 45 frames, 512×512 apparent size, coalesced.
For the Atlantis shuttle footage, and analogous:
ffmpeg -i STS-132_Liftoff_Space_Shuttle_Atlantis.ogv -r 15 -vf scale=512:-1 \ -ss 00:00:17 -to 00:00:22 -f image2pipe -vcodec ppm - | convert -deconstruct -delay 5 -loop 0 - STS-132_Liftoff_Space_Shuttle_Atlantis_512x.gif
produced better looking output, but the final GIF was considerably larger at 6.2MB, so I can’t upload it.
Explanation of some of the arguments:
- -loop 0 : add the Netscape Gif extension Loop count field to the output. 0 means infinite loop as described at: http://www.vurdalakov.net/misc/gif/netscape-looping-application-extension eog , firefox and chromium all loop infinitely by default even without it, so I’m not sure how necessary it is anymore.
- -delay 5 : time waited before showing the next frame, in hundreths of second, as described at: https://en.wikipedia.org/wiki/GIF#Animated_GIF byte 324. So 100 means 1 FPS, 5 means 1 / 0.5 == 20FPS .
- -deconstruct : compress across frames with rectangular diffs, see also: how can I resize an animated GIF file using ImageMagick?
Even if you reduce the height and framerate, the output GIF may still be larger than the video, since «real» non-GIF video formats compress across frames, while GIF only compresses individual frames.
convert input.mp4 rpi2-bare-metal-blink.gif
worked, but almost killed my computer because of memory overflow, and produced an ouptput 100x larger for my 2s 1Mb input file. Maybe one day ImageMagick will catch up.
This is another option that was brought to my attention and which claims intelligent algorithms, so let’s try it out.
First we need to convert the video to a sequence of images, and then feed that into gifsky, e.g.:
sudo snap install gifski mkdir -p frames ffmpeg \ -i opengl-rotating-triangle.mp4 \ -r 15 \ -vf scale=512:-1 \ -ss 00:00:03 -to 00:00:06 \ frames/%04d.png gifski -o opengl-rotating-triangle-gifski.gif frames/*.png
Image info: 954K, 45 frames, 512×512 apparent size, not coalesced, conversion time on a Lenovo P51: 4.8s.
Image info: 1.6M, 30 frames, 512×288 apparent size, not coalesced, conversion time on a Lenovo P51: 2.8s.
So for me, subjectively, this did not appear to offer significant benefit over ffmpeg’s palettegen .
convert animated gif to video on linux server while preserving frame rate
how do I convert an animated gif to a video (e.g. h264@mp4) programmatically on a linux server? I need this to process user generated content which should be output as several defined video formats; therefore its possible, that users may want to process animated gif files. I already have a set of working php scripts to transcode videofiles to specific formats (like vpx@webm and h264@mp4, scaled to specific resolutions) using avconv, but herefore I need video input. Usual ways seem to be to extract the frames of the gif and then encode it, like
convert file.gif file%03d.png avconv -i file%03d.png file.mp4
- this does not respect the pause between frames, as they can differ (like 1st frame 100ms pause, 2nd frame 250ms pause, 3rd frame 100ms pause, . )
- as the input comes from users, it may even vary, as some gifs may have 5fps and others 30fps
I noticed that avconv is able to process gifs by itself and therefore may respect the correct pauses, but when I do (like similarily described in How to convert GIF to Mp4 is it possible?)
avconv -i file.gif -r 30 file.mp4
avconv will only take the first frame of the gif, while it detects the file at least as video:
Duration: 00:00:00.04, start: 0.000000, bitrate: N/A Stream #0.0: Video: gif, pal8, 640x480, 25 tbn
(example gif ‘file.gif’ has 15 frames, each with 100ms pause => 1.5s duration, looping)
- What am I missing? Whats going wrong?
- Are there probably better tools for this use case?
- What are big sites like e.g. 9gag using to transcode uploaded gifs to video?
@LordNeckbeard just created this with gimp: s.xi-intersection.de/test2.gif — takes heavy use of this feature.Pauses are per frame in ms: 100, 150, 200, 250, 300, 400, 50, 50, 500, 200, 100, 100, 50, 75, 200, 200, 200, 200, 50, 50, 50, 40
avconv sucks. Get a recent static build of ffmpeg (the one from FFmpeg, not the counterfeit from Libav), then try: ffmpeg -i input.gif -c:v libx264 -pix_fmt yuv420p -movflags +faststart output.mp4
@LordNeckbeard Actually, this really solves the problem; seems to be a bug or like so in avconv. Would you please so kind and make an answer out of your comment?
2 Answers 2
Yet Another Avconv Bug (YAAB)
ffmpeg has better GIF demuxing support (and improved GIF encoding). I recommend ditching avconv and getting ffmpeg (the real one from FFmpeg; not the old charlatan from Libav). A static build is easy, or you can of course compile.
Example
ffmpeg -i in.gif -c:v libx264 -pix_fmt yuv420p -movflags +faststart out.mp4
If your animated GIF has an odd (not even) height or width, you need to add -vf scale=x:y to the line with x or y incremented by one if needed to make them even. The following worked fine for me with version 2.6.5: ffmpeg -i in.gif -vf scale=x:y -pix_fmt yuv420p out.mp4
If for some reason you are required to use avconv and imagemagick, you may want to try something like this:
ticks_per_frame = subprocess.check_output('identify -verbose -format %T_ '.format(gif_path).split()).split('_')[:-1] ticks_per_frame = [int(i) for i in ticks_per_frame] num_frames = len(ticks_per_frame) min_ticks = min(ticks_per_frame) subprocess.call('convert -coalesce tmp%d.png'.format(gif_path).split()) if len(set(ticks_per_frame)) > 1: num_dup = 0 num_dup_total = 0 for frame, ticks in enumerate(ticks_per_frame): num_dup_total += num_dup frame += num_dup_total num_dup = 0 if ticks > min_ticks: num_dup = (ticks / min_ticks) - 1 for i in range(num_frames + num_dup_total - 1, frame, -1): orig = 'tmp%d.png' % i new = 'tmp%d.png' % (i + num_dup) subprocess.call(['mv', orig, new]) for i in range(1, num_dup + 1): curr = 'tmp%d.png' % frame dup = 'tmp%d.png' % (i + frame) subprocess.call(['cp', curr, dup]) framerate = (100 / min_ticks) if min_ticks else 10 subprocess.call('avconv -r -i tmp%d.png -c:v libx264 -crf -pix_fmt yuv420p \ -vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -y .mp4'.format(framerate, quality, STORAGE_DIR + mp4_name).split()) subprocess.call(['rm'] + glob('tmp*.png'))
So, get the ticks in centiseconds for each frame of the gif (via identify), convert to multiple pngs, and then go through them while making duplicates based on the tick values. And don’t you worry, the png files will still remain in consecutive order. Using the real FFmpeg is still the best way to go.
ingramchen / gist:e2af352bf8b40bb88890fba4f47eccd0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
### Full command line options |
«` |
ffmpeg -f gif -i FOO.gif -pix_fmt yuv420p -c:v libx264 -movflags +faststart -filter:v crop=’floor(in_w/2)*2:floor(in_h/2)*2′ BAR.mp4 |
«` |
### Notie |
* output mp4 is encoded with h264, support Firefox/Chrome/Safari in Windows, Mac OSX, Android, and iOS. |
* one mp4 file for all platforms, there is no need to encode an extra `webm` movie, which encoding speed is pretty slow. |
* format as `yuv420p` for Firefox compatibility, the downside is color becomes less-saturate than original gif. |
* yuv420p only support even width/height, so crop filter is required |
* `-movflags +faststart` flags are optimized for online view in browser |
* compression ratio typically 10:1, pretty awesome. note that if original gif is < 512KB, convert as mp4 is less efficient. |
### Obtain static binary ffmpeg |
* http://johnvansickle.com/ffmpeg/ |