FFmpeg场景检测:为选中帧叠加原始帧编号的实现问题
解决方法:用元数据保存原始帧编号,避免场景检测误判
你的思路很对,确实需要让场景检测和文字叠加的操作完全独立,不过其实不用复杂的流拆分合并,通过**帧元数据(metadata)**就能轻松解决这个问题——核心思路是先把原始帧编号存入帧的元数据(这个操作不会改变画面,完全不影响场景检测),等选出目标帧后,再从元数据中读取原始编号进行叠加。
最终命令
ffmpeg -i video.mp4 -vf " metadata=add:key=original_frame:value=%{frame_num}, select=gt(scene\,0.003), drawtext=fontfile=/Windows/Fonts/Arial.ttf: text='frame\: %{metadata:original_frame}': x=(w-tw)/2: y=h-(2*lh): fontcolor=white: box=1: boxcolor=0x00000000@1: fontsize=30, setpts=N/(30*TB) " frame%d.jpg
命令拆解
metadata=add:key=original_frame:value=%{frame_num}:
把当前帧的原始编号(%{frame_num})存入名为original_frame的元数据字段中,这个操作仅修改帧的元信息,不会改变画面像素,所以场景检测完全基于原始视频内容,不会误判文字变化。select=gt(scene\,0.003):
执行你原本的场景检测逻辑,筛选出与前一帧差异较大的帧,此时选中的帧依然携带我们之前存入的原始帧编号元数据。drawtext滤镜:
不再直接用%{frame_num}(这个会返回筛选后的连续编号),而是用%{metadata:original_frame}读取我们提前存入的原始帧编号,实现正确的编号叠加。setpts=N/(30*TB):
调整输出帧的时间戳,让生成的图片编号保持连续(比如frame1.jpg、frame2.jpg),和你原本的需求一致。
备选方案:用split分离流(如果你偏好这种方式)
如果一定要用流拆分的思路,我们可以把输入流拆成两个分支:一个分支专门做场景检测(不碰画面),另一个分支叠加文字,然后通过overlay同步并只保留场景检测选中的帧:
ffmpeg -i video.mp4 -filter_complex " split [src][scene_detect]; # 分支1:仅做场景检测,标记需要保留的帧 [scene_detect] select=gt(scene\,0.003), setpts=PTS-STARTPTS [selected]; # 分支2:给所有帧叠加原始编号 [src] drawtext=fontfile=/Windows/Fonts/Arial.ttf:text='frame: %{frame_num}':x=(w-tw)/2:y=h-2*lh:fontcolor=white:box=1:boxcolor=black@0.5:fontsize=30 [texted]; # 同步两个分支,只保留selected分支存在的帧 [texted][selected] overlay=shortest=1, select=gt(n\,0) [out]; # 调整输出帧的时间戳 [out] setpts=N/(30*TB) " -map "[out]" frame_%d.jpg
不过这个方案比元数据方法更复杂,且性能稍差,所以更推荐第一种方式。
内容的提问来源于stack exchange,提问作者Fugi
相关产品推荐
相关产品推荐

