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


117

嗨,我需要使用ffmpeg从视频中提取帧。有没有比这更快的方法:

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



1
^更新的网址:trac.ffmpeg.org/wiki/Seeking
Lambart

5
如果您有空闲的CPU周期,则可以并行地从多个视频中提取:parallel -i {} -r 1/1 {.}-%03d.bmp ::: *mpg
Ole Tange,

Answers:


155

如果JPEG编码步骤过于注重性能,则始终可以将未压缩的帧存储为BMP图像:

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

这也具有不通过转码为JPEG量化而导致更多质量损失的优点。(PNG也是无损的,但是编码所需的时间往往比JPEG长得多。)


9
这导致很多帧掉落在我的机器上。我可以告诉ffmpeg渲染所有内容吗?
Evi1M4chine '16

45
@ Evi1M4chine只需删除-r参数,这将提取所有帧
studioj

14
我想补充一点,虽然JPEG在CPU上并不是很困难,但未压缩的位图在存储上真的很困难,因此我怀疑与JPEG相比,BMP的吞吐量会更高。
MarcusMüller'17

4
要提取所有帧:ffmpeg -r 1 file.mp4 -r 1 "$filename%03d.png"
fiatjaf

10
你的意思是ffmpeg -r 1 -i file.mp4 -r 1 "$filename%03d.png吧?(您错过了-i
约书亚

68

遇到了这个问题,因此这里有一个快速比较。比较这两种不同的方法,从38m07s长的视频中每分钟提取一帧:

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

1分36.029秒

这需要很长时间,因为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

0分4.89秒

这快了大约20倍。我们使用快速查找转到所需的时间索引并提取帧,然后为每个时间索引多次调用ffmpeg。请注意,这-accurate_seek 是默认设置 ,并确保您-ss在输入视频-i选项之前添加。

请注意,最好使用-filter:v -fps=fps=...而不是,-r因为后者可能不准确。尽管票被标记为固定票,但我仍然遇到了一些问题,因此最好放心使用。


6
2016年2月:从ffmpeg 2.1版本开始,精确搜索选项现已默认设置-trac.ffmpeg.org/wiki/Seeking
mafrosis

1
bc是不是原生Ubuntu的软件包,而不是一个可以使用bash: let "i = $i * 60"。顺便说一句-好主意
吉拉德·马亚尼

3
好的提示-ss之前添加-i。否则,整个视频将被解码,不需要的帧将被丢弃
-MoustafaAAtta

2
由于这是google的头等大事,我想指出的是,在2018年,这仍然是一种可以带来收益的方法。最好的结果似乎是ffmpeg在主机的每个内核上运行一个内核-对于bmp(对于bmp),其速度几乎呈线性提高(直到遇到其他瓶颈,例如磁盘)。
Knetic '18 -4-11

这是应该接受的答案,因为问题是关于“最快的方法”的。显然,您需要使用ffprobe来了解“ for循环”的退出变量,我认为它比其他方法要快。
iamprem

9

如果您确切知道要提取的帧,例如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

我将其与Imagemagick剪辑画面的管道一起使用,以从任何视频中预览10帧。显然,您需要使用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

小解释:

  1. 在ffmpeg表达式中+代表OR和*AND
  2. \,只是逃避,角色
  3. 没有-vsync vfr -q:v 2它似乎不起作用,但我不知道为什么-任何人?

4

我尝试过这个。在32秒内达到3600帧。你的方法真的很慢。您应该尝试一下。

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

我正在尝试 ffmpeg -i "input URL" -vf fps=1/5 out%d.png 输入URL必须是https链接的地方。
x2212

ffmpeg -i file.mpg -vf fps=1 %d.jpg
Kishan Vaghela

1

就我而言,我至少每秒需要一帧。我使用了上面的“寻求”方法,但想知道是否可以并行执行任务。我在这里使用N个进程和FIFO方法: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

本质上,我用&分叉了该过程,但将并发线程数限制为N。

在我的案例中,这将“寻求”方法从26秒提高到16秒。唯一的问题是,由于stdout被淹没,主线程不能干净地退出到终端。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.