阅读 15

【译】图片压缩的正确打开方式

图片压缩的正确打开方式

图像通常占据了页面大小的绝大部分,并且占据的绝大部分视觉空间。因此,优化图片通常会节省大量字节数和提升性能:浏览器下载的字节数越少,客户端带宽的争抢越少,浏览器的下载和屏幕加载内容速度越快。

图像优化既是一门艺术,也是一门科学:说它是一门艺术是因为对于如何最好地压缩单个图像并没有一个明确的答案;而说它是一门科学,则是因为有许多成熟的技术和算法可以显著地缩小图像的大小。给图像设置最佳配置需要仔细分析多个维度:格式所具备的能力、编码的内容、质量、像素维度等等。

优化矢量图

所有现代浏览器都支持 SVG,它是一种基于 xml 的二维图片格式。你可以直接在页面上嵌入 svg 标签,也可以作为外部资源引入。绝大多数基于矢量的绘图软件都可以创建 SVG 文件,甚至你也可以直接在编辑器中编辑它们。

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
    x="0px" y="0px" viewBox="0 0 612 792" xml:space="preserve">
<g id="XMLID_1_">
  <g>
    <circle fill="red" stroke="black" stroke-width="2" stroke-miterlimit="10" cx="50" cy="50" r="40"/>
  </g>
</g>
</svg>
复制代码

上面的例子渲染了下面这个简单的圆形,有黑色的轮廓和红色的背景。

svg 的圆形
svg 的圆形

可以看出,它包含很多元数据,比如层信息、注释,以及在浏览器中不需要的 xml 命名空间。你可以使用 SVGO这类工具来压缩 svg 文件大小。

SVGO 将上述的 SVG 文件的大小减少了58%,从 470 字节减少到 199 字节。

<svg version="1.2" baseProfile="tiny" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 612 792"><circle fill="red" stroke="#000" stroke-width="2" stroke-miterlimit="10" cx="50" cy="50" r="40"/></svg>
复制代码

因为 SVG 是一种基于 xml 的格式,所以还可以应用GZIP 压缩来减少传输大小。但是你需要先在服务器配置好压缩相关的配置。

一个栅格图像只是单个“像素”的二维网格,例如,一个100x100 像素的图像是一个 10,000 像素的序列。每个像素都存储了“RGBA”值:(R)红色波段,(G)绿色波段,(B)蓝色波段,(A) alpha 透明波段。

在浏览器内部,为每个波段配置了 256 个值,每个波段占用了 8 bit(2 ^ 8 = 256),所以每像素占用了 4 字节(4波段 x 8 bit = 32 bit = 4字节)。因此,如果我们知道栅格图片的尺寸,就可以轻松地计算文件大小

  • 100x100像素的图像由10,000像素组成
  • 10,000像素 x 4字节= 40,000字节
  • 40,000字节/ 1024 = 39 KB

(看不懂计算的可以看文末,有详细例子)

顺便提一下,不管传输的图像格式是什么,当图像被浏览器解码时,每个像素总是占用4个字节的内存。对于大型图像和没有大量可用内存的设备(例如低端移动设备)来说,这可能引发性能问题。

尺寸 像素 文件大小
100 x 100 10,000 39 KB
200 x 200 40,000 156 KB
300 x 300 90,000 351 KB
500 x 500 250,000 977 KB
800 x 800 640,000 2500 KB

对于一张 100x100 像素的图片来说,39kb 好像没什么问题。但是当尺寸变大时,文件大小会急速膨胀,使得下载耗费的性能变得很昂贵。

这篇文章到目前为止只关注“未压缩”的图像格式。值得庆幸的是,我们通过一些操作来优化图像文件的大小。

一个简单的策略是将图像的“位深度”降低:降低到每波段 8bit。每个波段占 8 bit 给了每个波段 256 个值和 16,777,216 (256 ^ 3) 种颜色。如果我们把颜色调整到 256 种会发生什么呢?原本占用 8 bit 的 rgb现在只占用了 7 bit ,那么每像素就节省了 2 字节(参考上面的公式)。这比原先的每像素 4 个字节压缩了 50%!

从左到右(PNG): 32bit (16M种颜色),7bit (128种颜色),5 bit (32种颜色)。
从左到右(PNG): 32bit (16M种颜色),7bit (128种颜色),5 bit (32种颜色)。

具有渐变颜色的复杂场景(例如,渐变或天空)需要更多的颜色来避免失真,比如上面第三张图片的像素化天空。从另一个角度来看,如果图片只使用了很少的颜色,那么颜色数量过多只会浪费宝贵的文件体积而已。

接下来你会发现,一旦你优化了其中一个像素的数据,那么你还可以巧妙地查询到附近的像素:事实上,许多图像,尤其是照片,都有许多相近颜色的像素——例如:天空,重复的纹理等待。利用这些信息,压缩器可以应用增量编码。不是存储每个像素的值,而是存储和附近像素的差异:如果相邻像素相同,则设置为”零“,只需要花费 1 bit!

当然,我们不能在这里就停下脚步。

人眼对不同的颜色有不同程度的敏感度:你可以通过减少或增加颜色位深度来优化你的颜色编码,从而实现这一点。“附近”的像素会形成一个二维网格。这意味着每个像素都有多个邻居:你可以利用这个特性来进一步改进增量编码。我们可以不用查看每个像素的相邻像素,而是查看一个较大的元素块,并使用不同的设置对不同的元素块进行编码。

