Creating video thumbnail contact sheets ...

Creating video thumbnail contact sheets for a batch of video files

Dec 13, 2025

image

If you have a large number of video files in a folder and want to batch process them with ffmpeg to create video contact sheet thumbnails for each video file, here is what you can do on a mac/linux.

Create your Bash script

In the folder create a new file called createthumbs.sh

Open your favorite text/code editor and use the following code

N=20
COLS=5
ROWS=4
FONT="/System/Library/Fonts/Supplemental/Arial.ttf"   # <-- change for your OS

shopt -s nullglob
for f in *.mp4 *.mov *.mkv *.avi *.m4v; do
  dur=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$f")
  fps=$(awk -v n="$N" -v d="$dur" 'BEGIN{ if(d<=0) d=1; printf "%.6f", n/d }')

  ffmpeg -hide_banner -y -i "$f" \
    -vf "fps=$fps,scale=320:-2,\
drawtext=fontfile=$FONT:text='%{pts\\:hms}':x=10:y=h-th-10:fontsize=18:box=1:boxborderw=4,\
tile=${COLS}x${ROWS}:margin=8:padding=8" \
    -frames:v 1 -q:v 2 "${f%.*}_sheet.jpg"
done

Save your changes.

Run your script

Open your terminal and navigate to the folder with the video files and the script you just created.

To run your script, enter the following in the terminal

bash createthumbs.sh

This will create now create 20 thumbnails in a 5×4 grid *_sheet.jpg for each common video extension.

Customisation

  • Replace FONT =/path/to/font.ttf: for a different font

    • macOS example: /System/Library/Fonts/Supplemental/Arial.ttf

    • Linux example: /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf

  • scale=320:-2 sets thumb width; height auto, keeping aspect ratio.

  • Change N for the number of thumbnails

  • Change ROWS, COLS for the number of rows and cols you want;
    make sure it equals N (=ROW*COLS)

  • Change margin, padding for the spacing between the thumbnails

Quick note: The script will overwrite existing *_sheet.jpg files. If you have video files with same name but different extension (eg. video.mp4 and video.m4v), you will get 1 thumbs file.

Timed Variant

If you prefer fixed spacing (e.g. every 30 seconds), skip ffprobe like below (single file example)

ffmpeg -i "input.mp4" -vf "fps=1/30,scale=320:-2,drawtext=fontfile=/path/to/font.ttf:text='%{pts\\:hms}':x=10:y=h-th-10:fontsize=18:box=1:boxborderw=4,tile=5x4:margin=8:padding=8" -frames:v 1 -q:v 2 out.jpg

Windows version

If you are on Windows, you can achieve the same by using a powershell script (which you need to run from Powershell)

$N=20; $Cols=5; $Rows=4
$Font="C\:/Windows/Fonts/arial.ttf"  # ffmpeg prefers forward slashes

Get-ChildItem -File -Include *.mp4,*.mov,*.mkv,*.avi,*.m4v | ForEach-Object {
  $f = $_.FullName
  $dur = [double](& ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$f")
  if ($dur -le 0) { $dur = 1 }
  $fps = $N / $dur

  & ffmpeg -hide_banner -y -i "$f" `
    -vf "fps=$fps,scale=320:-2,drawtext=fontfile=$Font:text='%{pts\:hms}':x=10:y=h-th-10:fontsize=18:box=1:boxborderw=4,tile=${Cols}x${Rows}:margin=8:padding=8" `
    -frames:v 1 -q:v 2 "$($_.DirectoryName)\$($_.BaseName)_sheet.jpg"
}

Final example

6×4 grid (24 thumbs), each thumb 300 px wide, timestamp on each.

Mac/Linux version

#!/usr/bin/env bash
N=24              # total thumbnails (6x4)
COLS=6
ROWS=4
THUMB_W=300
# macOS font example; on Linux you might use: /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf
FONT="/System/Library/Fonts/Supplemental/Arial.ttf"

shopt -s nullglob
for f in *.mp4 *.mov *.mkv *.avi *.m4v; do
  dur=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$f")
  fps=$(awk -v n="$N" -v d="$dur" 'BEGIN{ if(d<=0) d=1; printf "%.6f", n/d }')

  ffmpeg -hide_banner -y -i "$f" \
    -vf "fps=$fps,scale=${THUMB_W}:-2,\
drawtext=fontfile=${FONT}:text='%{pts\\:hms}':x=10:y=h-th-10:fontsize=18:box=1:boxborderw=4,\
tile=${COLS}x${ROWS}:margin=8:padding=8" \
    -frames:v 1 -q:v 2 "${f%.*}_sheet.jpg"
done

If you prefer “every X seconds” instead of 24 evenly spaced frames, replace fps=$fps with fps=1/30 (for every 30 s) and keep tile=6x4.

Linux font tip: set FONT="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf".

Windows version

# 6x4 grid, 300px thumb width with timestamps
$N=24; $Cols=6; $Rows=4; $TW=300
# Use forward slashes in the font path for ffmpeg:
$Font="C\:/Windows/Fonts/arial.ttf"

Get-ChildItem -File -Include *.mp4,*.mov,*.mkv,*.avi,*.m4v | ForEach-Object {
  $f = $_.FullName
  $dur = [double](& ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$f")
  if ($dur -le 0) { $dur = 1 }
  $fps = $N / $dur

  & ffmpeg -hide_banner -y -i "$f" `
    -vf "fps=$fps,scale=${TW}:-2,drawtext=fontfile=$Font:text='%{pts\:hms}':x=10:y=h-th-10:fontsize=18:box=1:boxborderw=4,tile=${Cols}x${Rows}:margin=8:padding=8" `
    -frames:v 1 -q:v 2 "$($_.DirectoryName)\$($_.BaseName)_sheet.jpg"
}

Check out my tool if you just need to create a video contact sheet for 1 file.

¿Te gusta esta publicación?

Comprar GraphicalFinity un café

1 comentar

Más de GraphicalFinity