使用FFMPEG:如何进行场景变化检测?带时间码?

37
根据这篇文章,似乎可以使用FFMPEG来检测视频中的场景变化:http://www.luckydinosaur.com/u/ffmpeg-scene-change-detector 现在我有一个显示书本文字的视频,当文字(单词或句子)被朗读时,它将被突出显示。类似于这个音频书籍:https://youtu.be/lA7L6ZNVKjc 我需要知道文字被突出显示的时间戳(因此是场景更改),这将使我能够在我的YouTube视频中添加时间戳标签,从而使听众更容易浏览有声读物。
哪个“神奇”的命令行可以实现这一点?
非常感谢!

1
任何着陆在这个问题/帖子上的人可能会发现这个链接很有用(https://video.stackexchange.com/questions/28613/how-to-extract-each-video-scene-with-ffmpeg) - dtmland
4个回答

50

结合使用场景过滤器(用于检测场景变化)和showinfo过滤器应该能够实现您想要的效果:

ffmpeg -i input.flv  \
       -filter:v "select='gt(scene,0.4)',showinfo" \
       -f null \
       - 2> ffout

此命令提取所有与前一帧相比差异大于 (gt) 0.4(在从 01 的比例尺上)的帧。对于这些帧,将以以下方式打印出信息 (showinfo)。

[Parsed_showinfo_1 @ 0x2d85e60] n:   0 pts:2537204 pts_time:2.5372  pos:  2998114 fmt:rgb24 sar:1/1 s:1920x1200 i:P iskey:1 type:I checksum:5616582E plane_checksum:[5616582E]

现在您只需要提取时间戳。我认为您对 pts_time 感兴趣。您可以这样做:

grep showinfo ffout | grep pts_time:[0-9.]* -o | grep [0-9.]* -o > timestamps

这将为您提供所有时间戳的列表:

2.5372
4.37799
6.65301
8.09344

为了让这种方法有效,您必须拥有一个实现场景检测功能的FFmpeg版本。此外,您还必须为阈值选择一个合适的值(在第一个命令中为0.4)。您可以尝试提取不同阈值的帧(然后手动检查帧),以找到最佳的阈值,如下:

ffmpeg -i input.flv \
       -filter:v "select='gt(scene,0.1)',showinfo" \
       -vsync 0 frames/%05d.jpg

仅供澄清:grep [0-9.]*并不像另一个回答所声称的那样排除整数。它匹配由数字和句点组成的任何字符序列,但也会匹配非数字,例如'4.4.4'。但是,ffmpeg不应输出此类格式不正确的时间戳。


5
@keypulsations,[.] 不匹配任何字符 .,它是一个括号表达式,可以匹配方括号中的任何单个字符。请参见 grep 手册中的“字符类和括号表达式”。虽然 grep [0-9.]* 也可以匹配浮点数和整数以外的内容(但实际上在此管道中没有),但你的正则表达式可能会排除整数,正如其他答案所指出的那样。 - ckoehn
@Laura,自2016年7月以来的所有版本,至少包括最新版本。我不知道这是什么时候添加的。 - ckoehn
@MartinDelille 尝试扩展 * 并抱怨无法这样做。使用引号应该可以解决这个问题。 - ckoehn
@ckoehn 谢谢,它的效果更好了。我唯一遇到的问题是最后一个 grep 将 pts_time:80.24 替换为空行。 - Martin Delille
不要忘记引用正则表达式,否则正则表达式可能返回零个结果。 - Danny
显示剩余4条评论

17

您可以简单地使用该命令:

ffmpeg -i inputvideo.mp4 -filter_complex "select='gt(scene,0.3)',metadata=print:file=time.txt" -vsync vfr img%03d.png

这将只保存与时间.txt文件相关的信息,如下所示。

frame:0    pts:108859  pts_time:1.20954
lavfi.scene_score=0.436456
frame:1    pts:285285  pts_time:3.16983
lavfi.scene_score=0.444537
frame:2    pts:487987  pts_time:5.42208
lavfi.scene_score=0.494256
frame:3    pts:904654  pts_time:10.0517
lavfi.scene_score=0.462327
frame:4    pts:2533781 pts_time:28.1531
lavfi.scene_score=0.460413
frame:5    pts:2668916 pts_time:29.6546
lavfi.scene_score=0.432326

帧是从开头检测到的镜头切换的序号。此外,请根据您的用例适当选择您的阈值(这里为0.3)以获得正确的输出。


此命令不起作用,并创建以下错误:“输出文件为空,未编码任何内容(如果使用了-ss / -t / -frames参数,请检查)” - bashan
尝试更改输入文件的帧率,似乎是输入映射的问题。 - Legolas
2
如果不是这样写:ffmpeg -i inputvideo.mp4,就会出现错误信息:output file #0 does not contain any stream - jdhao

9
我没有足够的声望在上面的答案留言,但我想指出@ckoehn和@keypulsations发布的grep仅会匹配浮点时间戳。如果要匹配整数和浮点时间戳,请使用以下正则表达式。
grep showinfo ffout | grep pts_time:[0-9.]* -o | grep -E '[0-9]+(?:\.[0-9]*)?' -o > timestamps

太好了,需要这一部分。 - SteveEdson

3

我尝试了@ckoehn的答案,一开始它工作正常。但后来停止工作了,最后一个grep中的星号引起了问题。为了避免这种情况,建议在grep语句中使用双引号,例如:

grep showinfo ffout | grep pts_time:[0-9.]* -o | grep "[0-9.]*" -o > timestamps

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接