Skip to content

Instantly share code, notes, and snippets.

@jkalucki
Last active March 10, 2024 22:52
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jkalucki/c81f8fe17599a8c9cd51b565d7dc27eb to your computer and use it in GitHub Desktop.
Save jkalucki/c81f8fe17599a8c9cd51b565d7dc27eb to your computer and use it in GitHub Desktop.
Creating Timelapse Video From Photos With FFMPEG

Creating Timelapse Video From Photos With FFMPEG

Man, there are a lot of outdated and incomplete tutorials about creating timelapse videos from JPG images and photos. Here's a quick start guide to get going in late 2019.

Software

I'm using ffmpeg version 4.2.1 on MacOS. It is free, well supported, and scales up to practically any number of input images or output video length.

There are a dozen paid timelapse software offerings out there, but I suspect they are just polished front-ends to ffmpeg. Small timelapse videos are possible in iMovie 10.1, but adding even a modest number of images bogs it down badly. The easiest approach is to create a rough lightly compressed video with ffmpeg and then edit the result in iMovie.

Resources

I've found the ffmpeg wiki to be the best place to find encoding details, especially the H.264 and H.265 pages. The man page is also a useful reference.

Options - So Many Options

Ffmpeg works on a pipeline model. Select some input to decode, optionally apply some filters, and encode the result.

Input

  • Source File Selection

    Depending on your source image file naming convention and sequencing, you'll need to dig into the image2 demuxer options, mostly around the -pattern_type option. Another good guide to image2 options.

    My AKASO V50Pro names image files with a timestamp, not with a continuous sequence, so I use -pattern_type glob -i YYYMMDD_\*.JPG to select all files created on YYYYMMDD (e.g. 20191126).

    My Nikon and iPhone sequentially name files as IMG_nnnn.JPG. These can be read with -i IMG_%4d.JPG.    

  • Framerate

    -framerate is supposed to change the video speed at the input phase, but I can't get it to work with H.265. It always just plays back at 25fps. ?    

  • Video Size

    It seems that you'd want to scale down images here with the -video_size option, before the filtering stage, but this option seems to have no effect. ? Instead, I just use the -s:v option, detailed below.  

Filters

  • Rotate and Flip

    ffmpeg can perform all sorts of video transformations, see ffmpeg -filters for a summary. If you need to rotate your video 180 degrees, for example if you had to mount your camera upside-down, use -vf "hflip,vflip" (I use -vf "hflip,vflip,format=yuv420p" to set the color encoding at the same time.)  

  • Color Encoding

    I've been using yuv420p as it seems to be both the most compatible and also reasonably compact. With my source material, I don't see any appreciable difference with the other yuv formats. I happen to set the color encoding in the filtergraph -vf "format=yuv420p", but there seems to be several ways to do this.

       

Output

  • Codec

    H.265, aka HEVC is generally well supported and compresses better than H.264. If you run into compatibility problems, such as glitchy video playback, backing off to H.264 is easy enough as the encoder takes all the same options as H.265. Using vlc version 3.0.8, I'm getting glitchy playback with some H.265 video encoding combinations, but Quicktime seems to play H.265 just fine. There's more detail on the wiki pages. Choose the codec with -c:v libx265 or -c:v lib264.

  • H.265 Subcodec: HVC Container

    For some reason you need to specify a hvc1 container or H.265 defaults to hev1 and won't be recognized on Apple devices. Just use -tag:v hvc1 to solve Apple Quicktime compatibility issues.

  • Quality

    The -crf option controls quality for a single pass encoding. 0 is lossless compression, but may produce incompatible video. Around 18 is perceptually lossless and should be fairly compatible with most players. Note that H.265 and H.264 will encode the same quality at different -crf values rates and the -crf values are logarithmic. 50 results in extremely compressed output.     If you need to hit a specific file size or encode for streaming, check the wiki for two-pass encodings.

  • Encoding Speed/Effort

    Faster encoding speed appears to mainly just sacrifice detail, but otherwise gives a reasonable preview of compression artifacts and other choices. Consider using -preset ultrafast while experimenting, then use medium or slow for the final version. The default -preset medium seems to be a good balance between encoding time and video detail. The even slower speeds don't seem to change the detail much with my source material and option set.

  • Tune

    There are tuning options for film quality, preserving grain, aninimation, slide shows, and more, all documented in the H.264 wiki.          

  • Framerate

    You can add duplicate frames to slow down the video speed, at the cost of adding file length, by using -r as an output option. -r 15 will create a video at 15 frames per second. If you are going to edit in iMovie anyway, it might be easier and more flexible to adjust the framerate there.  

  • Size

    The -s:v flag allows you to resize the video. This is a shortcut to the scale output filter. There are abbreviations defined for common video sizes, such as 4k, hd1080, hd720, etc.

  • File Format

    MPEG4 seems to be your best bet for compatibility. Name the output file with a .mp4 extension.  

