简体   繁体   English

使用ffmpeg提取帧的最快方法?

[英]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 从视频中提取帧.. 有没有比这更快的方法:

ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg

? ?

If the JPEG encoding step is too performance intensive, you could always store the frames uncompressed as BMP images:如果 JPEG 编码步骤的性能过于密集,您始终可以将未压缩的帧存储为 BMP 图像:

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.这还具有通过转码为 JPEG 进行量化不会导致更多质量损失的优点。 (PNG is also lossless but tends to take much longer than JPEG to encode.) (PNG 也是无损的,但编码时间往往比 JPEG 长得多。)

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:比较这两种从 38 分 07 秒长的视频中每分钟提取一帧的不同方法:

time ffmpeg -i input.mp4 -filter:v fps=fps=1/60 ffmpeg_%0d.bmp

1m36.029s 1m36.029s

This takes long because ffmpeg parses the entire video file to get the desired frames.这需要很长时间,因为 ffmpeg 解析整个视频文件以获得所需的帧。

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 0m4.689s

This is about 20 times faster.这大约快 20 倍。 We use fast seeking to go to the desired time index and extract a frame, then call ffmpeg several times for every time index.我们使用快速搜索到所需的时间索引并提取一帧,然后为每个时间索引调用多次 ffmpeg。 Note that -accurate_seek is the default , and make sure you add -ss before the input video -i option.请注意-accurate_seek是默认值,并确保在输入视频-i选项之前添加-ss

Note that it's better to use -filter:v -fps=fps=... instead of -r as the latter may be inaccurate.请注意,最好使用-filter:v -fps=fps=...而不是-r因为后者可能不准确。 Although the ticket is marked as fixed , I still did experience some issues, so better play it safe.虽然票被标记为 fixed ,但我仍然遇到了一些问题,所以最好安全一点。

If you know exactly which frames to extract, eg 1, 200, 400, 600, 800, 1000, try using:如果您确切知道要提取哪些帧,例如 1、200、400、600、800、1000,请尝试使用:

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.我将它与 Imagemagick 蒙太奇的管道一起使用,以便从任何视频中获得 10 帧预览。 Obviously the frame numbers you'll need to figure out using ffprobe显然,您需要使用ffprobe找出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在 ffmpeg 表达式中+代表 OR 和*代表 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?没有-vsync vfr -q:v 2它似乎不起作用,但我不知道为什么 - 有人吗?

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.每分钟输出一张图像,命名为 img001.jpg、img002.jpg、img003.jpg 等。 %03d 指示每个输出图像的序号将使用 3 位数字进行格式化。

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.fps=1/60更改为fps=1/30以每 30 秒捕获一次图像。 Similarly if you want to capture a image every 5 seconds then change fps=1/60 to fps=1/5同样,如果您想每 5 秒捕获一次图像,则将fps=1/60更改为fps=1/5

SOURCE: https://trac.ffmpeg.org/wiki/Create a thumbnail image every X seconds of the video来源: https://trac.ffmpeg.org/wiki/每 X 秒创建一个视频缩略图

I tried it.我尝试过这个。 3600 frame in 32 seconds. 32 秒 3600 帧。 your method is really slow.你的方法真的很慢。 You should try this.你应该试试这个。

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

这对我有用

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

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.04更改为保存所有帧所需的位数。

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我在这里使用了带有 FIFO 方法的 N 个进程: 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.本质上,我使用 & 对进程进行了分叉,但将并发线程数限制为 N。

This improved the 'seek to' approach from 26 seconds to 16 seconds in my case.在我的情况下,这将“寻求”方法从 26 秒改进为 16 秒。 The only problem is the main thread does not exit cleanly back to the terminal since stdout gets flooded.唯一的问题是主线程没有干净地退出回终端,因为 stdout 被淹没了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM