Fastest way to extract frames using ffmpeg?

VideoFfmpeg

Video Problem Overview


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

?

Video Solutions


Solution 1 - Video

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 - Video

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 - Video

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 - Video

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 - Video

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

Solution 6 - Video

I tried it. 3600 frame in 32 seconds. your method is really slow. You should try this.

ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg

Solution 7 - Video

This worked for me

ffmpeg -i file.mp4 -vf fps=1 %d.jpg

Solution 8 - Video

In my case I need frames at least every second. I used the 'seek to' approach above but wondered if I could parallelize the task. I used the N processes with FIFO approach here: https://unix.stackexchange.com/questions/103920/parallelize-a-bash-for-loop/216475#216475

open_sem(){
  mkfifo /tmp/pipe-$$
  exec 3<>/tmp/pipe-$$
  rm /tmp/pipe-$$
  local i=$1
  for((;i>0;i--)); do
    printf %s 000 >&3
  done
}
run_with_lock(){
    local x
    read -u 3 -n 3 x && ((0==x)) || exit $x
    (
    "$@" 
    printf '%.3d' $? >&3
    )&
}
N=16
open_sem $N
time for i in {0..39} ; do run_with_lock ffmpeg -ss `echo $i` -i /tmp/input/GOPR1456.MP4  -frames:v 1 /tmp/output/period_down_$i.jpg  & done

Essentially I forked the process with & but limited the number of concurrent threads to N.

This improved the 'seek to' approach from 26 seconds to 16 seconds in my case. The only problem is the main thread does not exit cleanly back to the terminal since stdout gets flooded.

Solution 9 - Video

Same as @makeworld's answer but also addresses issues mentioned here regarding frame count inconsistencies and need for vsync and here regarding use of vsync:

// reliably works for jpg output (and probably png too)
ffmpeg -i rgb.mov -vf setpts=N/FR/TB -vsync 0 ./images/%05d.jpg

// reliably works for png output only
ffmpeg -i rgb.mov -vsync 0 ./images/%05d.png

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionStpnView Question on Stackoverflow
Solution 1 - VideoMultimedia MikeView Answer on Stackoverflow
Solution 2 - VideoblutorangeView Answer on Stackoverflow
Solution 3 - VideomakeworldView Answer on Stackoverflow
Solution 4 - VideoVoyView Answer on Stackoverflow
Solution 5 - Videomouse_freakView Answer on Stackoverflow
Solution 6 - VideoKübraView Answer on Stackoverflow
Solution 7 - VideoKishan VaghelaView Answer on Stackoverflow
Solution 8 - VideoTyconView Answer on Stackoverflow
Solution 9 - Videodanday74View Answer on Stackoverflow