Fastest way to extract frames using ffmpeg?
Hi I need to extract frames from videos using ffmpeg.. Is there a faster way to do it than this:
ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg
?
Solution 1:
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.)
Solution 2:
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.
Solution 3:
If you know exactly which frames to extract, eg 1, 200, 400, 600, 800, 1000, try using:
select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)' \
-vsync vfr -q:v 2
I'm using this with a pipe to Imagemagick's montage to get 10 frames preview from any videos. Obviously the frame numbers you'll need to figure out using ffprobe
ffmpeg -i myVideo.mov -vf \
select='eq(n\,1)+eq(n\,200)+eq(n\,400)+eq(n\,600)+eq(n\,800)+eq(n\,1000)',scale=320:-1 \
-vsync vfr -q:v 2 -f image2pipe -vcodec ppm - \
| montage -tile x1 -geometry "1x1+0+0<" -quality 100 -frame 1 - output.png
.
Little explanation:
- In ffmpeg expressions
+
stands for OR and*
for AND -
\,
is simply escaping the,
character - Without
-vsync vfr -q:v 2
it doesn't seem to work but I don't know why - anyone?
Solution 4:
This is simpler than all the other commands so far:
ffmpeg -i input.mp4 '%04d.png'
Change 04
to however many digits you need to hold all frames. Make sure to always have a 0
before the number so output frame names are zero-padded.
Solution 5:
Output one image every minute, named img001.jpg, img002.jpg, img003.jpg, etc. The %03d dictates that the ordinal number of each output image will be formatted using 3 digits.
ffmpeg -i myvideo.avi -vf fps=1/60 img%03d.jpg
Change the fps=1/60
to fps=1/30
to capture a image every 30 seconds. Similarly if you want to capture a image every 5 seconds then change fps=1/60
to fps=1/5
SOURCE: https://trac.ffmpeg.org/wiki/Create a thumbnail image every X seconds of the video