最有效地使用PNG之续篇:Zopfli优化

来源:互联网 发布:新网域名转阿里云 编辑:程序博客网 时间:2024/06/05 21:55

原文作者:Jeff Atwood

2007年,我写过一篇文章介绍使用PNGOUT来产生非常小的PNG图片。我仍然经常提起这个话题,因为8年后的今天,我随便在网上看到的PNG图片很有可能是未经优化的。

举个例子,来看看pbfcomics.com上最近发布的这张卡通图:


把图片从网站直接保存下来,我们发现,这幅漫画是800 x 1412、32位的PNG图像,文件大小为671012字节。让我们把它保存为几种不同的(无损)格式,来看一看这幅图像要占用多少空间:

  • BMP,24位                     3,388,854
  • BMP,8位                        1,130,678
  • GIF,8位,无抖动                147,290
  • GIF,8位,最大抖动           283,162
  • PNG,32位                      671,012

PNG表现不错,因为它像GIF一样采用了压缩算法;与GIF不同的是,你的图像位深不会被限制在令人不快的8位(256色)。现在,我们用PNGOUT(点击这里下载)来处理一下,看看情况会怎样?

  • 原始的PNG           671,012
  • PNGOUT               623,859                 7%

随便从哪里拿来一个PNG,用PNGOUT处理一下,你很可能获得大约10%的文件大小缩减,也许还能更多。记住,这是无损压缩!输出的图像质量是完全相同的。在网上传输的文件会变得更小;而且文件越小,解压缩就越快。同学们,这是免费带宽啊!还有比这更好的事吗?

嗯,还真有……

2013年,谷歌推出了一种完全向后兼容新算法,他们称之为Zopfli:

Zopfli产生的输出通常比zlib在最大压缩比的情况下还要小3~8%。我们相信,Zopfli代表了Deflate压缩算法的当前工艺水平。为了可移植性,ZopfliC语言写成。这个库只支持压缩;现有的软件都能对它解压缩。ZopfligzipZipPNGHTTP请求等使用的压缩是位流兼容的。

非常抱歉,这个情况我了解得太晚了!还是让我们来验证一下他们的大胆狂言吧。拿上面这幅图片来试验,会发生什么呢?

  • 原始的PNG           671,012
  • PNGOUT                       623,859                 7%
  • ZopfliPNG                     585,117   13%

看起来不错啊!不过,那只是一张图片。在discourse.org上,我们都喜欢用表情图片。让我们试试第一版表情Emoji One——那是一整套64 x 64、32位的842个PNG文件:

  • 原始的PNG           2,328,243
  • PNGOUT               1,969,973             15%
  • ZopfliPNG             1,698,322         27%

哇,此等好事,我岂能错过!

在我的测试中,Zopfli处理过的PNG图像总能比PNGOUT小3~8%,尽管PNGOUT已经非常强大了——这真是令人难以置信的成就!而且,任何使用标准gzip压缩的资源都能从Zopfli获益,比如jQuery:


我们再来看一看各种标准的压缩测试:

测试素材

gzip -9

kzip

Zopfli

Alexa 10k

128mb

125mb

124mb

Calgary

1017kb

979kb

975kb

Canterbury

731kb

674kb

670kb

enwik8

36mb

35mb

35mb

 (奇怪得很,我之前都没有听说过kzip。事后证明,那又是我们的老朋友Ken Silverman的杰作。他也许使用了跟PNGOUT工具一样的压缩技巧。)

不过,有一个问题,因为问题总是有的——它同时也慢80倍。不,我没有搞错。你也没看错!

  • gzip -9                        5.6s
  • 7zip mm=Deflate mx=9         128s
  • Kzip                    336s
  • Zopfli                          454s

Gzip压缩一般比上表里的速度还要快一点,因为第9级是比较慢的:

 

时间

大小

gzip -1

11.5s

40.6%

gzip -2

12.0s

39.9%

gzip -3

13.7s

39.3%

gzip -4

15.1s

38.2%

gzip -5

18.4s

37.5%

gzip -6

24.5s

37.2%

gzip -7

29.4s

37.1%

gzip -8

45.5s

37.1%

gzip -9

66.9s

37.0%

你看吧,为了获得gzip -7和gzip -9之间那区区0.1%的压缩比差异,是否值得花上双倍的CPU时间呢?再说开一点,这也是为什么几乎所有的压缩工具的“极端”压缩级别或模式通常都不靠谱。你从算法悬崖边上快速掉了下去,因此你须待在曲线的中间或者最理想的位置,这常常就是缺省的压缩级别。他们选择那些缺省参数总是有原因的。

PNGOUT也不快,别急着用它;想象一下为了压缩一幅图像或一个文件会慢80倍的(这还是好的情况!),这肯定让人望而却步。如果只是处理小图片,你可能注意不到这个问题。但如果PNG比较大,你基本上可以出去吃一个三明治;或者如果你有一个多核CPU,处理完PNG的时间够你吃4~16个三明治。这也是为什么使用Zopfli来处理用户上传的图片可能是不明智的,因为第一台尝试用Zopfli处理10k x 10k PNG图片的服务器会掉入绝望的深渊。

然而,请记住解压缩的速度是一样的,并且绝对安全。这意味着你可能只想用Zopfli处理一些可以预处理的资源,也就是说,压缩一次,然后用于几百万次的下载场景——这跟你的用户上传一些PNG图片,不管你对这些图片做何等的优化,可能最多也只会被浏览几百次或几千次的应用场景是不同的。

举个例子吧。在discourse.org,我们有一个默认头像渲染器,可以为用户基于他们的用户名里的第一个字母产生一个很好看的PNG格式的头像,并且根据用户名的哈希值选定一种颜色。噢,对了,我们还用了很漂亮的来自谷歌的开源字体Roboto。


我们在输出头像图片的优化上花费了很多时间,因为这些头像会被使用几百万次。基于这些约束条件:

  • 10个数字
  • 26个字母
  • 大约256种颜色
  • 5种大小

预先把这些头像生成出来就是45000个文件,也并不是毫无道理。我们还有一个所有discourse.org实例都能访问的中央https CDN,需要的话也可以用于部署这些头像图片,这样可以进一步减小负载、提高缓存命中率。

因为这些图像都是单色的,为了节省空间,我把调色板降低到了8位(实际上用了128种颜色)。当然,我用PNGOUT优化处理了这些文件。它们差不多已经小到极限了。当我对这些头像文件执行Zopfli时,我超级兴奋,因为我期望看到3~8%的文件大小缩减。不过,在处理命令执行完之后,我看到的是……分别省了1字节、5字节、2字节……我有点忧伤!

(没错,生成“有损的”PNG图像在技术上是可行的,尽管有点奇怪,但我想那样有悖于PNG的精神——它就是为无损图像而生的!如果你想要有损的图像,那就用JPEG或其他格式吧。)

Zopfli的最大特色是,假设你不介意极高的CPU要求,它就是“用完就丢”的一次性优化步骤,你可以应用在任何地方,而且不会受到任何伤害。好吧,除了可能会烧掉很多空闲的CPU周期。

如果你参与的项目需要提供压缩素材,仔细看一看Zopfli吧。它不是银弹——听了上述建议之后,你拿自己的文件来测试看看——但对于干我们这行的人来说,它真正是给我们送来了免费带宽啊!

2 0