使用 FFmpeg 的视频有意义的缩略图

d33*_*ika 101 thumbnails jpeg ffmpeg thumbnail-generator mjpeg

FFmpeg 可以从视频中捕获图像,这些图像可以用作缩略图来表示视频。在FFmpeg Wiki中捕获了最常见的方法。

但是,我不想在某些时间间隔内选择随机帧。我发现了一些在 FFmpeg 上使用过滤器来捕捉场景变化的选项:

过滤器thumbnail试图找到视频中最具代表性的帧:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png
Run Code Online (Sandbox Code Playgroud)

并且以下命令仅选择与之前相比具有超过 40% 变化的帧(因此可能是场景变化)并生成 5 个 PNG 的序列。

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png
Run Code Online (Sandbox Code Playgroud)

以上命令的信息归功于Fabio Sonnati。第二个看起来更好,因为我可以得到 n 个图像并选择最好的。我试过了,它生成了 5 次相同的图像。

更多的调查使我:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png
Run Code Online (Sandbox Code Playgroud)

-vsync vfr确保您获得不同的图像。这仍然总是选择视频的第一帧,在大多数情况下,第一帧是署名/标志并且没有意义,所以我添加了一个-ss3 来丢弃视频的前 3 秒。

我的最终命令如下所示:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg
Run Code Online (Sandbox Code Playgroud)

这是我能做的最好的事情。我注意到,由于我只挑选了5个视频,所有视频都来自视频的开头,可能会错过视频后面出现的重要场景。

我想为任何其他更好的选择挑选你的大脑。

A.M*_*.M. 33

理想情况下,如何在 5 个时间跨度中的每一个中寻找第一个 > 40% 变化的帧,其中时间跨度是视频的第 1、2、3、4 和 5 个 20%。

您也可以将其分成 6 个时间跨度并忽略第一个时间跨度以避免积分。

在实践中,这意味着将 fps 设置为一个较低的数字,同时应用您的场景更改检查和您的参数以丢弃视频的第一位。

...就像是:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
Run Code Online (Sandbox Code Playgroud)

  • 为避免纯黑白等无聊的图像:生成比您需要的更多的缩略图,用 jpeg 压缩它们,并选择文件大小较大的图像。这本身就可以很好地获得不错的缩略图。 (10认同)
  • 至于白框,现在越来越复杂了,不过貌似还是有办法的:1.去掉黑框 2.negate(白变黑) 3.去掉白框 4.再次否定 5.提取缩略图(如果您确实设法去除黑色或白色框架,尤其是在一行上,您可以将其发回这里吗?我可以将其添加到未来读者的答案中。...或者实际上您可以创建一个新的问题和答案它。我肯定会赞成它。) (6认同)
  • 有过滤器可以检测黑框及其序列(http://www.ffmpeg.org/ffmpeg-filters.html#blackframe 或 http://www.ffmpeg.org/ffmpeg-filters.html#blackdetect)。您无法将它们中的任何一个用于您不断增长的单线中,但您绝对应该能够去除黑框(单独的步骤)并从生成的视频中提取缩略图。 (3认同)
  • 此命令不起作用,我收到错误消息“无法为“fps=fps=1/600”找到合适的输出格式。解决方案是在该参数之前添加`-vf`(参见http://stackoverflow.com/questions/28519403/ffmpeg-command-issue) (2认同)
  • 得到这个:“输出文件是空的,没有被编码(检查 -ss / -t / -frames 参数如果使用)”没有产生图像。开头是这样的:“为流 0 指定了多个 -filter、-af 或 -vf 选项,只会使用最后一个 optn '-filter:v fps=fps=1/600'。” (2认同)

ger*_*tas 13

定义有意义是困难的,但如果你想有效地制作 N 个缩略图,跨越整个视频文件,这就是我用来在用户上传的内容的生产中生成缩略图的方法。

伪代码

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`
Run Code Online (Sandbox Code Playgroud)

在哪里:

  • D -ffmpeg -i <movie>单独读取的视频持续时间或ffprobe具有很好的 JSON 输出编写器 btw
  • N - 你想要的缩略图总数
  • X - 缩略图编号,从 1 到 N
  • T - 缩略图的时间点

上面简单地记下了电影每个分区的中心关键帧。例如,如果电影长度为 300 秒并且您想要 3 个缩略图,那么在 50 秒、150 秒和 250 秒后需要一个关键帧。对于 5 个缩略图,它将是 30s、90s、150s、210s、270s。您可以根据电影时长 D 调整 N,例如 5 分钟的电影将有 3 个缩略图,但超过 1 小时将有 20 个缩略图。

表现

ffmpeg对于~1GB H.264,每次调用上述命令都需要几分之一秒(!)。那是因为它立即跳转到<time>位置(记住-ss之前-i)并获取第一个关键帧,这实际上是完整的 JPEG。渲染影片以匹配准确的时间位置不会浪费时间。

后期处理

您可以将上面的scale或 任何其他调整大小方法混合使用。您还可以删除纯色框或尝试将其与其他过滤器(如thumbnail.

  • 哇,将 `-ss N` 移到 `-i` 之前是一个了不起的技巧。谢谢你! (3认同)
  • 在 bash 中并使用 `bc` 进行数学计算,并播放时长为 3 分钟(180 秒)的视频:`D=180; 对于 $(seq 1 $N) 中的 X;回显 $X; T=$(bc &lt;&lt;&lt; "($X-0.5)*$D/$N"); ffmpeg -y -hide_banner -loglevel panic -ss $T -i in.mp4 -vf select="eq(pict_type\,I)" -vframes 1 $X.jpg; 完成` (3认同)
  • 您也可以使用 mediainfo 来获取持续时间,但它以毫秒为单位,因此需要转换为秒: `D=$(bc &lt;&lt;&lt; $(mediainfo --Inform="Video;%Duration%" "in.mp4" )/1000)` (2认同)
  • 您可能需要覆盖“bc”用于区域设置小数点分隔符的内容。您可以使用“LC_ALL=C.UTF-8 bc”来执行此操作,因此脚本将是:“D=180;”对于 $(seq 1 $N) 中的 X;回显 $X; T=$(LC_ALL=C.UTF-8 bc &lt;&lt;&lt; "($X-0.5)*$D/$N"); ffmpeg -y -hide_banner -loglevel panic -ss $T -i in.mp4 -vf select="eq(pict_type\,I)" -vframes 1 $X.jpg; done` 和 `mediainfo` 版本​​为: `D=$(LC_ALL=C.UTF-8 bc &lt;&lt;&lt; $(mediainfo --Inform="Video;%Duration%" "in.mp4")/1000) ` (2认同)

小智 6

尝试这个

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png
Run Code Online (Sandbox Code Playgroud)

使用此命令,我可以生成代表整个视频的所需数量的缩略图。