pngquant 是一个用于压缩 PNG 图像文件的工具。它可以显著减小 PNG 文件的大小,同时保持图像质量和透明度。通过减小文件大小,可以提高网页加载速度,并节省存储空间。pngquant 提供命令行接口和库,可轻松集成到各种应用程序和脚本中。

原理

pngquant 使用修改过的 Median Cut 量化算法以及其他技术来实现压缩 PNG 图像的目的。它的工作原理如下:

  1. 首先,pngquant 构建一个直方图,用于统计图像中的颜色分布情况。

  2. 接下来,它选择盒子来代表一组颜色。与传统的 Median Cut 算法不同,pngquant 选择的盒子是为了最小化盒子中颜色与中位数的差异。

  3. pngquant 使用感知模型给予图像中噪声较大的区域较少的权重,以建立更准确的直方图。

  4. 为了进一步改善颜色,pngquant 使用类似梯度下降的过程对直方图进行调整。它多次重复 Median Cut 算法,并在较少出现的颜色上增加权重。

  5. 最后,为了生成最佳的调色板,pngquant 使用 Voronoi 迭代(K-means)对颜色进行校正,以确保局部最优。

  6. 在重新映射颜色时,pngquant 只在多个相邻像素量化为相同颜色且不是边缘的区域应用误差扩散。这样可以避免在视觉质量较高且不需要抖动的区域添加噪声。

通过这些步骤,pngquant 能够在保持图像质量的同时,将 PNG 图像的文件大小减小到最低限度。

Median Cut 量化算法

假设我们有一张 8x8 像素的彩色图像,每个像素由红色、绿色和蓝色通道组成,每个通道的值范围是 0 到 255。

  1. 初始化:我们将图像中的每个像素视为一个颜色点,并将它们放入一个初始的颜色桶。

  2. 选择划分桶:在初始的颜色桶中选择一个具有最大范围的颜色通道,假设我们选择红色通道。

  3. 划分颜色:对于选定的红色通道,将颜色桶中的颜色按照红色通道的值进行排序,并找到中间位置的颜色值作为划分点。假设划分点的红色值为 120。

    划分前的颜色桶:

    • 颜色 1: (100, 50, 200)
    • 颜色 2: (150, 30, 100)
    • 颜色 3: (80, 120, 50)
    • 颜色 4: (200, 180, 160)

    划分后的颜色桶:

    • 子桶 1:
      • 颜色 1: (100, 50, 200)
      • 颜色 3: (80, 120, 50)
    • 子桶 2:
      • 颜色 2: (150, 30, 100)
      • 颜色 4: (200, 180, 160)
  4. 重复划分:我们继续选择颜色范围最大的通道,假设我们选择子桶 1 中的绿色通道。

    划分前的颜色桶(子桶 1):

    • 颜色 1: (100, 50, 200)
    • 颜色 3: (80, 120, 50)

    划分后的颜色桶(子桶 1):

    • 子桶 1.1:
      • 颜色 3: (80, 120, 50)
    • 子桶 1.2:
      • 颜色 1: (100, 50, 200)

    子桶 2 中只有两个颜色,不需要再进行划分。

  5. 颜色映射:将原始图像中的每个像素颜色映射到最接近的颜色桶中的颜色。

    假设原始图像中的一个像素为 (110, 70, 180),我们将它映射到颜色 1: (100, 50, 200)

    大概的公式为 sqrt((110-100)^2 + (70-50)^2 + (180-200)^2) ≈ 31.62

    通过 Median Cut 算法,我们将原始图像中的颜色数目从 64 个(8x8 像素)减少到 4 个颜色桶,从而实现了图像的量化

下载与安装

可以直接去官网下载

可以选择安装图形界面版、也可以选择安装命令行版。

图形化界面版就没什么好说的,直接下载自己电脑对应的版本,而且使用起来很简单

如果懂一点命令行,最好还是下载命令行版本,因为占用空间更小,压缩图片直接一行命令就完事,如果你会写脚本,还可以写一个专门的脚本用于一键批量压缩图片

