4

I am using ffmpeg to extract frames from videos. I have the following code thus far:

ffmpeg -i vid.mp4 -vf fps=1/10 images%05d.png

My issue is that it takes awhile! 6 seconds per image on average. Is there a way to speed this up? I am working with tens/hundreds of videos and I'm wanting to write up a shell script to automate the whole thing, but I don't want it to take days!

I have seen other programs that can do this but they have to literally play through the video to reach the frames to extract. That would be far too slow. Perhaps ffmpeg is doing something like this?

Any ideas how to speed this up?

The videos at hand are 1920x1080 GoPro videos and I'd like full resolution images.

Here's the output from the console:

ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
  built with Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
  configuration: --prefix=/opt/local --enable-swscale --enable-avfilter --enable-avresample --enable-libmp3lame --enable-libvorbis --enable-libopus --enable-libtheora --enable-libschroedinger --enable-libopenjpeg --enable-libmodplug --enable-libvpx --enable-libspeex --enable-libass --enable-libbluray --enable-lzma --enable-gnutls --enable-fontconfig --enable-libfreetype --enable-libfribidi --disable-indev=jack --disable-outdev=xv --mandir=/opt/local/share/man --enable-shared --enable-pthreads --cc=/usr/bin/clang --enable-vda --arch=x86_64 --enable-yasm --enable-gpl --enable-postproc --enable-libx264 --enable-libxvid --enable-nonfree --enable-libfdk-aac --enable-libfaac
  libavutil      54. 27.100 / 54. 27.100
  libavcodec     56. 41.100 / 56. 41.100
  libavformat    56. 36.100 / 56. 36.100
  libavdevice    56.  4.100 / 56.  4.100
  libavfilter     5. 16.101 /  5. 16.101
  libavresample   2.  1.  0 /  2.  1.  0
  libswscale      3.  1.101 /  3.  1.101
  libswresample   1.  2.100 /  1.  2.100
  libpostproc    53.  3.100 / 53.  3.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'vid.mp4':
  Metadata:
major_brand     : mp42
minor_version   : 1
compatible_brands: mp41mp42isom
creation_time   : 2015-09-18 03:38:20
  Duration: 00:01:18.23, start: 0.969178, bitrate: 20388 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc, bt709), 1920x1080 [SAR 1:1 DAR 16:9], 20004 kb/s, 29.97 fps, 29.97 tbr, 180k tbn, 59.94 tbc (default)
Metadata:
  creation_time   : 2015-09-18 03:38:20
  handler_name    : Core Media Video
  encoder         : GoPro AVC encoder
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 127 kb/s (default)
Metadata:
  creation_time   : 2015-09-18 03:38:20
  handler_name    : Core Media Audio
[swscaler @ 0x7fdb12000000] deprecated pixel format used, make sure you did set range correctly
Output #0, image2, to 'images%05d.png':
  Metadata:
major_brand     : mp42
minor_version   : 1
compatible_brands: mp41mp42isom
encoder         : Lavf56.36.100
Stream #0:0(und): Video: png, rgb24, 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 0.10 fps, 0.10 tbn, 0.10 tbc (default)
Metadata:
  creation_time   : 2015-09-18 03:38:20
  handler_name    : Core Media Video
  encoder         : Lavc56.41.100 png
Stream mapping:
  Stream #0:0 -> #0:0 (h264 (native) -> png (native))
Press [q] to stop, [?] for help
frame=    9 fps=0.2 q=0.0 Lsize=N/A time=00:01:30.00 bitrate=N/A    
video:17647kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
0

1 Answer 1

3

I have no idea if what you're doing is optimal, but "playing the video" to extract frames is essentially what has to happen, at least a little, if the frame you're extracting is not an I-frame (most frames are not).

The chosen frame is likely represented as the accumulated differences since the last complete frame, which is what interframe compression does. In order to extract it, a program must read and reconstruct the intervening frames -- not from the beginning of the video, just from the most recent complete frame

Depending on how many frames you need from each video, it might make sense to first convert to an "all-I" codec like MJPEG, where every frame stands alone. Then extracting any given frame would be much faster. When done you can delete the converted file.

4
  • 1
    I think the bottleneck here is frame seeking rather than decoding. Try encoding a file in ffmpeg from a mid-point in the source (-ss 2400 or 40 minutes in) and ffmpeg will take some time to get going. Whereas on modern silicon, software players easily decode and process 24-30, if not 60, frames per second to provide realtime playback. That said, converting to I-frame only codec is the right way forward.
    – Gyan
    Sep 18, 2015 at 16:17
  • Converting the whole video to an MJPEG doesn't make sense to me over just converting all frames to JPEG then deleting the ones you don't want. Am I missing something here?
    – user3643
    Feb 29, 2020 at 0:09
  • @user24601 - One difference would be that once you have MJPEG you can still play the video to locate frames. Of course it depends on what the OP's process is -- decoding to a JPEG stack vs an MJPEG video should take about the same amount of time. It's what comes after that, that matters.
    – Jim Mack
    Feb 29, 2020 at 2:32
  • @Jim You can "programmatically play" a jpg stack, assuming they are sorted. If you're targeting specific frames, you'd just call the file name in a jpg stack ... Just thinking out loud. I'm working on a related project involving image extraction from a video.
    – user3643
    Feb 29, 2020 at 17:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.