Putting It All Together

ffmpeg -pattern_type glob -i 20191126_\*.JPG -s hd1080 -c:v libx265 -crf 18 -preset ultrafast -vf "hflip,vflip,format=yuv420p" -tag:v hvc1 265-tagged-hd.mp4

@daz
Copy link

daz commented Jan 14, 2023

If you have a lot of images or you need them in a specific order, you can use concat rather than pattern_type glob.

Create a file-list.txt:

file 'IMG_0004.jpg'
file 'IMG_0005.jpg'
file 'IMG_0008.jpg'
file 'IMG_0001.jpg'

And your ffmpeg command looks like:

ffmpeg -f concat -safe 0 -i file-list.txt \
      -c:v libx265 -tag:v hvc1 \
      -crf 18 \
      -preset ultrafast \
      -pix_fmt yuv420p \
      output.mp4

@jkalucki
Copy link
Author

jkalucki commented Jan 14, 2023 via email

@daz
Copy link

daz commented Jan 14, 2023

For sure. I’m not sure the cut-off number but if you have too many images you’ll get argument list too long: ffmpeg with the glob

@bartzz
Copy link

bartzz commented Mar 6, 2023

Great tutorial. I've created an up to date article for 2023 for the latest FFmpeg. You can also create timelapses from YouTube live.

@mario-hillen
Copy link

I create the filelist with ls and it works for me. Thank you daz

ffmpeg -f concat -safe 0
-i <(for f in $(ls *.jpg | sort -n ); do echo "file '$PWD/$f'"; done)
-c:v libx265 -tag:v hvc1
-crf 18
-preset ultrafast
-pix_fmt yuv420p
output.mp

@daz
Copy link

daz commented May 3, 2023

I didn't realise you could do it inline, nice

@wiedehopf
Copy link

wiedehopf commented Sep 1, 2023

So apparently you can use the -r switch twice, before the input and after the codec to get the framerate to work nicely at 60fps.

I'm using the following script:

#!/bin/bash

# source directory of the jpg files, specified as 1st argument
TARGETDIR=$1
# output file, for example /tmp/lapse1.mkv, specified as 2nd argument
OUTPUT=$2

# Only use very Nth picture
NTH=1

INFPS=60
OUTFPS=60

TMPLIST="$TARGETDIR/.flist.timelapse"


set -e
set -x

ls $TARGETDIR | grep -e '\.jpg$' | sort -V | awk "NR%$NTH==0" | sed -e "s/^/file '/" -e "s/$/'/" | tee "$TMPLIST" | tail
ffmpeg \
    -r $INFPS \
    -f concat -safe 0 -i "$TMPLIST" \
    -pix_fmt yuv420p \
    -c:v libsvtav1 -crf 24 -cpu-used 1 -row-mt 1 -tiles 2x2 \
    -s hd1080 \
    -r $OUTFPS \
    "$OUTPUT"

@raleighlittles
Copy link

FYI at least on Windows, the glob pattern doesn't work, you would have to store them in a text file list.

image

Pattern type 'glob' was selected but globbing is not supported by this libavformat build

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment