如何使用ffmpeg从video中删除重复的帧

首先,我会在前面说我是没有专家的video处理,虽然我一直摆弄ffmpeg多年(在相当有限的方式)。 因此,我并不是经常使用所有语言的人,所以我的操作对我所要做的事情会产生怎样的影响……但是,无论如何,

我在这里检查了几个链接,例如: ffmpeg – 顺序删除重复的帧

…但内容并没有真正帮助我。

我有几百个使用ffmpeg和其他类似应用程序在Windows和Linux下创build的video剪辑。 然而,他们在video中显示“不动”的时候遇到了一些问题。

举个例子,比方说,我们有一些网站可以将一个实时video插入网页浏览器的Flashvideo播放器/插件中。 在这种情况下,我们正在讨论一个交通摄像机videostream。

有一个ffmpeg运行的实例,将(Windows)桌面的一个区域捕获到一个video文件中,即:

ffmpeg -hide_banner -y -f dshow ^ -i video="screen-capture-recorder" ^ -vf "setpts=1.00*PTS,crop=448:336:620:360" ^ -an -r 25 -vcodec libx264 -crf 0 -qp 0 ^ -preset ultrafast SAMPLE.flv 

比方说,被捕获的实际“显示”看起来像这样:

 123456789 XXXXX 1234567 XXXXXXXXXXX 123456789 XXXXXXX ^---a---^ ^-P-^ ^--b--^ ^----Q----^ ^---c---^ ^--R--^ 

…其中每个字符位置表示(帧)的(序列)。 由于互联网连接较差,可以在一段较长的时间内显示“单帧”(“X”字符是前一帧的(几乎)精确副本)。 因此,这意味着我们有捕获的video片段,图片根本不会改变(无论如何,肉眼)。

我们如何处理重复的框架?…如果'重复'与ffmpeg不一样,但是对于观察者看起来差不多一样,我们的方法会如何改变?

如果我们简单地删除重复的帧,video的“步调”就会丢失,过去需要5秒钟的时间才能显示,现在只需要几分之一秒的时间,这样会产生一个非常干燥,不自然的动作,虽然有video中没有重复的图像。 这似乎是可行的使用ffmpeg与'mp_decimate'选项,即: –

  ffmpeg -i SAMPLE.flv ^ ... (i) -r 25 ^ -vf mpdecimate,setpts=N/FRAME_RATE/TB DEC_SAMPLE.mp4 

我引用的那个引用使用了一个命令,显示当它认为它们是“相同的”时,“mp_decimate”将删除哪些帧,即:

  ffmpeg -i SAMPLE.flv ^ ... (ii) -vf mpdecimate ^ -loglevel debug -f null - 

…但是知道(复杂的格式化)的信息,我们如何重新组织video,而不是执行多次ffmpeg的运行来提取video片段以便以后重新合并?

在这种情况下,我猜我们必须运行如下的东西: –

  • 用户指定重复的“阈值持续时间”(可能只运行1秒)
  • 确定并保存主video信息(fps等 – 假设帧率恒定)
  • 映射(复制开始的帧/时间) – >否。 帧/重复的持续时间
  • 如果重复的持续时间小于用户阈值,则不要将此时间段视为“一系列重复帧”并继续前进
  • 提取“非重复”video片段(上图中的a,b和c)
  • 用原始video的规格创build“新video”(空)
  • 对于每个video片段提取该片段的最后一帧,创build一个带有刚刚提取的帧(持续时间=用户规格= 1秒)的重复帧的短video片段,将(当前video片段+短片段)附加到“新video”

…但在我的情况下,很多捕获的video可能是30分钟长,并有数百个10秒长的停顿,因此重buildvideo将花费很长时间使用这种方法。

这就是为什么我希望有一些“可靠”和“更智能”的方式来使用ffmepg(有/没有'mp_decimate'filter)做'十进制'function只有几个通行证左右…也许有一种方法,甚至可以指定所需的段(例如,在一个文本文件中)和ffmpeg运行它会停止/重新启动它在指定的时间/帧数转码?

简而言之,是否有另一个应用程序(用于Windows或Linux),可以做我正在寻找的,而不必手动设置开始/停止点,手动提取/合并video片段…?

我一直试图用Win7-SP1下的ffmpeg N-79824-gcaee88d和Puppy Linux Slacko 5.6.4下的(不同的版本,我现在还没有记得)来做这一切。

多谢一堆线索。

我假设你想要做的是保持帧运动和1秒的重复帧,但放弃其余的。

 ffmpeg -i in.mp4 -vf "select='if(gt(scene,0.01),st(1,t),lte(t-ld(1),1))',setpts=N/FRAME_RATE/TB" trimmed.mp4 

选择过滤器表达式所做的是使用if-then-else运算符:

gt(scene,0.01)检查当前帧是否检测到相对于前一帧的运动。 与传感器/压缩噪声或帧中的视觉噪声相比,通过查看哪个值准确地捕获实际活动,必须基于人工观察来校准该值。 请参阅此处了解如何获取所有场景更改值的列表。

如果帧被评估为有运动,则then子句评估st(1,t) 。 函数st(val,expr)st(val,expr)的值存储在一个变量val ,并返回该表达式值作为结果。 因此,保持的帧的时间戳将在该变量中保持更新,直到遇到静态帧。

else子句检查当前帧时间戳和存储值的时间戳之间的差异。 如果差值小于1秒,帧将被保留,否则被丢弃。

setpts清理所有选定帧的时间戳。

编辑 :我测试了我的命令与我合成的视频输入,它的工作。

我在这个问题上做了一些工作,并发现了以下的作品…

看来输入视频必须有一个“恒定的帧速率”才能正常工作,所以第一个命令是:

 ffmpeg -i test.mp4 ^ -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" ^ -vsync cfr test01.mp4 

然后我需要看看每一帧的“分数”。 这样的名单是由:

 ffmpeg -i test01.mp4 ^ -vf select="'gte(scene,0)',metadata=print" -f null - 

我会看看所有的分数…和平均他们(意思) – 有点狡猾,但它似乎工作好吧。 在这个例子中,平均得分是“0.021187”。

然后我必须选择一个“持久性”值 – 让“重复”帧运行多久。 如果你强迫它只保留一帧,整个视频往往运行得太快…所以,我已经用了0.2秒作为一个起点。

所以下一个命令变成:

 ffmpeg -i test01.mp4 ^ -vf "select='if(gt(scene,0.021187),st(1,t),lte(t-ld(1),0.20))', setpts=N/FRAME_RATE/TB" output.mp4 

之后,由此产生的'output.mp4'视频似乎工作得很好。 这只是一点点摆弄“持久性”的价值,可能需要做的妥协之间有一个更平滑的播放视频和场景突然变化一点。

我已经把一些Perl代码放在一起工作好了,我会研究如何发布,如果人们对它感兴趣…最终(!)

编辑: 这个“抽取”的另一个好处是,文件的持续时间较短(显然),他们的规模较小。 例如,运行00:07:14并且大小为22MB的示例视频转到00:05:35和11MB。

可变的帧速率编码是完全可能的,但我不认为它做你认为它的作用。 我假设你想删除这些重复的帧来节省空间/带宽? 如果是这样,它将无法工作,因为编解码器已经在做它。 编解码器使用参考帧,只编码从参考改变了什么。 因此,重复的框架几乎没有空间开始。 基本上帧被编码为一个数据包说,复制前一帧,并做出这一改变。 X帧有零变化,所以只需要几个字节来编码每一个。