大约两年前, 我尝试改进FFmpeg对GIF编码的支持,至少要很体面。尤其是要在GIF编码器中加入透明机制。然而你写的代码并不总是能使其达到最优,这种情况非常常见。但这仍然只是阻止编码器陷入尴尬的尝试。
不过最近在 Stupeflix,我们需要一个方法给 Legend app 生成高质量的 GIF,所以我决定在这上面再花些功夫。
所有在这篇博文 FFmpeg
2.6 中列举的特性都是可用的,并且在Legend app的下一版本中将使用这些特性 (大概在3月26号左右)。
文章太长不要去读:在 部分关注怎么使用即可。
初始提升(2013)让我们看下2013年引入GIF编码器的透明机制的作用:
% ffmpeg -v warning -ss 45 -t 2 -i big_buck_bunny_1080p_h264.mov -vf scale=300:-1 -gifflags -transdiff -y bbb-notrans.gif % ffmpeg -v warning -ss 45 -t 2 -i big_buck_bunny_1080p_h264.mov -vf scale=300:-1 -gifflags +transdiff -y bbb-trans.gif % ls -l bbb-*.gif -rw-r--r-- 1 ux ux 1.1M Mar 15 22:50 bbb-notrans.gif -rw-r--r-- 1 ux ux 369K Mar 15 22:50 bbb-trans.gif这个选项默认生效,当你的图片是高度运动的或者色彩变化强烈,你应该关闭它。
另一个实现的压缩机制是剪切,剪切是仅仅重绘GIF图中一个子矩形但不改变其他地方的基本手段。当然在影片中,这么做用处不大,我们后面再谈。
除了上述机制,之前我没有再取得什么进展。也可能是其他措施不太明显,总之,在图片质量上还存在不少缺陷。
256 色的局限性你可能知道,GIF 是受限于256色调色板。并且默认情况下,FFmpeg 只使用一个通用调色版去尝试覆盖所有的颜色区域,以此来支持含有大量内容的文件:
有序抖动和误差扩散使用抖动来避免陷入这个问题(256色限制),在上面的邦尼大熊兔GIF中,应用了有序Bayer抖动。通过它的8×8网状图案可以很轻易地辨认出来。尽管它不是最好的方式,但是它同样具有很多优点,例如生动,快速,实际上能够防止带条效应和类似的视觉小毛病。
你将会发现大多数的其它抖动方法都是基于误差的,原理是:一个单色误差(从调色板中挑选的颜色与想要的颜色之间的差异)将会传播到整个画面上。引起帧之间的一种”群集效应“,甚至是帧之间完全相同的源的区域,然而这经常会提供一个更好的质量,因为它完全抹去了GIF的压缩:
% ffmpeg -v warning -ss 45 -t 2 -i big_buck_bunny_1080p_h264.mov -vf scale=300:-1:sws_dither=ed -y bbb-error-diffusal.gif % ls -l bbb-error-diffusal.gif -rw-r--r-- 1 ux ux 1.3M Mar 15 23:10 bbb-error-diffusal.gif 更好的调色板提高GIF图片质量的第一步就是定义一个更好的调色板。GIF格式存储了一个全局调色板,但你可以对一张图片(或者是子画面;覆盖在前一帧上的后一帧,但它可以覆盖在一个特定的偏移位置上以获得一个更小的尺寸)重新定义一个调色板。每一帧的调色板都可以取代全局调色板来只对一帧起作用。一旦你停止定义一个调色板,它将会回落到全局调色板。这意味着你不能对一系列的帧定义一个调色板,而这恰恰是你想做的。(典型的做法是在每个场景变化的时候定义一个新的调色板)。
所以,换句话说,你需要遵守这样的模式:一个全局调色板,或者,每帧一个调色板。
每帧一个调色板 (未实现)我最初开始在每帧中计算出一个调色板,但是我发现了这样存在如下缺陷:
这就是我之所以没有使用这种方法而是选择计算一个全局调色板来代替的两个原因。现在我回想起来,它可能与重试这种方法有关,因为在某种程度上,现在的色彩量化比我当初测试的时候的状态要好一些。
对于一系列的帧的每一帧都使用相同的调色板(典型的做法是在场景变化时,就像前面提到的那样)也是可能的。或者,更好的做法是:只在子矩形变化时使用。
所有的这些都当作一个练习留给读者吧。欢迎补充,如果你对这个感兴趣的话可以随时和我联系。
一个全局调色板(已实施)具有一个全局调色板意思是一个2-pass(二次验码)压缩方式(除非你愿意把所有的视频帧都存储在内存里)。