减少PNG图片大小

时间:2023-02-04 23:23:59

转载请注明出处:http://blog.csdn.net/asdzheng/article/details/51598623

本篇文章翻译自谷歌出的优化视频里面的光头佬(Colt McAnlis),原文地址需*, 以下正文:

我在谷歌工作的其中一个好处是可以浏览很多的Android程序,看看这些程序有哪些共同的地方可以优化。

后来我注意到一个可怕的趋势:越来越臃肿的PNG文件。

正如上一篇文章说的,PNG是一种很屌和可扩展的图片格式,图片质量高又支持透明度。因此,它成为这十几年来开发者梦寐以求的透明图片格式。

问题是PNG格式图片(就像上一篇提到的增加2个像素宽度就增加了两倍大小)很容易臃肿。因此,可以想象的是,很多现在在使用的PNG图片并没有经过严格的处理。

对于减少PNG文件大小,我有以下这些建议:

你应该使用一种优化工具

如果你理解PNG格式,有一些点是明显可以优化的:

  • 去掉不需要的区块(chunks)
  • 减少特殊颜色
  • 优化每一行的过滤算法
  • 优化DEFLATE压缩率

这几点人们早就知道,PNG优化是一个老大难的问题了。20年前,Ken_Silverman就写出了第一个流行的PNG优化工具,PNGOUT(这后来变成了毁灭公爵3D引擎的核心源码)。从那以后,又陆续的出现很多PNG优化工具,你随便google一下就能搜出一大堆,比如:

PNGQuantImageMagickPNGGauntletPNGOutPNGCrushOptiPNGCryoPNGPNGCompressorYahooSmush.itPNGOptimizerPunyPNGTinyPngPNGWolfAdvpngDeflOptDefluffHuffmixTruePNGPNGnq-s9Median Cut PosterizerscriptpngpngslimzopfliPNG

不幸的是,上面这么多工具里,没有一个工具能优化到所有的点,它们都只优化了其中一部分。所以这里并没有最好的工具,你只要把时间花在分析哪种工具对你最有用就好,其他的不去管它。

不过,对我个人而言上面列表里我最喜欢的工具是zopfliPNG。它提供了一个更有效和更强大层级的压缩器,可以更好的匹配你的数据,从而减少你的PNG文件大小。它可以减少PNG文件5%大小而不影响图片的任何质量,虽然有点少,但不要忘了还可以继续给用其他压缩器继续压缩。

很重要的一点是,如果有很多数据经过你的应用程序,你应该在数据传输过程(上传/发布图片)中就对PNG进行优化,不要让大图四处流动。

减少颜色

如果上面的工具都不好用,而你又想在使用上面这些工具前自己先手动处理一下图片,这时你需要注意的是:尽管你自己可以手动做很多处理,但我还是建议你应该只专注在减少你图片里特殊颜色的数量上,因为这会直接影响到所有其它层级的压缩效果,其他的事交给工具去做。

在PNG压缩过程中的过滤(filtering)阶段,压缩的强度是取决于相邻像素色的差异程序。例如:减少特殊颜色的数量会减少相邻像素色的差异程序,继而减小过滤出来后的值的大小。因此,到了DEFLATE压缩阶段就会得到更多重复的值,当然就能压缩得更小。

值得注意的是,减少了特殊颜色的数量,到了对图片做有损处理阶段会更有效果。

由于工具并没有人类对于图片质量的感知能力,在一些情况,只要有一点点小差错就可能使整张图片糊掉。这就是为什么这道程序你应该自己处理的原因。不过,如果你处理得当的话,用户应该不会注意到,同时你又节省了一大堆空间。

选择正确的像素格式

这本来可以不说的,但我见到一些APK使我不得不说:

你应该确保使用正确的PNG像素格式

例如,如果你的图片没有透明通道,但你又使用了RGBA 32bpp(bit per pixel 每像素32位)像素格式,那么你的图片就浪费了整整1/4的空间,这种情况你就应该使用24bpp的真彩色(RGB)或者用JPG格式。又或如果你的图片只包含灰度数据,那你应该只使用8bpp的GrayScale格式来保存图片。

这是最基本的事,不要因为使用了错误的像素格式而使PNG文件的臃肿。

索引图片,一级棒!!!

让我们继续,颜色的减少应该始终从尝试优化你的颜色数开始,那么就可以把图片定义为索引格式(INDEXED FORMAT)了。索引颜色的原理是,只选择使用最佳的256种颜色,然后把你的图片的所有像素都替换成这256种颜色。想想看从原来的1600万种颜色(24bpp)减少到256种,这会减少多少空间呀。