我们可以发现,图像的优化逐渐变得复杂了起来(或者说有趣了起来,这取决于你自己的观点),并且这是学术和商业研究的比较活跃的领域。图像占用了大量的字节,因此开发更好的图像压缩技术具有很大的价值。如果您想了解更多信息,请访问Wikipedia page,或者查看WebP 压缩技术白皮书以获得实际示例。

那说了那么多牛逼的,学术性的内容,它如何才能帮助我们优化网站上的图片呢?理解问题的关键是很重要的:RGBA像素、位深度和各种优化技术。在开始讨论各种栅格图像格式之前,理解并记住所有这些概念都是非常重要的。

无损图像压缩与有损图像压缩

对于某些数据类型的压缩,例如页面的源代码和可执行文件,最关键的是:压缩程序不能更改或丢失任何原始信息。一个确实或错误的数据可能会完全改变文件的内容和含义。而对于其他一些类型的数据,如图像、音频和视频,压缩后交付原始数据“近似”的表达,是完全可以接受的。

事实上,由于眼睛的工作原理,我们常常可以丢弃每个像素的一些信息,以减少图像的文件量——例如:我们的眼睛对不同的颜色有不同的敏感度,这意味着我们可以用更少的 bit 来编码一些颜色。因此,一个典型的图像优化流程是由两个高级步骤组成的:

  1. 图像通过有损过滤器处理,消除一些像素数据。
  2. 图像通过无损过滤器处理,压缩像素数据。

第一步是可选的,是否使用将取决于特定的图像格式,但最重要的是在于理解:任何图像都可以经过有损压缩来减小体积。事实上,各种图像格式(如GIF、PNG、JPEG和其他格式)之间的区别在于它们在应用有损压缩和无损压缩时所使用(或省略)的特定算法的组合。

那么,有损和无损优化的“最优”配置是什么呢?这答案取决于图像内容和你自己的标准,比如通过有损压缩优化文件大小和失真之间的权衡:在某些情况下,您可能希望跳过有损压缩,以完全保真的方式传递复杂的细节。这就是它没有统一的标准的原因,这也是发挥你自己判断能力的地方。

作为一个动手操作的例子,当使用有损格式(例如JPEG)时,压缩程序通常会公开一个可定制的“质量”设置(例如,Adobe Photoshop中的“保存为Web”功能提供的质量选项),那通常是1到100之间的数字,它控制着特定的有损和无损算法集合。为了获得最佳效果,可以尝试各种图像质量,不要害怕降低降低画质——一般情况下视觉效果不会减弱太多,但是文件大小可能会减小很多。

注意,由于编码图像的算法不同,不同图像格式的质量等级不能直接进行比较:值为 90 的高质量 JPEG 与相同值的 WebP 格式将产生完全不同的结果。事实上,即使是相同图像格式,不同质量级别也可能根据压缩器的实现算法而产生明显不同的输出。

图像优化清单

当你在优化你的图像时,要记住一些技巧和技巧

  • 优先使用矢量格式:矢量图像的分辨率和大小是独立的,这让它非常适合多设备和高分辨率的情况。
  • 缩小和压缩 SVG 资源:
    • 大多数绘图应用程序生成的 XML 标记通常包含可删除的和不必要元数据;
    • 确保您的服务器为 SVG 资源设置了 GZIP 压缩
  • 优先使用 WebP 而不是栅格图片:WebP 格式的图片大小通常会比旧格式小很多
  • 选择最佳栅格图像格式:确定你的功能需求,并选择适合它们需求的格式。
  • 多对栅格图片的最佳质量进行试验:不要害怕降低质量,通常显示结果是很不错的,而且大小节省非常明显
  • 删除不必要的图像元数据:许多栅格图像包含有关资源的不必要的元数据,例如地理信息、相机信息,等等。使用适当的工具去除这些数据。
  • 提供缩放后的图片:调整图像大小,并确保“显示”尺寸尽可能接近图像的“自然”尺寸。尤其要密切关注大型图像,因为它们占用了最大的开销。
  • 自动化,自动化,自动化!投资自动化工具和前端基建,以确保你的所有图片资源始终都被优化。

译者的话

这篇文章干货满满,建议看两遍。虽然都是学术性的知识点,但是知识的广度是非常重要的,说不定哪天就用到了呢?

开篇讲解了 SVG 的原理,SVG 图片一般是用作 icon,其实我们能够操作的并不多,只要注意开启 GZIP 即可。

再讲到了栅格图片的原理,栅格图片是我们最常用的图片格式。栅格图片的本质就是在每个像素点的位置存放一段表示颜色的 RGB 值。因为 255 = 2^8,二进制表示也就是 11111111,占用了 8 个比特;再由于 RGBA 有四个波段(红、黄、蓝、透明),一个 rgba 的白色用二进制表示就是(11111111,11111111,11111111,11111111),占用了 24bit,1 字节 = 8bit,所以一个 rgba 颜色就占用了 4 字节。再看一张 100x100 的图片,有 10,000 个像素,每个像素 4 字节所以就占用了 40000 字节。这就是一张栅格图片的计算方法了。

栅格图片的压缩一直是学术界的重中之重,压缩分为两种,有损和无损。

有损的原理是将人眼难以辨别的相似的颜色归为一类,直接以颜色相同处理(用 0 标识),这样有损图片画质。

无损的原理是只将附近相同的颜色标识为 0,保证数据不丢失和可还原。

一般我们会将两者结合使用,权衡画质和体积大小的优缺点,选取最优解。

最后总结了优化图片的一些经验,总而言之收获满满,是一篇高质量科普文。

本文使用 mdnice 排版