大约两年前, 我尝试改进FFmpeg对GIF编码的支持,至少要很体面。尤其是要在GIF编码器中加入透明机制。然而你写的代码并不总是能使其达到最优,这种情况非常常见。但这仍然只是阻止编码器陷入尴尬的尝试。
不过最近在 Stupeflix,我们需要一个方法给 Legend app 生成高质量的 GIF,所以我决定在这上面再花些功夫。
所有在这篇博文 FFmpeg 2.6 中列举的特性都是可用的,并且在Legend app的下一版本中将使用这些特性 (大概在3月26号左右)。
文章太长不要去读:在Usage 部分关注怎么使用即可。
让我们看下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中,应用了有序Bayer抖动。通过它的8x8网状图案可以很轻易地辨认出来。尽管它不是最好的方式,但是它同样具有很多优点,例如生动,快速,实际上能够防止带条效应和类似的视觉小毛病。
你将会发现大多数的其它抖动方法都是基于误差的,原理是:一个单色误差(从调色板中挑选的颜色与想要的颜色之间的差异)将会传播到整个画面上。引起帧之间的一种”群集效应“,甚至是帧之间完全相同的源的区域,然而这经常会提供一个更好的质量,因为它完全抹去了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
我最初开始在每帧中计算出一个调色板,但是我发现了这样存在如下缺陷:
开销: 一个256色调色板大小为768B,并且不包括在LZW 算法机制中,所以它没有被压缩。同时因其必须储存在每帧中,这就意味着对于25FPS的序列来说会有每秒150千比特的开销( 768 *8 *25b =150 *1024b )。 虽然大多数时候这可以忽略。
在我最初的测试当中由于调色板的变化产生了亮度闪烁效果,这一点儿也不好。
具有一个全局调色板意思是一个2-pass(二次验码)压缩方式(除非你愿意把所有的视频帧都存储在内存里)。
第一遍是对整个图片计算一个调色板,这就是新的palettegen滤波器参与进来的地方。这个滤波器对每一帧的所有颜色制作一个直方图,并且基于这些生成一个调色板。
在技术层面上还存在一些琐事:这个滤波器实现了Paul Heckbert的这篇Color Image Quantization for Frame Buffer Display (1982)论文中的算法的一个变种。这里是我记得的一些不同之处(或者说是关于论文中未定义的行为的特异性):
它使用一个全解析度的色彩直方图。而不是论文中作为关键建议使用的下采样 RGB 5:5:5 直方图,这个滤波器对1600万种可能的 RGB 8:8:8 色彩使用了一个哈希表。
对方格的分割任然是在中点上进行的,对要分割的方格的选择是根据方格中的颜色方差来进行的(一个带有大的色彩方差的方格将会优先截掉)。
对方格中的颜色求平均值取决于颜色的重要性,就我而言,这在论文中并没有定义。
当沿着一个维度(红,绿或蓝)进行分割方格时,假如相等,绿色是优先于红色的,然后再是蓝色。
所以不管怎样,这个滤波器都是在做色彩量化,并且生成一个调色板(通常保存在一个PNG文件里)。
它通常看起来像这个样子(upscaled):
第二遍(验码)是通过paletteuse滤波器完成的,就跟它的名字一样,它将会使用这个调色板来生成最终的量化颜色流,它的任务是在生成的调色板中找出最合适的颜色来表示输入的颜色。这也是你可以选择使用哪种抖动方法的地方。
这里同样有一些技术侧面上的小问题:
滤波器实现了五种抖动方法,然而最初的论文中只提出了一种方法。
就像palettegen一样,色彩分辨率(将24-bit输入颜色映射到一个调色板条目上)的完成没有破坏输入。它是通过一个K-d Tree(当k = 3时,很明显,每一个维度都是RGB的组成部分)的迭代实现和一个缓存系统来达到这一目标的。
使用这两个滤波器可以让你将GIF编码成这样(单全局调色板,无抖动):
评论删除后,数据将无法恢复
评论(47)
引用来自“Feng_Yu”的评论
gif就不是ffmpeg的强项,可以说处理图片效果质量不怎么样。stackoverflow的建议是使用convert命令(imagemagick)引用来自“哆啦比猫”的评论
convert 瞬间撑爆4G内存……引用来自“Feng_Yu”的评论
你处理了什么?我上次把一组几百张720p的png帧序列合并成gif,速度挺快,也没见撑爆内存的。引用来自“哆啦比猫”的评论
720p 10秒 30fps,撑爆了引用来自“Feng_Yu”的评论
gif就不是ffmpeg的强项,可以说处理图片效果质量不怎么样。stackoverflow的建议是使用convert命令(imagemagick)引用来自“哆啦比猫”的评论
convert 瞬间撑爆4G内存……引用来自“Feng_Yu”的评论
你处理了什么?我上次把一组几百张720p的png帧序列合并成gif,速度挺快,也没见撑爆内存的。引用来自“Feng_Yu”的评论
gif就不是ffmpeg的强项,可以说处理图片效果质量不怎么样。stackoverflow的建议是使用convert命令(imagemagick)引用来自“哆啦比猫”的评论
convert 瞬间撑爆4G内存……引用来自“Feng_Yu”的评论
gif就不是ffmpeg的强项,可以说处理图片效果质量不怎么样。stackoverflow的建议是使用convert命令(imagemagick)