I create a lot of animated gifs and I’m getting into creating videos as well. All of these are initially created by rendering a series of png files into a folder called
frames. Over the last few years I’ve figured out some pretty good recipes for converting these frames into animated gifs and videos. So I’m sharing.
Both ImageMagick and ffmpeg are extremely complex programs with tons of parameters that allow you to do all kinds of image, video and audio manipulation. I’m not any kind of an expert on any of these. And this post is not meant as thorough documentation on these programs. It’s more of a cookbook of commands that handle specific use cases well. I’ll also try to explain what each of these parameters does, which might help you to come up with your own commands.
Nothing beats thoroughly reading the official docs, but if you just want to get something done quickly, these recipes should help you.
It confused me at first, but ImageMagick is not a single executable. It’s more of a suite of graphics utilities. Maybe under the hood it’s all one executable, but it presents as different commands you can call. For generating an animated gif, you’ll be using the
As I said, I keep my frames in a folder called
frames. They are named sequentially like so:
I use four digits, which gives me up to 10,000 frames. At 30 fps, that gives me about 333 seconds worth of video, or about five and a half minutes. Good enough for my needs now. If you go with three digits, you’ll only get 33 seconds worth. If I ever need to do more than five minutes in a single clip, five digits will give me almost an hour.
convert uses simple wildcard matching, so my input is simply
frames/*.png and the output can be something like
Next you need to figure out the frame rate. I usually go with 30 fps. You don’t enter frame rate directly with
convert though. You need to specify a delay between each frame. And here’s where things get really weird. The unit you use for the delay parameter is hundredths of a second. So if you want 30fps, you’d specify 100 / 30 or 3.333… Not exactly intuitive, but you can get used to anything. Put it all together and you get:
convert -delay 3.333 frames/*.png out.png
But there’s one more parameter I put in there, which is
convert has a whole ton of stuff you can do with it to affect how the gif is put together.
-layers Optimize combines a few of the most useful ones into a single command. I found that before I started using this, I’d get an occasional animation that just totally glitched out for a frame or two. Actually, it was more than occasional. Not every time, but often enough to be a problem. Using this parameter completely solved it, so I highly recommend it. If you want to read more up on it: https://legacy.imagemagick.org/Usage/anim_opt/#optimize
So the final command I use is:
convert -delay 3.333 -layers Optimize frames/*.png out.png
This has been my bread and butter command for a long time now.
But lately, I’ve been running into problems using ImageMagick to create longer, larger format gifs. This hasn’t been an issue at all when I was just posting to Twitter, which has something like a 15mb size limit. But as I’ve been posting gifs in other places that can be up to 100mb, I’ve started making larger and longer animations. And ImageMagick has been crashing pretty regularly while generating these. I’ve watched a graph of memory use and I’m pretty sure that it’s just running out of memory. It uses up all my physical memory, then uses up all my swap, then crashes. On at least one occasion I got it to work by closing down every other program that was running on the computer. That freed up just enough memory to get through. But it doesn’t always work. So that’s… less than ideal.
ffmpeg for gifs
[Update] Before using the command below, read this post, which has to do with color palettes: More ffmpeg Tips
I’ve been using ffmpeg for a while now to create videos, and I knew it could create gifs as well, so I’ve recently given it a try and it seems to do just as good a job as ImageMagick. And it can handle those high res, long gifs as well. So I’m pretty happy so far. Creating gifs with ffmpeg is also pretty straightforward, except for the input filename specification, which I’ll cover next. Here’s the command I use:
ffmpeg -framerate 30 -i frames/frame_%04d.png out.gif
Breaking that down, the
framerate param is just the number of frames per second. Simple.
-i is for input, and you point it to your sequential frames. and finally, the output filename.
The only complexity is the
frame_%04d.png part. This is a printf type of string formatting convention. The
%04d part means that you have four digits (4d), and they will be padded with 0s. So frame 37 will be
As I said, this has been working really well for me so far, but I haven’t been using it very long, so if there are any issues, I might come upon them later.
Video creation is what ffmpeg is usually used for. It’s an amazingly powerful and complicated tool. Even just figuring out all of the possible parameters can be daunting. So I’ll go over some of the common use cases I have and what each parameter does.
Mostly I’ve been optimizing the videos I create for use on YouTube. This uses h264 encoding and a specific pixel format. Here’s the basic command I use for creating a video from a sequence of frames:
ffmpeg -framerate 30 -i frames/frame_%04d.png -s 1920x1080 -c:v libx264 \
-preset veryslow -crf 20 -pix_fmt yuv420p out.mp4
(That should be all one line. I wrapped it to make it more readable.)
Yeah, a lot to take in there. So let’s go through it param-by-param.
-framerate 30 is just the fps setting.
-i frames/frame_%04d.png specifies the input files as described earlier.
-s 1920x1080 sets the size of the video in the format of
height. I believe that if you leave this param off, it will create the video based on the size of the source images. But you can use this to scale up or down as well.
-c:v libx264 OK, this will take a little background. ffmpeg can be used for creating or processing both audio and video – or both at the same time. Some of the parameters can be applied to audio or video. In this case,
-c is letting you specify what codec to use for encoding. But is it a video codec or an audio codec? By adding
:v we’re specifying that it’s a video codec. Fairly obvious here because there is no audio involved in this case, but explicit is always good. I’ve seen examples where people use
-s:v to specify the size of the video, which seems overkill to me. But ffmpeg can also handle things like subtitles, and maybe those can be sized? Again, explicit is good. Anyway, here we are saying to use the libx264 codec for encoding the video. Which is good for Youtube.
-preset veryslow Here we have a tradeoff – speed vs size. These are the presets you can use:
- medium – default preset
The faster you go, the larger your file size will be. It’s recommended that you use the slowest preset you have patience for.
-crf 20 is the constant rate factor. There are two ways of encoding h264 – constant rate factor (CRF) and average bit rate (ABR). Apparently CRF gives better quality generally. This parameter can go from 0 to 51, where 0 is lossless and 51 is crap. 23 is default and anything from around 17 to 28 is probably going to be generally acceptable. The CRF setting also affects file size. Lower CRF means a higher file size. So if you need the lowest possible file size, use a high CRF and a slow preset. It will take a long time and look like crap, but it will be small! More info on all this here: http://trac.ffmpeg.org/wiki/Encode/H.264
-pix_fmt yuv420p is the pixel format. You might be more used to RGB888 where you have red, green and blue channels, and 8 bits per each of those channels. YUV is just an alternate way of formatting colors and pixels. Mind-numbingly detailed description over at the old wikipedia: https://en.wikipedia.org/wiki/YUV . But this is a good format for Youtube.
out.mp4 is the file that is created after applying everything else.
So there you go. my bread and butter video creation command.
But there are a few more tidbits I use:
I usually try to make my animated gifs loop smoothly. But for longer form videos this is not always what you are going for. Still, you usually don’t want the video to just get to the end and stop. A fade out to black is a nice ending effect. You could build that into your source animation code, or you could do it in post of course. But ffmpeg has fades built in. Here’s the altered command:
ffmpeg -framerate 30 -i frames/frame_%04d.png -s 1920x1080 -c:v libx264 \
-preset veryslow -crf 24 -pix_fmt yuv420p -vf "fade=t=out:st=55:d=5" out.mp4
Here, I’ve added the param
vf is for video filter, I believe. This takes a string in a very compact format. It starts by specifying the type of filter and its definition:
"fade=...". After the equals sign is a list of parameters in the format:
a=x:b=y:c=z. We’ll step through those:
t=out means the type of fade out.
st=55 tells the fade to start at 55 seconds into the video.
d=5 means the fade will last 5 seconds.
In most cases the
d parameters will add up to the total video length, but you can do all kinds of things here. Like fading out mid video and then fading back in later.
t=in would execute a fade in. You might want to do that at the start of your video.
Although I have not had the need for it, I assume you can use
-af to create an audio fade.
One of the big benefits of video over animated gifs is the possibility of adding music or sound effects to the animation. As mentioned, ffmpeg can do that too.
I believe it’s possible to add the soundtrack to the video as you are creating it from the source frames, but this does not seem like a good workflow to me. I might generate dozens of videos before coming up with one I decide to go with. So it makes more sense to me to add the audio when I’m done. Here’s my command for adding audio to an existing video:
ffmpeg -i video.mp4 -i audio.wav -map 0:v -map 1:a -c:v copy output.mp4
Again, let’s go through each param.
-i video.mp4 is the video you’ve already created that you want to add audio to.
-i audio.wav is the sound file you want to add to the video. Yes, we have two inputs here. That’s how it works.
-map 0:v The map parameter tells ffmpeg which input is what. Here we’re saying the first input (0) is video (v).
-map 1:a As you can probably guess, this says that input 1 is audio.
-c:v copy As covered before, this is the video codec. In this case though, we don’t want to encode the video all over again. We just want to copy the video over and add the audio to it.
output.mp4 is the final combined audio/video file.
So that’s pretty straightforward and works great. But this kind of assumes that your video and audio are the same size. They might be, which is nice, but they might not be, which raises the question of how long to make the final video? Do you make it the same length of the video or the length of the audio?
I believe by default, it will choose the longest option. So if your video is one minute long and you throw a five minute song on top of it, you’ll wind up with a five minute video. Honestly, I’ve never tried this so I don’t know what happens. I assume you get four minutes of blackness. Or maybe four minutes of the last frame of the source video. Or maybe it loops?
On the other hand, if your video is longer than your audio, you will probably wind up with some amount of silence after the audio finishes.
So the other option here is the
ffmpeg -i video.mp4 -i audio.wav -map 0:v -map 1:a -c:v copy -shortest output.mp4
All this does is make the output video the same length as the shortest input.
I’m sure you can figure out all the different use cases here. And you probably want to think about fading your audio out along with a video fade so the sound/music doesn’t just suddenly stop.