pngquant 是一个优秀的 PNG 图像的有损压缩(lossy compression)工具.

pngquant 官网:

pngquant 库: libimagequant—Image Quantization Library

pngquant Github:kornelski/pngquant -

pngquant 作者:Developed by Kornel Lesiński and contributors. Based on code by Greg Roelofs and Jef Poskanzer.

很多优秀的图片压缩工具,如 ImageAlpha、TinyPng,都诞生于 pngquant.


1.pngquant 安装

[1] - 方式一: 下载 deb 包,离线安装.

# ubuntu16.04 
sudo dkpg -i pngquant_2.5.0-1_amd64.deb

# ubuntu18.04
sudo dkpg -i pngquant_2.5.0-2_amd64.deb

[2] - 方式二: apt 安装

sudo apt-get update
sudo apt-get install pngquant

[3] - 方式三: 源码编译安装(版本最新)

Releases 源码下载:

git clone --recursive
tar -xzf pngquant-2.12.2-src.tar.gz

cd pngquant/
sudo make && sudo make install

安装完成后,可以查看 pngquant 的版本信息和帮助信息:

pngquant --version
# 2.12.2 (January 2018)
which pngquant
# /usr/local/bin/pngquant
pngquant -h

2. pngquant 命令行使用

pngquant 命令行基本使用:

pngquant --quality=65-80 image.png

pngquant 常用参数选项:

[1] - -f: 输出文件强制覆盖

[2] - -o: 指定输出的文件名

[3] - --ext: 指定后缀

[4] - --quality: 图片质量


pngquant image.png
# 指定输出文件名
pngquant image.png -o image_copy.png
# 强制覆盖
pngquant image.png -o image_copy.png -f

pngquant -h 参数列表:

[1] - --quality min-max:设置图片压缩质量的最小-最大区间,有效范围是0(最差)-100(最好).

[2] - --ext new.png:自定义输出图片的名称后缀. 默认是-or8.png-fs8.png. 如果采用 --ext=.png --force 参数项,则 pngquant 会覆盖输入文件.

[3] - -o out.png--output out.png:将输出结果写入到指定路径. 只支持单张输入图片.

[4] - --skip-if-larger:如果转换后文件仍比较大,则不保存转换后的文件.

[5] - --speed N:压缩速度与压缩质量的平衡,有效范围为 1(slowest, highest quality, smallest files) - 11(fastest, less consistent quality, light comperssion). 默认值为 3(推荐). speed10 可以大幅提高压缩速度,而只降低了5%的压缩质量.

[6] - --nofs:停用 Floyd-Steinberg dithering.

[7] - --floyd=0.5:控制 dithering 的程度(0=none, 1=full). 注:不能少了 =.

[8] - --posterize bits:根据字节数,降低调色板(palette)的精度.

[9] - --strip:Don't copy optional PNG chunks. Metadata is always removed on Mac (when using Cocoa reader).

3. pngquant Python 调用

主要是利用 os.system()实现.

Python - 调用终端执行命令 - AIUAI

#--*-- coding: utf-8 --*--
import os

png_file = "test_image.png"
os.system("pngquant --quality 50-80 %s" %png_file)
# 或
os.system("pngquant --quality 50-80 %s -o %s" %(png_file, "out_test_image.png"))

Github 上的一份 Python 调用 - Github - python-iOS-pngquant-PNG-

python 脚本 pngquant 压缩PNG,主要功能包括:

[1] - 会压缩根目录下的png 的图片;

[2] - 本地记录一份压缩过的所有图片信息;

[3] - 如果是相同名字的图片替换过,有三种情况:

(3.1) - 两种图片大小相同,不会操作;

(3.2) - 新的图片小于旧的图片,不会压缩,更新图片信息;

(3.3) - 新的图片大于旧的图片,会压缩,更新图片信息;

[4] - 如果是新增,直接压缩,更新存储记录.

#! /usr/bin/python
# -*- coding: utf-8 -*-
# -*- author: lianxue.aho -*-

import os
import os.path
import pickle

def pngquantPicture(file):
    os.system("pngquant -f --ext .png --quality 50-80 \"" + file + "\"")
    filesize = os.path.getsize(file)
    dict = {file: filesize}
    return dict;

def dumpStorageImageInfo(imageInfoList):
    output = open('storageImageInfo.pkl','wb')
    pickle.dump(imageInfoList, output)

def loadStorageImageInfo():
    if os.path.isfile('storageImageInfo.pkl'):
       pkl_file = open('storageImageInfo.pkl', 'rb')
       imageInfoListStorage = pickle.load(pkl_file)
       return imageInfoListStorage

def compareFileSize(imageInfoListStorage, file):
    oldImage = 0
    for index, imageInfoDict in enumerate(imageInfoListStorage):
        if file in imageInfoDict:
           oldImage = 1
           fileSizeOld = imageInfoDict[file]
           fileSizeNew = os.path.getsize(file)
           if fileSizeOld == fileSizeNew:
           elif fileSizeOld < fileSizeNew:
              dict = pngquantPicture(file)
              imageInfoListStorage[index] = dict
           elif fileSizeOld > fileSizeNew:
              imageInfoListStorage[index] = {file: fileSizeNew}

    return oldImage

def compress(path):
    imageInfoList = []
    imageInfoListStorage = loadStorageImageInfo();

    for dir_path, dir_names, file_names in os.walk(path):
        file_names = filter(
            lambda file_name: file_name[-4:] == '.png', 
        file_names = map(
            lambda file_name: os.path.join(dir_path, file_name), 
        for file in file_names:
            print file
            if imageInfoListStorage:
               oldImage = compareFileSize(imageInfoListStorage, file)
               print 'image are stored, not have to add picture'
               if oldImage == 0:
                  dict = pngquantPicture(file)
                  print 'image are stored, have to add picture'
               dict = pngquantPicture(file)
               print 'no image stored, have to add picure'

    if imageInfoListStorage:
       print 'update storage picture'
    elif imageInfoList:
       print 'add storage picture'

if __name__ == '__main__':

4. pngquant - Features && Algorithm

4.1. Features

[1] - High-quality palette generation using a combination of vector quantization algorithms.

[2] - Unique adaptive dithering algorithm that adds less noise to images than the standard Floyd-Steinberg.

[3] - Easy to integrate with shell scripts, GUIs and server-side software.

[4] - Fast mode for real-time processing/large numbers of images.


4.2. Algorithm

pngquant uses modified version of Median Cut quantization algorithm and additional techniques to mitigate deficiencies of Median Cut.

Instead of splitting boxes with largest volume or number of colors, boxes are selected to minimize variance from their median value.

Histogram is built with addition of a basic perception model, which gives less weight to noisy areas of the image.

To improve color further, histogram is adjusted in a process similar to gradient descent (Median Cut is repeated many times with more weight on poorly represented colors).

Finally, colors are corrected using Voronoi iteration (K-means), which guarantees locally optimal palette.

pngquant works in premultiplied alpha color space to give less weight to transparent colors.

When remapping, error diffusion is applied only to areas where several neighboring pixels quantize to the same value, and which are not edges. This avoids adding noise to areas which have high visual quality without dithering.

5. 相关材料

[1] - iOS Python脚本 用pngquant 压缩PNG使 APP瘦身10M以上

[2] - Using pngquant in PHP

Last modification:April 8th, 2019 at 08:46 pm