使用 Ffmpeg 对视频进行抽帧操作,即把视频中感兴趣的帧输出为图片;该操作也经常被称之为视频快照、视频截图等。

视频抽帧按照输出文件的格式不同,可以输出为 jpgpnggifwebp 等格式文件。

视频抽帧按照选取视频帧的条件不同,可以分为按类型、按时间、按场景变化等不同的套路。

本文从以上两个方面汇总使用 Ffmpeg 抽帧的套路。

🔗参数

🔗输出的帧的数量

可以通过 -frames:v 选项或 -vframes 来控制。

🔗输出的帧的频率

可以通过 -r 选项来控制;也可以通过 fpssetpts 来控制。

🔗选取帧的方式

可以通过万能的 select 来设定,也可以通过设定输出帧率配合 -vsync 来设定。

🔗输出格式

🔗png

视频抽帧输出为 png 格式文件,是最常见的模式。

首先需要设置输出的格式(通过 -f 选项)为 image2

输出文件名需要以 .png 结尾;由于 image2 格式支持输出多张图像文件,所以输出文件名支持 %d 占位符,也支持其他的占位符,具体用法参见 image2 的文档。

典型的用法如下:

ffmpeg -i input.flv -vf fps=1 -frames:v 10 out%d.png

🔗jpg

视频抽帧输出为 jpg/jpeg 格式文件的方式与 png 很类似。

首先需要设置输出的格式(通过 -f 选项)为 image2

输出文件名需要以 .jpg 结尾;文件名也支持占位符。

另外,可以通过额外的参数控制输出图像的质量:-q 取值范围为 [2, 31], 2 的输出质量最好。

典型的用法如下:

ffmpeg -i input.flv -vf fps=1 -frames:v 10 -q:v 2 out%d.jpg

🔗gif

gif 格式支持动画显示,所以可以把多个图像文件合并为一个 gif 文件。

首先需要设置输出的格式(通过 -f 选项)为 gif

输出文件名需要以 .gif 结尾。

另外,可以通过额外的参数控制动画的循环次数:-loop 取值 > 0 表示动画的循环次数;取值 -1 表示不循环;取值 0 表示无限循环。

典型的用法如下:

ffmpeg -i input.flv -vf fps=1 -frames:v 10 -loop 0 -f gif out.gif

上述的方法的最大问题是输出的图像质量较差,优化的方法是(参见 Stackoverflow 答案)添加过滤器 palettegenpaletteuse

ffmpeg -i input.flv -frames:v 10 -loop 0 -f gif \
    -filter "split[x1][x2];[x1]palettegen[p];[x2][p]paletteuse" \
    -r 1 \
    out.gif

🔗webp

webp 是那家 中国大陆地区不存在 公司首倡的图像格式,目标是替代 gifpng 的应用场景。其支持静态图像和动态图像(动画)。gifwebp 的对比可以参见客户端上动态图格式对比和解决方案

首先需要设置输出的格式(通过 -f 选项)为 webp;输出编码器设置为 libwebp

输出文件名需要以 .webp 结尾。

另外,可以通过额外的参数控制动画的循环次数:-loop 取值 > 0 表示动画的循环次数;取值 -1 表示不循环;取值 0 表示无限循环。

ffmpeg -i input.flv -vf fps=1 -frames:v 10 -loop 0 -f webp -c:v libwebp out.webp

如果需要控制输出图像质量,可以参见 libwebp 文档;默认的配置基本能够满足需要。

🔗选帧方式

🔗等间隔选帧

通过 -r 参数实现。

🔗thumbnail 过滤器

thumbnail 过滤器可以在指定大小的一个帧集合里面选出最有代表性的帧。

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter:v thumbnail \
    out.webp

🔗select 过滤器

select 是一个超级强大的过滤器,可以通过表达式实现各种选取帧的套路。

🔗通过帧类型选取

只选取 I 帧:

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter "select='eq(pict_type,I)'" \
    out.webp

选取 IP 帧:

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter "select='eq(pict_type,I)+eq(pict_type,P)'" \
    out.webp

🔗通过时间间隔选取

通过 select 也可以实现按时间间隔选取帧。如每 5 秒一帧:

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter "select='gte(t,selected_n\\*5\\+start_t)'" \
    out.webp

🔗通过帧间隔选取

通过 select 也可以实现按帧的间隔选取帧。如每 5 帧一帧:

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter "select='not(mod(n,100))'" \
    out.webp

🔗按场景变化程度阈值选取

场景变化是指相邻的两帧之间的运动矢量也就是两帧内容的差异。选择场景变化程度超过某阈值的帧在某些应用场景中有独特的优势。

ffmpeg -i input.flv -frames:v 10 -loop 0 -f webp -c:v libwebp \
    -filter "select='gt(scene,0.4)'" \
    out.webp

🔗多种条件选取

按照 Ffmpeg 中的表达式运算规则* 运算符表示逻辑和(AND),+ 运算符表示逻辑或(OR)。如果了解这一点,就可以在 select 过滤器中组合各种条件实现复杂的帧选取逻辑。

select='eq(pict_type,I)+gt(scene,0.4)' 可以选取所有场景变化满足条件的帧和所有 I 帧。

🔗补充说明

🔗字符转义

在组合 select 的参数时,需要注意特殊字符(如 , + *)的转义,具体参见 官方文档

🔗输出动画的速度

在按照帧类型选取时,由于帧的时间戳的不均匀变化,可能会导致输出动画的速度不固定。

如果需要确定的输出动画速度,可以使用 -r 选项配合 setpts 过滤器一起使用。

🔗视频片段选取

如果能在输入参数处设置片段的起始信息,可以加快抽帧的速度,参见官方关于 Seeking 的说明。

🔗输出图像的大小

如果需要指定输出图像的大小,可以用于 -s 参数或直接使用 scale 过滤器。

🔗示例脚本下载

  1. snapshot_by_frame_type 按照帧类型抽帧,可以设置目标类型和输出动画速度
  2. snapshot_by_interval 按照时间间隔抽帧,可以设置时间间隔和输出动画速度
  3. snapshot_by_thumbnail 按照帧类型和场景变化抽帧,可以设置输出动画速度

以上。

更多参见: