Fastest way to extract frames using ffmpeg?
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
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 4
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 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
Related videos on Youtube
![Stpn](https://i.stack.imgur.com/NgqeK.jpg?s=256&g=1)
Stpn
Updated on April 08, 2022Comments
-
Stpn about 2 years
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
?
-
Lambart over 6 years^ updated url: trac.ffmpeg.org/wiki/Seeking
-
Ole Tange over 6 yearsIf you have CPU cycles to spare, you can extract from multiple videos in parallel:
parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
-
-
mafrosis over 8 yearsFeb 2016: as of ffmpeg 2.1, the accurate seek option is now default - trac.ffmpeg.org/wiki/Seeking
-
Guig about 8 yearsthis seems to output one more frame that there is in the video (number I can get with the showinfo filter or ffprobe)
-
Evi1M4chine almost 8 yearsThis results in a lot of frame dropping on my machine. Can I tell ffmpeg to render everything?
-
studioj over 7 years@Evi1M4chine just remove the -r parameter this will extract all frames
-
Marcus Müller about 7 yearsI'd like to add that while JPEG isn't really hard on the CPU, uncompressed Bitmaps is really really hard on the storage, so I doubt you'll get higher throughput with BMP compared to JPEG.
-
gilad905 over 6 years
bc
is not a native Ubuntu package, instead one can use bash:let "i = $i * 60"
. BTW - excellent idea -
fiatjaf over 6 yearsTo extract all frames:
ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
-
MoustafaAAtta over 6 yearsGood tip adding
-ss
before-i
. Otherwise, the whole video will be decoded and the unrequired frames will be discarded -
Knetic about 6 yearsSince this is top of google, I'd like to note that in 2018 this is still an approach that yields dividends. The best result seems to be running one
ffmpeg
per core of your host - which (for bmp) yields near-linear improvements in speed (until you hit some other bottleneck, like disk). -
Domi.Zhang over 5 yearsCould you explain why the second method be faster than the first method much more? They may also do the same work: locate the frame. The first method locate a frame at 10-frame intervals, and the second method locate a specified frame in many times.
-
Joschua over 4 yearsYou mean
ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png
, right? (you were missing the-i
) -
feed_me_pi about 4 yearsI am trying
ffmpeg -i "input URL" -vf fps=1/5 out%d.png
where the input URL has to be an https link. -
FireBrand over 3 yearsIs it possible to output a frame, or every frame into a local variable (In Node or Python for example)?
-
Zimba over 3 years
eq(n\,1)
would extract 2nd frame (order starts at 0) -
Antonio Gomez Alvarado over 3 years
-q:v
is an alias for-qscale:v
(trac.ffmpeg.org/wiki/Encode/MPEG-4) and controls image quality.-vsync vfr
is the video sync method (you first need to understand-vf
which is a filtergraph) . According to the docsvfr
meansFrames are passed through with their timestamp or dropped so as to prevent 2 frames from having the same timestamp.
I think this is to avoid the default optioncfr
as stated here ffmpeg.org/ffmpeg.html -
llogan over 3 yearsNo need for
-pix_fmt rgba
. The PNG encoder will automatically choose the appropriate pixel format. -
makeworld over 3 years@llogan thanks, removed it with an edit. Got a source for that though?
-
llogan over 3 yearsYou can view a list of supported pixel formats for the PNG encoder with
ffmpeg -h encoder=png
. Seeavcodec_find_best_pix_fmt_of_list
documentation. -
kungphil almost 3 yearsNeeded to add an additional
d
asffmpeg -i input.mp4 '%04d.png'
-
Franva almost 3 yearshi I'm trying to get the frame every 10 seconds, I changed the 60.0 to 10.0 but it still only retrieve 46 images in a 45min video. Could someone help please?
-
Franva almost 3 yearshi @giladmayani new to Linux, could you please put the full command here? thanks
-
gilad905 almost 3 years@Franva should be smt like this (UNTESTED):
-ss `let "j = $i * 60" && echo $j`
-
Tatui1969 almost 3 yearsI'm try to use
time for i in {3600..7680} ; do ffmpeg -accurate_seek -ss `echo $i*60.0 | bc` -i input.mp4 -frames:v 1 $filename%03d.png ; done
and ffmpeg complains about:Output file is empty, nothing was encoded (check -ss / -t / -frames parameters if used)
-
PRMan over 2 years@Domi.Zhang Because -ss skips to that point without decoding any frames. The other option decodes every frame.
-
PRMan over 2 yearsSure, scaling them to a tiny size makes it faster, but that's not what he asked for.