命令行版本的使用

下载完 pngquant 后,为了在任何地方都能使用,需要配置一下环境变量

配置完成后,就可以在命令行中使用 pngquant 了

终端输入 pngquant --help 后,会看到下面提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pngquant, 2.12.5 (July 2019) (Rust), by Kornel Lesinski, Greg Roelofs.

usage: pngquant [options] [ncolors] -- pngfile [pngfile ...]
pngquant [options] [ncolors] - >stdout <stdin

options:
--force overwrite existing output files (synonym: -f)
--skip-if-larger only save converted files if they're smaller than original
--output file destination file path to use instead of --ext (synonym: -o)
--ext new.png set custom suffix/extension for output filenames
--quality min-max don't save below min, use fewer colors below max (0-100)
--speed N speed/quality trade-off. 1=slow, 4=default, 11=fast & rough
--nofs disable Floyd-Steinberg dithering
--posterize N output lower-precision color (e.g. for ARGB4444 output)
--strip remove optional metadata (default on Mac)
--verbose print status messages (synonym: -v)

Quantizes one or more 32-bit RGBA PNGs to 8-bit (or smaller) RGBA-palette.
The output filename is the same as the input name except that
it ends in "-fs8.png", "-or8.png" or your custom extension (unless the
input is stdin, in which case the quantized image will go to stdout).
If you pass the special output path "-" and a single input file, that file
will be processed and the quantized image will go to stdout.
The default behavior if the output file exists is to skip the conversion;
use --force to overwrite. See man page for full list of options.

下面对一些参数做详细的介绍

参数 作用
–force 覆盖已存在的输出文件
–skip-if-larger 只保存转换后的文件,如果它们比原始文件小
–output file 使用自定义后缀/扩展名替代 –ext
–ext new.png 设置自定义的输出文件后缀
–quality min-max 不保存低于 min 的颜色,使用较少的颜色低于 max
–speed N 速度/质量的权衡。1=慢,4=默认,11=快速且粗糙
–nofs 禁用 Floyd-Steinberg 差值
–posterize N 输出低精度的颜色(例如,ARGB4444 输出)
–strip 移除可选的元数据(默认在 Mac 上)
–verbose 打印状态消息

但其实我们一般用不到这么多,如果要压缩一个 png 图片的话,只需要输入以下命令

1
pngquant example.png --output test.png

上面命令表示将 example.png 压缩,并输出为 test.png,如果 test.png 已经存在,则会跳过转换,如果不存在,则会生成新的文件

使用 –quality 参数可以指定压缩质量,范围是 0-100,默认为 65,可以根据自己的需要调整,可以是一个范围例如 80-90,也可以是一个具体的值例如 85,值越高图片质量越好

1
pngquant example.png --output test.png --quality 80-90

上面命令表示将 example.png 压缩,并输出为 test.png,压缩质量在 80-90 之间

使用 –speed 参数可以指定压缩速度,范围是 1-11,默认为 4,可以根据自己的需要调整,值越高压缩速度越快,但压缩质量也会稍微下降

1
pngquant example.png --output test.png --speed 11

上面命令表示将 example.png 压缩,并输出为 test.png,压缩速度为 11,值越高压缩速度越快,但压缩质量也会稍微下降

使用 –nofs 参数可以禁用 Floyd-Steinberg 差值,可以提高压缩率,但会降低图片质量

1
pngquant example.png --output test.png --nofs

上面命令表示将 example.png 压缩,并输出为 test.png,禁用 Floyd-Steinberg 差值

使用 –strip 参数可以移除可选的元数据,可以减小文件大小,但会降低图片质量

1
pngquant example.png --output test.png --strip

如果你只是对 pngquant 感兴趣,并只是想学习基本使用,那么看到这里就行了

常用的三个命令,会下面下个基本够用了,图片名替换你实际的图片名

  • pngquant 73kb.png --output test.png

  • pngquant 73kb.png --quality=82 --output test.png

  • pngquant 73kb.png --speed=1 --quality=82 --output test.png