ffmpeg is complaining about there being a missing %d in the filename because you've asked it to convert multiple frames.
This post suggests this would be a better way of using ffmpeg to extract single frames
ffmpeg -i n.wmv -ss 00:00:20 -t 00:00:1 -s 320×240 -r 1 -f singlejpeg myframe.jpg
[edit]
After a bit more research, here is a command line which works outputing single png frames
ffmpeg -i test.avi -vcodec png -ss 10 -vframes 1 -an -f rawvideo test.png
Tested on my ubuntu 12.04 laptop
Answer from Nick Craig-Wood on Stack Overflowffmpeg is complaining about there being a missing %d in the filename because you've asked it to convert multiple frames.
This post suggests this would be a better way of using ffmpeg to extract single frames
ffmpeg -i n.wmv -ss 00:00:20 -t 00:00:1 -s 320×240 -r 1 -f singlejpeg myframe.jpg
[edit]
After a bit more research, here is a command line which works outputing single png frames
ffmpeg -i test.avi -vcodec png -ss 10 -vframes 1 -an -f rawvideo test.png
Tested on my ubuntu 12.04 laptop
Easy way, use Open CV.
import cv2
vc = cv2.VideoCapture('Test.mp4')
c=1
if vc.isOpened():
rval , frame = vc.read()
else:
rval = False
while rval:
rval, frame = vc.read()
cv2.imwrite(str(c) + '.jpg',frame)
c = c + 1
cv2.waitKey(1)
vc.release()
python - extracting frames using FFMPEG? - Stack Overflow
video - Fastest way to extract frames using ffmpeg? - Stack Overflow
images/frame extraction
The fast way to extract all frames from video
This prints the frame number on the left upper corner of each frame output and selects only 1 frame every 30 frames:
ffmpeg -reinit_filter 0 -i "input.mkv" -vf "drawtext=text='%{n}':start_number=1:fontcolor=white:bordercolor=black:borderw=3:fontsize=200,select='not(mod(n,30))',scale=-1:720" -fps_mode vfr -y "IMG_Folder\%6d.jpg"
If you want the counter to start at 0 then 30,60,90 instead of 1,31,61,91 remove the startnumber part.
In Windows you can use something like this to do this to all videos inside a folder and save the pictures in a folder same name as video:
for %a in (*.mp4 *.mkv) do if not exist "%~na" md "%~na"& ffmpeg -reinit_filter 0 -i "%~a" -vf "drawtext=text='%{n}':fontcolor=white:bordercolor=black:borderw=3:fontsize=200,select='not(mod(n,30))',scale=-1:720" -fps_mode vfr -y "%~na\%6d.jpg"
At it's most simple, extract single image from frame of video:
ffmpeg -i video.mp4 -ss HH:MM:SS -vframes 1 image.jpg
#!/bin/bash
file="$1"
name="${file%.*}"
# Extract the first frame
ffmpeg -hide_banner -i "$file" -vf "select=eq(n\,0)" -vsync vfr "$name-first_frame.jpg"
# Set the frame interval
frame_interval=30
# Calculate digits required for frame numbering
total=$(ffmpeg -i "$file" -map 0:v:0 -c copy -f null -y /dev/null 2>&1 | grep -Eo 'frame= *[0-9]+' | grep -Eo '[0-9]+' | tail -1)
digits=${#total}
# Extract every nth frame and rename to include actual frame number
ffmpeg -hide_banner -i "$file" -vf "select='not(mod(n\,$frame_interval))',showinfo" -vsync vfr temp_%0${digits}d.jpg 2>&1 | grep 'pts_time' | awk -v name="$name" -v digits="$digits" -F'|' '{
split($5, a, "=");
split($8, b, "=");
printf "mv temp_%0" digits "d.jpg " name "-frame_%0" digits "d.jpg\n", b[2], b[2]*30
}' | bash
# Remove temporary files
rm temp_*.jpg
exit 0
Save as: frame_extract.sh
Change mode executable: chmod +x frame_extract.sh
Usage: ./frame_extract.sh input_file.mkv
# Define the file path as a parameter
param(
[Parameter(Mandatory=$true)]
[string]$FilePath
)
# Get the file name without extension
$name = [System.IO.Path]::GetFileNameWithoutExtension($FilePath)
# Extract the first frame
& ffmpeg -hide_banner -i $FilePath -vf "select=eq(n\,0)" -vsync vfr "$name-first_frame.jpg"
# Set the frame interval
$frameInterval = 30
# Calculate digits required for frame numbering with zero padding
$totalFramesOutput = & ffmpeg -i $FilePath -hide_banner -map 0:v:0 -c copy -f null - 2>&1
$totalFrames = ($totalFramesOutput | Select-String -Pattern 'frame=\s*[0-9]+' | ForEach-Object { $_.Matches } | ForEach-Object { $_.Value.TrimStart("frame=") } | Select-Object -Last 1)
$digits = $totalFrames.Length
# Extract every nth frame and rename to include actual frame number
$output = & ffmpeg -hide_banner -i $FilePath -vf "select='not(mod(n\,$frameInterval))',showinfo" -vsync vfr "temp_%0${digits}d.jpg" 2>&1
$output | Select-String -Pattern 'pts_time' | ForEach-Object {
if ($_ -match 'pts_time:(\d+)') {
$frameNumber = int
$paddedFrameNumber = $frameNumber.ToString("D$digits")
$tempFileName = "temp_" + $frameNumber.ToString("D$digits") + ".jpg"
$newFileName = "$name-frame_$paddedFrameNumber.jpg"
Rename-Item -Path $tempFileName -NewName $newFileName
}
}
# Remove temporary files
Remove-Item "temp_*.jpg"
I'm not the greatest at PowerShell, but this should be close.
I have no way to test it, but it should help. (Hopefully!)
Added logic to grab the first frame.
Output filename uses frame count, not sequential numbers.
If the JPEG encoding step is too performance intensive, you could always store the frames uncompressed as BMP images:
ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp
This also has the advantage of not incurring more quality loss through quantization by transcoding to JPEG. (PNG is also lossless but tends to take much longer than JPEG to encode.)
Came across this question, so here's a quick comparison. Compare these two different ways to extract one frame per minute from a video 38m07s long:
time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp
1m36.029s
This takes long because ffmpeg parses the entire video file to get the desired frames.
time for i in {0..39} ; do ffmpeg -accurate_seek -ss `echo $i*60.0 | bc` -i input.mp4 -frames:v 1 period_down_$i.bmp ; done
0m4.689s
This is about 20 times faster. We use fast seeking to go to the desired time index and extract a frame, then call ffmpeg several times for every time index. Note that -accurate_seek is the default
, and make sure you add -ss before the input video -i option.
Note that it's better to use -filter:v -fps=fps=... instead of -r as the latter may be inaccurate. Although the ticket is marked as fixed, I still did experience some issues, so better play it safe.