这有一张例图,右边是索引格式的图片:

减少PNG图片大小

上面这张Google涂鸦是从Photoshop的”save for web”选项导出来的,它的图片格式被设置为PNG8(8bpp),它的颜色都来自下面的调色板:

减少PNG图片大小

把真彩图转成索引图,你就得把每一个特殊的颜色都替换成调色板里的颜色,这样做能把占32位的一个像素减少到8位,这一步就减少了很多空间。

这个模式还会在过滤和压缩阶段带来进一步的节省:

  1. 由于特殊颜色已经减少,这意味着相邻的颜色有更高概率是同样的颜色
  2. 又由于相邻同样像素颜色数量增加,在过滤阶段会产生更多重复的值,在使用LZ77的DEFLAFT算法的压缩效果会更好

如果你的图片可以用索引图替代,这种方法将大大的减少图片的大小,所以花时间检查下你大部分图片可否被转换成索引图是值得的。

优化全透明的像素

索引模式其中一个不错的功能是 : 你可以在调色板中表示特定的颜色,以作为”透明”。当RGBA格式的PNG图片被解析进内存里,透明像素会被相应的处理。有趣的是透明通道完全是二进制的,它指定一个像素是否可见。

减少PNG图片大小

这种”穿透”型的透明图片会被更有效的压缩,通常来说,如果一张图片有大面积的透明背景,那么它会有很多相似的像素,这样更有利于压缩。

但这种情况只在索引模式下有效。在那些你要用这种”穿透”型透明图片,但你的PNG图片又得是RGB模式,这种情况很容易犯一种错误,就是没有把看不见的像素去掉。想一下下面这个例子,两张图片都支持透明通道和真彩色,但右边那张明显小很多。

减少PNG图片大小

造成这两张图片大小不同的原因也很明显:就你看有没有去掉透明通道:

减少PNG图片大小

尽管有透明通道的图片只被渲染其中一部分,但对于RGB层来说那些像素数据还在那(只是没渲染而已),这就意味着在过滤和压缩阶段还必须处理所有这些看不见的数据。

相反,如果你知道哪些像素不会显示,并确保它们是均匀的,那么我们就可以把那些看不见的像素替换成相同值。这样做会产生更多单一颜色的像素行,即会提高压缩率。

这是一个让你在RGB模式下使用”穿透”型透明但不至于产生大图的小技巧。

有损预处理

索引模式的PNG图片是屌,但是,仅仅256种颜色并不能准确的显示每一张图片,有些可能需要257,310,512,912种颜色才能让图片看起来像样。由于索引模式只支持256种颜色,所以这些图片都必须被定义成RGB(24bpp),尽管只使用了其中一部分颜色。

幸运的是,你可以手动的减少颜色数量类似用索引方式。

这种创建一张索引图片的过程,叫做矢量化可能更好一点。这是一个多维数的四舍五入过程,更直接的说是你图片的所有颜色会根据它们的相似性而分组。在给定的一个组里,组内的所有颜色会被替换成”中心点”那个值,这会最大限度减少误差。(类似沃罗诺伊图)

下面这张2维图片展示了这个过程是怎么设值的。

减少PNG图片大小

对一张图片使用矢量化替换会很有效的减少特殊颜色的数量,把它们替换成视觉上”相似”的一种颜色。

还有一个方法让你定义一张图片最大颜色数。

例如,下图左边是一张24位每个体像素的鹦鹉图,而右图只有16种颜色数可用。

减少PNG图片大小

你可以即刻的看出来右边的鹦鹉严重失真,所有过渡色都被替换掉,看起来一坨一坨,很显然这张图片需要多于16种颜色才不失真。

矢量化图片的这个过程,可以让你更清楚图片到底需要多少种颜色,也可以帮你减少图片大小。不过,除了pngquant,我还不知道有哪个工具可以让你手动指定具体的颜色数。

所以,如果这个工具你用得不爽,你也可以搞一个自己的矢量化工具来做这事。

协作

总结一下,你应该是用一个工具把图片尽可能的压缩到最小,这些工具的作者已经花了很多时间在解决这些问题上,站在巨人的肩膀上你会工作得更快。不过呢,在使用这些工具压缩前还是有很多工作你可以亲自去处理的。

So,加油做出更小的PNG图片吧!

PS:由于文人水平有限,如有翻译得不好的地方,请留言讨论。
PPS:CSDN的图片注释没显示出来,可以到我简书的博客看。