Fastest way to extract frames using ffmpeg?

335,461

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:

  1. In ffmpeg expressions + stands for OR and * for AND
  2. \, is simply escaping the , character
  3. 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

Share:
335,461

Related videos on Youtube

Stpn
Author by

Stpn

Updated on April 08, 2022

Comments

  • Stpn
    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
      Lambart over 6 years
    • Ole Tange
      Ole Tange over 6 years
      If you have CPU cycles to spare, you can extract from multiple videos in parallel: parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
  • mafrosis
    mafrosis over 8 years
    Feb 2016: as of ffmpeg 2.1, the accurate seek option is now default - trac.ffmpeg.org/wiki/Seeking
  • Guig
    Guig about 8 years
    this seems to output one more frame that there is in the video (number I can get with the showinfo filter or ffprobe)
  • Evi1M4chine
    Evi1M4chine almost 8 years
    This results in a lot of frame dropping on my machine. Can I tell ffmpeg to render everything?
  • studioj
    studioj over 7 years
    @Evi1M4chine just remove the -r parameter this will extract all frames
  • Marcus Müller
    Marcus Müller about 7 years
    I'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
    gilad905 over 6 years
    bc is not a native Ubuntu package, instead one can use bash: let "i = $i * 60". BTW - excellent idea
  • fiatjaf
    fiatjaf over 6 years
    To extract all frames: ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
  • MoustafaAAtta
    MoustafaAAtta over 6 years
    Good tip adding -ss before -i. Otherwise, the whole video will be decoded and the unrequired frames will be discarded
  • Knetic
    Knetic about 6 years
    Since 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
    Domi.Zhang over 5 years
    Could 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
    Joschua over 4 years
    You mean ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png, right? (you were missing the -i)
  • feed_me_pi
    feed_me_pi about 4 years
    I am trying ffmpeg -i "input URL" -vf fps=1/5 out%d.png where the input URL has to be an https link.
  • FireBrand
    FireBrand over 3 years
    Is it possible to output a frame, or every frame into a local variable (In Node or Python for example)?
  • Zimba
    Zimba over 3 years
    eq(n\,1) would extract 2nd frame (order starts at 0)
  • Antonio Gomez Alvarado
    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 docs vfr means Frames 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 option cfr as stated here ffmpeg.org/ffmpeg.html
  • llogan
    llogan over 3 years
    No need for -pix_fmt rgba. The PNG encoder will automatically choose the appropriate pixel format.
  • makeworld
    makeworld over 3 years
    @llogan thanks, removed it with an edit. Got a source for that though?
  • llogan
    llogan over 3 years
    You can view a list of supported pixel formats for the PNG encoder with ffmpeg -h encoder=png. See avcodec_find_best_pix_fmt_of_list documentation.
  • kungphil
    kungphil almost 3 years
    Needed to add an additional d as ffmpeg -i input.mp4 '%04d.png'
  • Franva
    Franva almost 3 years
    hi 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
    Franva almost 3 years
    hi @giladmayani new to Linux, could you please put the full command here? thanks
  • gilad905
    gilad905 almost 3 years
    @Franva should be smt like this (UNTESTED): -ss `let "j = $i * 60" && echo $j`
  • Tatui1969
    Tatui1969 almost 3 years
    I'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
    PRMan over 2 years
    @Domi.Zhang Because -ss skips to that point without decoding any frames. The other option decodes every frame.
  • PRMan
    PRMan over 2 years
    Sure, scaling them to a tiny size makes it faster, but that's not what he asked for.