PNG 图片的压缩原理

泥巴电报 - 2021-03-22 - 技术分享 / 开发
2021-3-22|最后更新: 2023-5-27|
type
status
date
slug
summary
tags
category
icon
password
图片压缩是个很大的话题。本文从具体的工具进行初探其中的原理。
本文探索的工具有:
看一下 TinyPNG 官网上是如何介绍的:
它是如何工作的?
好问题! 当您上传一张PNG(Portable Network Graphics)图片时,图片中相似的颜色会被合并。 这种技术被称作“量化(quantization)”。 通过减少颜色数量,可以将24位PNG文件转换成更小的8位索引颜色图像。 所有不必要的元数据也被丢弃。 结果是支持100%透明度的更好的PNG文件。 两者兼得!
24位?8位?量化?这是些什么概念,还有它压缩后会损失清晰度吗?一起来探索下。

PNG 特点

  • 无损压缩
  • 体积小
  • 支持透明效果

PNG 类型

  • PNG 8:8即指8bit,2^8 = 256种颜色,这表示一张 PNG8 最多能存储256种颜色;
  • PNG 24:24指的是24位,分为3个,即RGB,各占8bit,可以包含 2^24 种颜色;
  • PNG 32:32表示有32bit,除了RGB占了24bit,还有8 bit可以表示透明度,0-255表示透明程度;

PNG 数据格式

首先看一张 png 图片的十六进制数据:
数据头部得 8950 4e47 0d0a 1a0a 是 PNG 图片的头,通常通过这串编码判断文件是否为 PNG 格式图片。
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR 00000010: 0000 0018 0000 002a 0806 0000 00a9 c59b .......*........ 00000020: 5e00 0000 0173 5247 4200 aece 1ce9 0000 ^....sRGB....... 00000030: 010f 4944 4154 480d d5d7 ed09 c230 1485 ..IDATH......0.. 00000040: e134 d8df e222 ddc0 1d1c c1d1 1cc1 1ddc .4...".......... 00000050: c045 c4df 952a 2790 d298 7edc 7b73 2218 .E...*'...~.{s". 00000060: 10d2 2aef d3a6 508c 73bf 1ab7 fbfd 800f ..*...P.s....... 00000070: db6b 1044 78d7 fb33 e6af 76b8 1cbb ee81 .k.Dx..3..v..... 00000080: 3963 3431 3eb8 f71e 41ef 9a27 13f1 be77 9c41>...A..'...w 00000090: a718 0780 39ee 86b5 5c7e 68dd 1557 8d78 ....9...\\~h..W.x 000000a0: 1c4c 2479 06d3 3b01 c658 ae00 20f6 fd2c .L$y..;..X.. .., 000000b0: 700e a314 1901 c46a 2009 5003 c900 3632 p......j .P...62 000000c0: 0b30 9145 8085 ac02 0c64 1328 4544 4009 .0.E.....d.(ED@. 000000d0: 2206 ac88 0ab0 206a 408b 9800 0d62 06a4 "..... [email protected].. 000000e0: 88c7 0f6b 0ef3 1d48 dfbc 2640 1ac7 caa8 ...k...H..&@.... 000000f0: 014d 5c0d 68e3 2ac0 1217 03d6 b808 2889 .M\\.h.*.......(. 00000100: 6f02 a5f1 5580 115f 0458 f159 8019 cf00 o...U.._.X.Y.... 00000110: 763c 016a c447 a056 3c00 35e3 00b2 0d48 v<.j.G.V<.5....H 00000120: 3849 dce5 641b 90d2 bfeb b8c0 e908 afeb 8I..d........... 00000130: b84c f882 b93f 9b42 616f 0028 39f9 0f07 .L...?.Bao.(9... 00000140: 1f25 0c91 1bbe 7533 6200 0000 0049 454e .%....u3b....IEN 00000150: 44ae 4260 82 D.B`.
我通过截图来描述下其他的字段含义:
notion image
每段的十六进制编码就有一个特定的含义。

PNG 的压缩

预解析

PNG 图片用差分编码(Delta encoding)对图片进行预处理,处理每一个像素点中每条通道的值。 差分编码的目的,就是尽可能的将png图片数据值转换成一组重复的、低的值,这样的值更容易被压缩。 e.g. 有图片为从左到右均匀渐变色,列举像素信息映射程数组为:
[1,2,3,4,5,6,7,8]。
使用 X - A 编码为
[2-1,3-2,4-3,6-5,7-6,8-7] => [1,1,1,1,1,1,1] [1,1,1,1,1,1,1]
出现大量重复数字,这非常容易进行压缩。 这也是多数颜色单一、颜色变化规律的图片特别好压缩,且可以压缩得特别小的原因。
差分编码处理的是每一个的像素点中每条颜色通道的值,R(红)、G(绿)、B(蓝)、A(透明)四个颜色通道的值分别进行处理。

压缩

压缩阶段会将预处理阶段得到的结果进行Deflate压缩,它由 Huffman 编码 和 LZ77压缩 构成。 Deflate压缩会标记图片所有的重复数据,并记录数据特征和结构,会得到一个压缩比最大的png图片 编码数据。
Deflate是一种压缩数据流的算法,任何需要流式压缩的地方都可以使用这种算法。
PNG 图片是由于很多的数据库组成的,但其实其中有很多的数据块是没有用的。例如通过 PS 生成的 PNG 图片中存在 “this image was made in photoshop.” 这样没有实际作用的数据块 chunks
notion image

LZ77派生算法

LZ77编码是一种基于字典的、“滑动窗”的无损压缩算法。 LZ77编码步骤如下: 1.从当前压缩位置开始,考察未编码的数据,并试图在滑动窗口中找出最长的匹配字符串,如果找到,则进行步骤 2 ,否则进行步骤 3 2.输出三元符号组( off,len,c )。其中 off 为窗口中匹配字符串相对窗口边界的偏移,len为可匹配的长度,c 为下一个字符,即不匹配的第一个字符。然后将窗口向后滑动 len+1 个字符,继续步骤 1. 3.输出三元符号组( 0,0,c )。其中 c 为下一个字符。然后将窗口向后滑动一个字符,继续步骤 1.

回到TinyPNG

TinyPNG 提到使用量化(Quantization)将相近的颜色合并,其将 PNG24 压缩成 PNG8 。这很明显是有损的压缩,它使用的就不是以上介绍 基于LZ/Huffman的DEFLATE 算法无损压缩。

其他的一些压缩方案对比

pngquant

pngquant 是国外一个有损的 PNG 压缩开源库,提供了命令行和源码库形式,可以将24位或32位的 RGBA PNG 图转换成8位 PNG 图并保留透明通道。(压缩率基本上可以达到70%)
notion image
根据资料显示,tinypng、pngquant、ImageAlpha、pngnq都是有损压缩,基本采用的都是quantization算法,将24位的PNG图片转换为8位的PNG图片,减少图片的颜色数;pngcrush、optipng、pngout、advpng都是无损压缩,采用的都是基于LZ/Huffman的DEFLATE算法,减少图片IDAT chunk区域的数据。一般有损压缩的压缩率会大大高于无损压缩。

参考

《数据压缩原理与应用》
如何选择开源协议Clang 插件计算圈复杂度