嗨,我需要使用 ffmpeg 从视频中提取帧。有没有比这更快的方法:
ffmpeg -i file.mpg -r 1/1 $filename%03d.jpg
?
如果 JPEG 编码步骤对性能要求太高,您始终可以将未压缩的帧存储为 BMP 图像:
ffmpeg -i file.mpg -r 1/1 $filename%03d.bmp
这还有一个优点,即通过转码为 JPEG 进行量化不会造成更多质量损失。 (PNG 也是无损的,但编码时间往往比 JPEG 长得多。)
遇到这个问题,所以这里有一个快速比较。比较这两种不同的方法从 38 分 7 秒长的视频中每分钟提取一帧:
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.689秒
这大约快了 20 倍。我们使用快速查找来找到所需的时间索引并提取一帧,然后为每个时间索引调用几次 ffmpeg。请注意,
-accurate_seek
是默认值
,并确保在输入视频 -ss
选项之前添加 -i
。
请注意,最好使用
-filter:v -fps=fps=...
而不是-r
,因为后者可能不准确。虽然票被标记为已修复,但我仍然遇到了一些问题,所以最好谨慎行事。
这比迄今为止所有其他命令更简单:
ffmpeg -i input.mp4 '%04d.png'
将
04
更改为容纳所有帧所需的数字。确保数字前始终有一个 0
,以便输出帧名称用零填充。
如果您确切知道要提取哪些帧,例如 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
.
小解释:
+
代表 OR,*
代表 AND\,
只是转义 ,
字符-vsync vfr -q:v 2
,它似乎不起作用,但我不知道为什么 - 有人吗?每分钟输出一张图像,命名为img001.jpg、img002.jpg、img003.jpg等。%03d表示每张输出图像的序号将使用3位数字进行格式化。
ffmpeg -i myvideo.avi -vf fps=1/60 img%03d.jpg
将
fps=1/60
更改为 fps=1/30
以每 30 秒捕获一张图像。同样,如果您想每 5 秒捕获一张图像,请将 fps=1/60
更改为 fps=1/5
这对我有用
ffmpeg -i file.mp4 -vf fps=1 %d.jpg
我试过了。 32 秒内 3600 帧。你的方法真的很慢。你应该试试这个。
ffmpeg -i file.mpg -s 240x135 -vf fps=1 %d.jpg
就我而言,我至少每秒都需要帧。我使用了上面的“寻求”方法,但想知道是否可以并行化该任务。我在这里使用了 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 秒。唯一的问题是主线程无法干净地退出回到终端,因为标准输出被淹没。
与@makeworld的答案相同,但也解决了这里提到的关于帧计数不一致和需要垂直同步的问题以及这里关于使用垂直同步的问题:
// 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
在 Win-11 PC 上使用 ffmpeg-6.0,在我尝试之前我无法让它工作 ffmpeg -i input.mp4 帧_%%03d.png
不知道为什么我需要 %% 而不是上面提到的 %。