Image Sequences & Time-Lapse: From Photos to Video

Sun Sep 22 2024

Turn image sequences into smooth videos, create time-lapses, and extract frames from video for editing or analysis.

Whether you're building a time-lapse from hundreds of photos or need to extract every frame from a video for frame-by-frame editing, FFmpeg handles image sequences elegantly. This guide covers both directions: photos to video and video to photos.

Image Naming Patterns

FFmpeg expects numbered files in sequence. Common patterns:

  • frame_001.jpg, frame_002.jpg, ... (zero-padded)
  • img%03d.png matches img001.png, img002.png, etc.
  • photo_%04d.jpg matches photo_0001.jpg, photo_0002.jpg, etc.

If your files aren't numbered consecutively, you'll need to rename them first or use a file list.

Photos to Video

# Basic image sequence to video (30 fps) ffmpeg -framerate 30 -i frame_%04d.jpg -c:v libx264 -crf 20 -pix_fmt yuv420p output.mp4 # Time-lapse from photos (play faster) # If you shot 1 photo per second over 1 hour, play at 24 fps for a 2.5 minute video ffmpeg -framerate 24 -i img_%05d.jpg -c:v libx264 -preset medium -crf 18 -pix_fmt yuv420p timelapse.mp4 # High-quality for archival (ProRes or lossless) ffmpeg -framerate 30 -i frame_%04d.png -c:v prores_ks -profile:v 3 -pix_fmt yuva444p10le output.mov

The -framerate parameter sets the playback speed. Lower values slow down playback; higher values speed it up.

Handle Non-Sequential Files

# Create a file list when images aren't numbered sequentially # list.txt: # file '/path/to/photo1.jpg' # file '/path/to/photo2.jpg' # file '/path/to/another.jpg' # Then convert ffmpeg -f concat -safe 0 -i list.txt -vf "fps=25" -c:v libx264 -crf 20 -pix_fmt yuv420p output.mp4 # Or use bash to generate the list printf "file '%s'\n" *.jpg > list.txt ffmpeg -f concat -safe 0 -i list.txt -vf "fps=24" -c:v libx264 -crf 20 -pix_fmt yuv420p out.mp4

Scale and Crop Images

If your photos have varying sizes or orientations:

# Scale all images to 1920x1080, maintaining aspect ratio with padding ffmpeg -framerate 24 -pattern_type glob -i '*.jpg' \ -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \ -c:v libx264 -crf 20 -pix_fmt yuv420p padded.mp4 # Center crop to 16:9 ffmpeg -framerate 30 -i photo_%04d.jpg \ -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080" \ -c:v libx264 -crf 20 -pix_fmt yuv420p cropped.mp4

Video to Image Frames

# Extract all frames as PNG ffmpeg -i input.mp4 frame_%04d.png # Extract one frame per second ffmpeg -i input.mp4 -vf "fps=1" frame_%04d.jpg # Extract frames from 10s to 20s only ffmpeg -ss 00:00:10 -to 00:00:20 -i input.mp4 frame_%04d.png # High-quality extraction (minimal compression) ffmpeg -i input.mp4 -q:v 1 frame_%05d.jpg

The -q:v option controls JPEG quality (1 is best, 31 is worst). For lossless, use PNG.

Add Motion and Transitions

# Ken Burns effect (slow zoom and pan) ffmpeg -framerate 24 -i img_%04d.jpg \ -vf "scale=1920:1080,zoompan=z='min(zoom+0.0015,1.5)':d=125:x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':s=1920x1080" \ -c:v libx264 -crf 20 -pix_fmt yuv420p zoom.mp4 # Crossfade between images (each shown for 3 seconds with 0.5s fade) ffmpeg -framerate 1/3 -i img_%03d.jpg -vf "framerate=fps=30:interp_start=0:interp_end=255:scene=100" \ -c:v libx264 -crf 20 -pix_fmt yuv420p crossfade.mp4

Stabilization and Color Correction

# Apply color correction to all frames ffmpeg -framerate 24 -i frame_%04d.jpg \ -vf "eq=brightness=0.06:saturation=1.2" \ -c:v libx264 -crf 20 -pix_fmt yuv420p color_corrected.mp4 # Denoise time-lapse (reduce flickering) ffmpeg -framerate 24 -i img_%04d.jpg \ -vf "hqdn3d=1.5:1.5:6:6" \ -c:v libx264 -crf 18 -pix_fmt yuv420p denoised.mp4

Pitfalls

  • Missing frames in a sequence will cause FFmpeg to stop or error; check numbering carefully.
  • Always use -pix_fmt yuv420p for web compatibility (many players don't support other formats).
  • Large image sizes can slow processing; scale down if resolution isn't critical.
  • Photos from phones may have EXIF orientation flags; use -vf "transpose=1" if needed to rotate.
  • When combining portraits and landscapes, decide on padding vs cropping early to avoid inconsistent framing.

Real-World Example: Sunset Time-Lapse

Let's say you have 500 photos taken every 10 seconds over an hour, named DSC_0001.jpg to DSC_0500.jpg:

# Rename for FFmpeg pattern (if needed) cd photos/ for f in DSC_*.jpg; do num=$(echo "$f" | sed 's/DSC_0*\([0-9]*\)\.jpg/\1/') mv "$f" "frame_$(printf %04d $num).jpg" done # Create 24 fps time-lapse (500 frames at 24fps = ~21 seconds) cd .. ffmpeg -framerate 24 -i photos/frame_%04d.jpg \ -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2" \ -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p sunset.mp4

This workflow lets you archive, edit, and share time-lapses or any frame-based project with full control over the output quality and format.