开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
使用 FFmpeg 处理高质量 GIF 图片 - 技术翻译 - 开源中国社区

使用 FFmpeg 处理高质量 GIF 图片 【已翻译100%】

标签: FFmpeg
oschina 推荐于 2年前 (共 17 段, 翻译完成于 03-20) 评论 47
收藏  
268
推荐标签: FFmpeg 待读

大约两年前, 我尝试改进FFmpeg对GIF编码的支持,至少要很体面。尤其是要在GIF编码器中加入透明机制。然而你写的代码并不总是能使其达到最优,这种情况非常常见。但这仍然只是阻止编码器陷入尴尬的尝试。

不过最近在 Stupeflix,我们需要一个方法给 Legend app 生成高质量的 GIF,所以我决定在这上面再花些功夫。

所有在这篇博文 FFmpeg 2.6 中列举的特性都是可用的,并且在Legend app的下一版本中将使用这些特性 (大概在3月26号左右)。

文章太长不要去读:Usage 部分关注怎么使用即可。

Parser7
 翻译得不错哦!

初始提升(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

centerimg

这个选项默认生效,当你的图片是高度运动的或者色彩变化强烈,你应该关闭它。

另一个实现的压缩机制是剪切,剪切是仅仅重绘GIF图中一个子矩形但不改变其他地方的基本手段。当然在影片中,这么做用处不大,我们后面再谈。

除了上述机制,之前我没有再取得什么进展。也可能是其他措施不太明显,总之,在图片质量上还存在不少缺陷。

yzfcat
 翻译得不错哦!

256 色的局限性

你可能知道,GIF 是受限于256色调色板。并且默认情况下,FFmpeg 只使用一个通用调色版去尝试覆盖所有的颜色区域,以此来支持含有大量内容的文件:

centerimg

被盗用户
 翻译得不错哦!

有序抖动和误差扩散

使用抖动来避免陷入这个问题(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
社会主义好
 翻译得不错哦!

更好的调色板

提高GIF图片质量的第一步就是定义一个更好的调色板。GIF格式存储了一个全局调色板,但你可以对一张图片(或者是子画面;覆盖在前一帧上的后一帧,但它可以覆盖在一个特定的偏移位置上以获得一个更小的尺寸)重新定义一个调色板。每一帧的调色板都可以取代全局调色板来只对一帧起作用。一旦你停止定义一个调色板,它将会回落到全局调色板。这意味着你不能对一系列的帧定义一个调色板,而这恰恰是你想做的。(典型的做法是在每个场景变化的时候定义一个新的调色板)。

所以,换句话说,你需要遵守这样的模式:一个全局调色板,或者,每帧一个调色板。

社会主义好
 翻译得不错哦!

每帧一个调色板 (未实现)

我最初开始在每帧中计算出一个调色板,但是我发现了这样存在如下缺陷:

  • 开销: 一个256色调色板大小为768B,并且不包括在LZW 算法机制中,所以它没有被压缩。同时因其必须储存在每帧中,这就意味着对于25FPS的序列来说会有每秒150千比特的开销( 768 *8 *25b =150 *1024b )。 虽然大多数时候这可以忽略。

  • 在我最初的测试当中由于调色板的变化产生了亮度闪烁效果,这一点儿也不好。

Parser7
 翻译得不错哦!

这就是我之所以没有使用这种方法而是选择计算一个全局调色板来代替的两个原因。现在我回想起来,它可能与重试这种方法有关,因为在某种程度上,现在的色彩量化比我当初测试的时候的状态要好一些。

对于一系列的帧的每一帧都使用相同的调色板(典型的做法是在场景变化时,就像前面提到的那样)也是可能的。或者,更好的做法是:只在子矩形变化时使用。

所有的这些都当作一个练习留给读者吧。欢迎补充,如果你对这个感兴趣的话可以随时和我联系。

社会主义好
 翻译得不错哦!

一个全局调色板(已实施)

具有一个全局调色板意思是一个2-pass(二次验码)压缩方式(除非你愿意把所有的视频帧都存储在内存里)。

第一遍是对整个图片计算一个调色板,这就是新的palettegen滤波器参与进来的地方。这个滤波器对每一帧的所有颜色制作一个直方图,并且基于这些生成一个调色板。

社会主义好
 翻译得不错哦!

在技术层面上还存在一些琐事:这个滤波器实现了Paul Heckbert的这篇Color Image Quantization for Frame Buffer Display (1982)论文中的算法的一个变种。这里是我记得的一些不同之处(或者说是关于论文中未定义的行为的特异性):

  • 它使用一个全解析度的色彩直方图。而不是论文中作为关键建议使用的下采样 RGB  5:5:5 直方图,这个滤波器对1600万种可能的 RGB 8:8:8      色彩使用了一个哈希表。

  • 对方格的分割任然是在中点上进行的,对要分割的方格的选择是根据方格中的颜色方差来进行的(一个带有大的色彩方差的方格将会优先截掉)。

  • 对方格中的颜色求平均值取决于颜色的重要性,就我而言,这在论文中并没有定义。

  • 当沿着一个维度(红,绿或蓝)进行分割方格时,假如相等,绿色是优先于红色的,然后再是蓝色。

所以不管怎样,这个滤波器都是在做色彩量化,并且生成一个调色板(通常保存在一个PNG文件里)。

它通常看起来像这个样子(upscaled):

centerimg

社会主义好
 翻译得不错哦!

颜色映射与抖动

第二遍(验码)是通过paletteuse滤波器完成的,就跟它的名字一样,它将会使用这个调色板来生成最终的量化颜色流,它的任务是在生成的调色板中找出最合适的颜色来表示输入的颜色。这也是你可以选择使用哪种抖动方法的地方。

这里同样有一些技术侧面上的小问题:

  • 滤波器实现了五种抖动方法,然而最初的论文中只提出了一种方法。

  • 就像palettegen一样,色彩分辨率(将24-bit输入颜色映射到一个调色板条目上)的完成没有破坏输入。它是通过一个K-d Tree(当k = 3时,很明显,每一个维度都是RGB的组成部分)的迭代实现和一个缓存系统来达到这一目标的。

使用这两个滤波器可以让你将GIF编码成这样(单全局调色板,无抖动):

centerimg

社会主义好
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(47)
Ctrl/CMD+Enter

mark
技术流
技术流
Very Good,MARK
mark
牛X
+1
好文!
感觉好利害
虽然看不懂,但是还是感觉好厉害!!
mark
ffmpeg官方提供有静态编译的Linux通用二进制包,我们要做的就是下载-解压-运行:
http://ffmpeg.org/download.html#build-linux
http://johnvansickle.com/ffmpeg/
不明觉厉
等bpg的动画
mark
mark
学习了
好文章,牛逼~
好文 好技
顶部