使用不同的软件工具,比如Gimp或者Windows Paint,保存同样的屏幕截图的时候,产生的PNG文件的大小会不同。在这个帖子里,我将用Python脚本尝试解析PNG文件,同时了解造成前面所说差异的原因。
简介
PNG是一种在Internet上常用的图形格式。我习惯于把屏幕截图保存为PNG格式。和普通的照片比起来,软件工具的屏幕截图用到的颜色数目会更少,所以我们可以通过数据压缩技术得到比较小的图形文件。另外,PNG是个开源的图形格式。基于这些原因,我更倾向于使用PNG格式来保存软件工具的屏幕截图。
说老实话,过去我很少注意软件产生的PNG文件的大小,直到最近,当我需要给个人网站准备图形文件的时候,为了更好的网页浏览效果,我才开始注意各种PNG文件的大小。一般来说,我主要使用Gimp和Windows Paint工具来创建软件工具的屏幕截图。前者Gimp是个强大的免费图形处理工具软件,用它来生成屏幕截图真的有点大材小用的感觉。WindowsPaint是Windows自带的图形工具。从Windows 7起,下Windows系统自带的Paint工具支持一些简单的图形编辑功能,勉强可以用来创建屏幕截图。在使用这些工具时,我假设这些工具可以生成最优的PNG图形文件。可是直到最近,我才发现,对于同样的屏幕截图,Gimp和Windows Paint生成的PNG文件大小并不一样。而且,尽管Paint并没有和PNG文件相关的选项,可是它仍然能生成比Gimp中选择最高压缩比的选项所产生的PNG还要小的文件。处于好奇,我尝试理解造成这种区别的原因。在这个帖子里,我将分享我从这个过程中学到的东东。
PNG 格式
这里,PNG指的是Portable Network Graphics。它是一个位图格式。最初,PNG是以作为GIF格式的替代品为设计、开发目标的。如想了解更多有关PNG格式,请移步相关Wikipedia网页。
简单来说,PNG文件的开始包含8字节的头信息,然后就是各种具有特别意义的数据块。每个数据块包含如下四个部分:
- 数据块的长度 – 4 字节
- 数据块的类型 – 4 字节
- 数据块数据 – 前面提到的数据块长度指定的大小
- CRC – 4 字节
也就是说,如果一个数据块的前四个字节是个数值为8192的整数,它表明数据块中的数据大小为8192字节。该数据块的大小即为 4+4+8192+4=8204 字节.
数据块的第二个部分(从第五到第八字节)指明了该数据块的类别。它通常是区分大小写的ASCII文本。一个PNG文件至少包含如下几个关键的数据块:
- IHDR: 必须是文件中的第一个数据块,它记录图形的包含宽、高等元信息。
- PLTE or sRGB: 图形所用到的所有颜色
- IDAT: 图形数据
- IEND: 图形结束的地方
一个PNG文件可以包含多个IDAT数据块。解析PNG文件的时候,所有的IDAT块需要按照它们在文件中存储的顺序依次连接起来,然后才解压缩。
下面的截图演示了一个PNG文件在TotalCommander的Lister工具中以十六进制选项查看的例子:
使用 Python 解析 PNG 文件
首先我们创建一个描述PNG文件中数据块的类:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
class Chunk(object): '''Definition of a chunk contained in a PNG file ''' def __init__(self, buf): '''TODO: to be defined1. ''' self.__length = 0 self.__type = 0 self.__data = [] self.__crc = 0 self.__size = 0 # The total size of the chunk in bytes if buf: self.load(buf) def load(self, buf): '''Load the chunk from the given buffer. @param buf List of bytes read from the PNG file @return: None ''' length_buf = binascii.hexlify(buf[:4]) #self.__length = int(''.join(length_buf), 16) self.__length = int(length_buf, 16) fmt = '4c' #self.__type = ''.join(struct.unpack(fmt, ''.join(buf[4:8]))) self.__type = buf[4:8].decode() end_data = 8+self.__length self.__data = buf[8:end_data] buf = buf[end_data:] crc_bytes = binascii.hexlify(buf[:4]) #self.__crc = int(''.join(crc_bytes), 16) self.__crc = int(crc_bytes, 16) self.__size = 8 + self.__length + 4; def totalSize(self): '''TODO: Returns the total size of the chunk in bytes. @return: TODO ''' return self.__size def type(self): '''TODO: Docstring for type. @return: TODO ''' return self.__type def data(self): '''TODO: Docstring for data. @return: TODO ''' return self.__data def length(self): '''TODO: Docstring for length. @return: TODO ''' return self.__length |
解析PNG文件的时候,首先打开该文件,读取所有内容到内存,然后解析文件的头信息以及数据块:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
class PNG(object): '''class for a PNG file. ''' def __init__(self, file_path): '''TODO: to be defined1. ''' self.__fileName = file_path self.__file = None self.__fobj = None self.size = [0, 0] self.__chunks = [] # List of chunks self.__width = 0 self.__height = 0 self.__bitDepth = 0 self.__colorType = 0 self.__cmpMtd = 0 self.__fltMtd = 0 self.__itlMtd = 0 self.__totalIDAT = 0 if file_path: self.readFile(file_path) self.__file = file_path def loadHeader(self, buf): '''TODO: Docstring for loadHeader. @param buf Byte buffer read from the import file @return: TODO ''' pass def readFile(self, file_path): '''TODO: Docstring for readFile. @param file_path Full path to the png file @return: True if successful, False otherwise ''' if file_path is None and self.__file is None: logger.info("File name is empty. Nothing to load!") return if file_path is not None: self.__file = file_path # Open the file for reading self.__fobj = open( self.__file, 'rb') fsize = os.path.getsize( file_path ) # map the file into memory fno = self.__fobj.fileno() data = mmap.mmap( fno, fsize, access=mmap.ACCESS_READ ) fmt = '8c' header = struct.unpack( fmt, data[:8] ) # Get the header information #self.loadHeader() #logger.debug("Header: %s" % header ) #working buffer buf = data[8:] ihdr = Chunk(buf) self.parseIHDR(ihdr.data() ) chunk_size = ihdr.totalSize() self.__chunks = [] self.__chunks.append(ihdr) while len(buf) > chunk_size: buf = buf[chunk_size:] ck = Chunk(buf) chunk_size = ck.totalSize() self.__chunks.append(ck) self.parseIDAT() ### close the file data.close() self.__fobj.close() ... |
一个PNG文件通常包含至少一个IDAT数据块。IDAT数据块包含了压缩后的图形数据。图形数据是个常见的矩形像素数组。数组的每个元素指明了相关的颜色。PNG图形文件中常见的颜色类型有三种:
- 灰度像素。最小像素大小:1 位
- 颜色索引像素。最小像素大小:1 位
- 真彩色像素:最小像素大小:24 位
另外,每个像素还可以包含关于透明颜色的信息。这个信息通常是8位,所以一个带有透明信息的真彩色像素大小为4字节。
PNG文件中的图形数据是从上到下依次排列的。每行的像素数据从左到右依次排列,形成一条扫描线。每条扫描线都包含两部分:第一个字节指明了所用的滤波方法,接下来就是从左到右依次排列的图形数据。所有的扫描线最后用和zlib兼容的DEFLATE算法压缩。因为PNG文件所用的数据压缩算法和zlib兼容,所以我们可以使用Python的zlib模块来解压缩PNG图形:
1 2 3 4 5 6 7 8 |
def testCompress(self): idat_data = b'' self.__totalIDAT = 0 for i in range(1, len(self.__chunks)): if self.__chunks[i].type() == 'IDAT': idat_data += self.__chunks[i].data() img_data = zlib.decompress( idat_data ) ... |
如想了解更多关于zlib压缩文件的格式,请移步zlib RFC网页 简而言之,zlib压缩后的数据在开头的地方有两个字节代表了和压缩算法有关的参数信息。如果以十六进制方式查看zlib压缩后的数据的时候,首两个字节看起来会像”78 da”。从这两个字节,我们可以猜出数据压缩时所用到的某些参数。但对于一些关键参数比如压缩级别,我们无法得知具体的数值,因为不同的压缩级参数会对应于相同的zlib头信息。为了获得真正的压缩参数,我们可以采取暴力测试的方式,用常见的压缩参数逐一测试。这种测试可以通过一个Python脚本来实现。
其实Python已经有可以解析PNG文件的模块,但是因为我需要测试不同的压缩参数,所以我自己写了一个简单的Python脚本来方便测试。完整的脚本可从这里下载。下载的脚本名为sspng.py。它可以用在Python2.7和3.4
环境中。运行该脚本时需要给定一个PNG文件名。脚本运行后会显示该PNG图形的基本信息,以及压缩信息。
Exampels
下面我们以TotalCommander 8.52a在Windows 10 x64上的屏幕截图为例。屏幕截图如下图所示
为了产生如上的屏幕截图,首先使得TotalCommander显示在桌面的最前端,按下“Alt-PrtScrn”。TotalCommander的截图将会被拷贝到系统的剪贴板上,然后分别把它黏贴到Gimp或者Windows Paint中,然后保存为PNG文件。这里我用的Gimp是最新的开发版本,Paint是系统自带的。在保存PNG文件的时候,Paint并没有任何和PNG相关的选项,而Gimp有包含PNG压缩级别等一些选项。这里全部采用缺省选项。Gimp和Paint产生的PNG文件大小分别是:
- Paint: 125,149 字节
- Gimp: 162,688 字节
假设Gimp产生的PNG文件路径为
c:\users\wdong\Pictures\totalcmd-en-gimp.png,我们可以按如下所示运行sspng.py:
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 |
C:\Users\dongw\Pictures>c:\Python27\python.exe sspng.py totalcmd-en-gimp.png Information about totalcmd-en-gimp.png IHDR information: Width: 1010 Height: 793 Bit depth: 8 Color type: 2 Compression method: 0 Filter method: 0 Interlace method: 0 Chunk pHYs: Data length: 9, total size: 21 bytes Chunk tIME: Data length: 7, total size: 19 bytes Chunk tEXt: Data length: 25, total size: 37 bytes Chunk IDAT: IDAT chunk size: 8192 bytes, total IDAT size: 162326 bytes Chunk IEND: Data length: 0, total size: 12 bytes size of img_data = 2403583 zlib header in the PNG file = 78, da With compression level 9, mem_level 8, strategy 1, zlib.compress gives 162326 bytes |
数据块中有793条扫描线。图形的真彩色的,也就是说每个像素有三个字节。由于每条扫描线的第一个字节是关于滤波信息的,那么这个IDAT中所包含的图形数据就有(1+1010*3)*793=2403583字节。压缩后的数据总共有162326字节大小,以8192字节的大小分割为多个小块分别储存在多个IDAT数据块中。看起来Gimp 2.9仍然使用标准的zlib压缩算法,因为我们可以通过Python的zlib模块实现和该PNG一样的压缩比
用Windows Paint产生的PNG文件做实验的输出结果如下:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
C:\Users\dongw\Pictures>c:\Python27\python.exe sspng.py totalcmd-cn-paint.png Information about totalcmd-cn-paint.png IHDR information: Width: 1010 Height: 793 Bit depth: 8 Color type: 2 Compression method: 0 Filter method: 0 Interlace method: 0 Chunk sRGB: Data length: 1, total size: 13 bytes Chunk gAMA: Data length: 4, total size: 16 bytes Chunk pHYs: Data length: 9, total size: 21 bytes Chunk IDAT: IDAT chunk size: 65445 bytes, total IDAT size: 124688 bytes Chunk IEND: Data length: 0, total size: 12 bytes size of img_data = 2403583 zlib header in the PNG file = 78, 5e With compression level 0, mem_level 1, strategy 0, zlib.compress gives 2427294 bytes With compression level 1, mem_level 1, strategy 0, zlib.compress gives 176182 bytes With compression level 2, mem_level 1, strategy 0, zlib.compress gives 160285 bytes With compression level 3, mem_level 1, strategy 0, zlib.compress gives 146284 bytes With compression level 4, mem_level 1, strategy 0, zlib.compress gives 134058 bytes With compression level 5, mem_level 1, strategy 0, zlib.compress gives 125760 bytes With compression level 6, mem_level 1, strategy 0, zlib.compress gives 117158 bytes With compression level 7, mem_level 1, strategy 0, zlib.compress gives 116340 bytes With compression level 8, mem_level 1, strategy 0, zlib.compress gives 113287 bytes With compression level 9, mem_level 1, strategy 0, zlib.compress gives 111966 bytes With compression level 0, mem_level 2, strategy 0, zlib.compress gives 2415384 bytes With compression level 1, mem_level 2, strategy 0, zlib.compress gives 170222 bytes With compression level 2, mem_level 2, strategy 0, zlib.compress gives 155723 bytes With compression level 3, mem_level 2, strategy 0, zlib.compress gives 143172 bytes With compression level 4, mem_level 2, strategy 0, zlib.compress gives 129169 bytes With compression level 5, mem_level 2, strategy 0, zlib.compress gives 122506 bytes With compression level 6, mem_level 2, strategy 0, zlib.compress gives 114908 bytes With compression level 7, mem_level 2, strategy 0, zlib.compress gives 114331 bytes With compression level 8, mem_level 2, strategy 0, zlib.compress gives 111654 bytes With compression level 9, mem_level 2, strategy 0, zlib.compress gives 110420 bytes With compression level 0, mem_level 3, strategy 0, zlib.compress gives 2409474 bytes With compression level 1, mem_level 3, strategy 0, zlib.compress gives 164668 bytes With compression level 2, mem_level 3, strategy 0, zlib.compress gives 150928 bytes With compression level 3, mem_level 3, strategy 0, zlib.compress gives 139918 bytes With compression level 4, mem_level 3, strategy 0, zlib.compress gives 124791 bytes With compression level 5, mem_level 3, strategy 0, zlib.compress gives 118426 bytes With compression level 6, mem_level 3, strategy 0, zlib.compress gives 112056 bytes With compression level 7, mem_level 3, strategy 0, zlib.compress gives 111606 bytes With compression level 8, mem_level 3, strategy 0, zlib.compress gives 108879 bytes With compression level 9, mem_level 3, strategy 0, zlib.compress gives 107891 bytes With compression level 0, mem_level 4, strategy 0, zlib.compress gives 2406529 bytes With compression level 1, mem_level 4, strategy 0, zlib.compress gives 161203 bytes With compression level 2, mem_level 4, strategy 0, zlib.compress gives 148187 bytes With compression level 3, mem_level 4, strategy 0, zlib.compress gives 137649 bytes With compression level 4, mem_level 4, strategy 0, zlib.compress gives 121817 bytes With compression level 5, mem_level 4, strategy 0, zlib.compress gives 115812 bytes With compression level 6, mem_level 4, strategy 0, zlib.compress gives 110109 bytes With compression level 7, mem_level 4, strategy 0, zlib.compress gives 109712 bytes With compression level 8, mem_level 4, strategy 0, zlib.compress gives 107154 bytes With compression level 9, mem_level 4, strategy 0, zlib.compress gives 106122 bytes With compression level 0, mem_level 5, strategy 0, zlib.compress gives 2405059 bytes With compression level 1, mem_level 5, strategy 0, zlib.compress gives 159511 bytes With compression level 2, mem_level 5, strategy 0, zlib.compress gives 146528 bytes With compression level 3, mem_level 5, strategy 0, zlib.compress gives 136218 bytes With compression level 4, mem_level 5, strategy 0, zlib.compress gives 120177 bytes With compression level 5, mem_level 5, strategy 0, zlib.compress gives 114230 bytes With compression level 6, mem_level 5, strategy 0, zlib.compress gives 108545 bytes With compression level 7, mem_level 5, strategy 0, zlib.compress gives 108230 bytes With compression level 8, mem_level 5, strategy 0, zlib.compress gives 105560 bytes With compression level 9, mem_level 5, strategy 0, zlib.compress gives 104583 bytes With compression level 0, mem_level 6, strategy 0, zlib.compress gives 2404324 bytes With compression level 1, mem_level 6, strategy 0, zlib.compress gives 158833 bytes With compression level 2, mem_level 6, strategy 0, zlib.compress gives 146029 bytes With compression level 3, mem_level 6, strategy 0, zlib.compress gives 135299 bytes With compression level 4, mem_level 6, strategy 0, zlib.compress gives 120105 bytes With compression level 5, mem_level 6, strategy 0, zlib.compress gives 114075 bytes With compression level 6, mem_level 6, strategy 0, zlib.compress gives 108182 bytes With compression level 7, mem_level 6, strategy 0, zlib.compress gives 107714 bytes With compression level 8, mem_level 6, strategy 0, zlib.compress gives 104922 bytes With compression level 9, mem_level 6, strategy 0, zlib.compress gives 103967 bytes With compression level 0, mem_level 7, strategy 0, zlib.compress gives 2403959 bytes With compression level 1, mem_level 7, strategy 0, zlib.compress gives 157836 bytes With compression level 2, mem_level 7, strategy 0, zlib.compress gives 145142 bytes With compression level 3, mem_level 7, strategy 0, zlib.compress gives 134822 bytes With compression level 4, mem_level 7, strategy 0, zlib.compress gives 119127 bytes With compression level 5, mem_level 7, strategy 0, zlib.compress gives 113359 bytes With compression level 6, mem_level 7, strategy 0, zlib.compress gives 107632 bytes With compression level 7, mem_level 7, strategy 0, zlib.compress gives 107275 bytes With compression level 8, mem_level 7, strategy 0, zlib.compress gives 104603 bytes With compression level 9, mem_level 7, strategy 0, zlib.compress gives 103742 bytes With compression level 0, mem_level 8, strategy 0, zlib.compress gives 2403954 bytes With compression level 1, mem_level 8, strategy 0, zlib.compress gives 157680 bytes With compression level 2, mem_level 8, strategy 0, zlib.compress gives 145059 bytes With compression level 3, mem_level 8, strategy 0, zlib.compress gives 134853 bytes With compression level 4, mem_level 8, strategy 0, zlib.compress gives 118942 bytes With compression level 5, mem_level 8, strategy 0, zlib.compress gives 113425 bytes With compression level 6, mem_level 8, strategy 0, zlib.compress gives 107872 bytes With compression level 7, mem_level 8, strategy 0, zlib.compress gives 107516 bytes With compression level 8, mem_level 8, strategy 0, zlib.compress gives 104837 bytes With compression level 9, mem_level 8, strategy 0, zlib.compress gives 103848 bytes With compression level 0, mem_level 9, strategy 0, zlib.compress gives 2403954 bytes With compression level 1, mem_level 9, strategy 0, zlib.compress gives 158120 bytes With compression level 2, mem_level 9, strategy 0, zlib.compress gives 145273 bytes With compression level 3, mem_level 9, strategy 0, zlib.compress gives 134947 bytes With compression level 4, mem_level 9, strategy 0, zlib.compress gives 119473 bytes With compression level 5, mem_level 9, strategy 0, zlib.compress gives 114064 bytes With compression level 6, mem_level 9, strategy 0, zlib.compress gives 108411 bytes With compression level 7, mem_level 9, strategy 0, zlib.compress gives 108041 bytes With compression level 8, mem_level 9, strategy 0, zlib.compress gives 105321 bytes With compression level 9, mem_level 9, strategy 0, zlib.compress gives 104264 bytes With compression level 0, mem_level 1, strategy 1, zlib.compress gives 2427294 bytes With compression level 1, mem_level 1, strategy 1, zlib.compress gives 176182 bytes With compression level 2, mem_level 1, strategy 1, zlib.compress gives 160285 bytes With compression level 3, mem_level 1, strategy 1, zlib.compress gives 146284 bytes With compression level 4, mem_level 1, strategy 1, zlib.compress gives 138646 bytes With compression level 5, mem_level 1, strategy 1, zlib.compress gives 129359 bytes With compression level 6, mem_level 1, strategy 1, zlib.compress gives 120436 bytes With compression level 7, mem_level 1, strategy 1, zlib.compress gives 119583 bytes With compression level 8, mem_level 1, strategy 1, zlib.compress gives 116264 bytes With compression level 9, mem_level 1, strategy 1, zlib.compress gives 115021 bytes With compression level 0, mem_level 2, strategy 1, zlib.compress gives 2415384 bytes With compression level 1, mem_level 2, strategy 1, zlib.compress gives 170222 bytes With compression level 2, mem_level 2, strategy 1, zlib.compress gives 155723 bytes With compression level 3, mem_level 2, strategy 1, zlib.compress gives 143172 bytes With compression level 4, mem_level 2, strategy 1, zlib.compress gives 134176 bytes With compression level 5, mem_level 2, strategy 1, zlib.compress gives 126207 bytes With compression level 6, mem_level 2, strategy 1, zlib.compress gives 118319 bytes With compression level 7, mem_level 2, strategy 1, zlib.compress gives 117738 bytes With compression level 8, mem_level 2, strategy 1, zlib.compress gives 114529 bytes With compression level 9, mem_level 2, strategy 1, zlib.compress gives 113488 bytes With compression level 0, mem_level 3, strategy 1, zlib.compress gives 2409474 bytes With compression level 1, mem_level 3, strategy 1, zlib.compress gives 164668 bytes With compression level 2, mem_level 3, strategy 1, zlib.compress gives 150928 bytes With compression level 3, mem_level 3, strategy 1, zlib.compress gives 139918 bytes With compression level 4, mem_level 3, strategy 1, zlib.compress gives 128996 bytes With compression level 5, mem_level 3, strategy 1, zlib.compress gives 121984 bytes With compression level 6, mem_level 3, strategy 1, zlib.compress gives 115209 bytes With compression level 7, mem_level 3, strategy 1, zlib.compress gives 114679 bytes With compression level 8, mem_level 3, strategy 1, zlib.compress gives 111747 bytes With compression level 9, mem_level 3, strategy 1, zlib.compress gives 110594 bytes With compression level 0, mem_level 4, strategy 1, zlib.compress gives 2406529 bytes With compression level 1, mem_level 4, strategy 1, zlib.compress gives 161203 bytes With compression level 2, mem_level 4, strategy 1, zlib.compress gives 148187 bytes With compression level 3, mem_level 4, strategy 1, zlib.compress gives 137649 bytes With compression level 4, mem_level 4, strategy 1, zlib.compress gives 125905 bytes With compression level 5, mem_level 4, strategy 1, zlib.compress gives 119155 bytes With compression level 6, mem_level 4, strategy 1, zlib.compress gives 113051 bytes With compression level 7, mem_level 4, strategy 1, zlib.compress gives 112575 bytes With compression level 8, mem_level 4, strategy 1, zlib.compress gives 109749 bytes With compression level 9, mem_level 4, strategy 1, zlib.compress gives 108727 bytes With compression level 0, mem_level 5, strategy 1, zlib.compress gives 2405059 bytes With compression level 1, mem_level 5, strategy 1, zlib.compress gives 159511 bytes With compression level 2, mem_level 5, strategy 1, zlib.compress gives 146528 bytes With compression level 3, mem_level 5, strategy 1, zlib.compress gives 136218 bytes With compression level 4, mem_level 5, strategy 1, zlib.compress gives 124186 bytes With compression level 5, mem_level 5, strategy 1, zlib.compress gives 117731 bytes With compression level 6, mem_level 5, strategy 1, zlib.compress gives 111589 bytes With compression level 7, mem_level 5, strategy 1, zlib.compress gives 111112 bytes With compression level 8, mem_level 5, strategy 1, zlib.compress gives 108285 bytes With compression level 9, mem_level 5, strategy 1, zlib.compress gives 107157 bytes With compression level 0, mem_level 6, strategy 1, zlib.compress gives 2404324 bytes With compression level 1, mem_level 6, strategy 1, zlib.compress gives 158833 bytes With compression level 2, mem_level 6, strategy 1, zlib.compress gives 146029 bytes With compression level 3, mem_level 6, strategy 1, zlib.compress gives 135299 bytes With compression level 4, mem_level 6, strategy 1, zlib.compress gives 123926 bytes With compression level 5, mem_level 6, strategy 1, zlib.compress gives 117330 bytes With compression level 6, mem_level 6, strategy 1, zlib.compress gives 111161 bytes With compression level 7, mem_level 6, strategy 1, zlib.compress gives 110609 bytes With compression level 8, mem_level 6, strategy 1, zlib.compress gives 107642 bytes With compression level 9, mem_level 6, strategy 1, zlib.compress gives 106434 bytes With compression level 0, mem_level 7, strategy 1, zlib.compress gives 2403959 bytes With compression level 1, mem_level 7, strategy 1, zlib.compress gives 157836 bytes With compression level 2, mem_level 7, strategy 1, zlib.compress gives 145142 bytes With compression level 3, mem_level 7, strategy 1, zlib.compress gives 134822 bytes With compression level 4, mem_level 7, strategy 1, zlib.compress gives 122917 bytes With compression level 5, mem_level 7, strategy 1, zlib.compress gives 116738 bytes With compression level 6, mem_level 7, strategy 1, zlib.compress gives 110681 bytes With compression level 7, mem_level 7, strategy 1, zlib.compress gives 110313 bytes With compression level 8, mem_level 7, strategy 1, zlib.compress gives 107484 bytes With compression level 9, mem_level 7, strategy 1, zlib.compress gives 106377 bytes With compression level 0, mem_level 8, strategy 1, zlib.compress gives 2403954 bytes With compression level 1, mem_level 8, strategy 1, zlib.compress gives 157680 bytes With compression level 2, mem_level 8, strategy 1, zlib.compress gives 145059 bytes With compression level 3, mem_level 8, strategy 1, zlib.compress gives 134853 bytes With compression level 4, mem_level 8, strategy 1, zlib.compress gives 123086 bytes With compression level 5, mem_level 8, strategy 1, zlib.compress gives 116643 bytes With compression level 6, mem_level 8, strategy 1, zlib.compress gives 110820 bytes With compression level 7, mem_level 8, strategy 1, zlib.compress gives 110508 bytes With compression level 8, mem_level 8, strategy 1, zlib.compress gives 107777 bytes With compression level 9, mem_level 8, strategy 1, zlib.compress gives 106770 bytes With compression level 0, mem_level 9, strategy 1, zlib.compress gives 2403954 bytes With compression level 1, mem_level 9, strategy 1, zlib.compress gives 158120 bytes With compression level 2, mem_level 9, strategy 1, zlib.compress gives 145273 bytes With compression level 3, mem_level 9, strategy 1, zlib.compress gives 134947 bytes With compression level 4, mem_level 9, strategy 1, zlib.compress gives 123774 bytes With compression level 5, mem_level 9, strategy 1, zlib.compress gives 117299 bytes With compression level 6, mem_level 9, strategy 1, zlib.compress gives 111406 bytes With compression level 7, mem_level 9, strategy 1, zlib.compress gives 111126 bytes With compression level 8, mem_level 9, strategy 1, zlib.compress gives 108399 bytes With compression level 9, mem_level 9, strategy 1, zlib.compress gives 107291 bytes |
这说明我们无法用标准的zlib压缩算法重现该PNG文件中的压缩比。比较以上两次试验结果,我们可以看到如下所示几个关于在Windows 10 x64系统上,Windows Paint和Gimp所产生的PNG文件的不同之处:
- Gimp以8192字节分割IDAT数据块,而Windows Paint以65445字节来分割
- Windows 10系统的Paint应该是在压缩图形数据之前进行了某种滤波预处理以优化压缩
- Windows 10系统的Paint并没有使用最优的压缩参数
使用Gimp 2.8以及Windows 7所带的Paint等做类似实验后发现,这些工具都是使用标准的zlib压缩算法在所产生的PNG中压缩数据的。
优化PNG文件
一般上有如下三种途径优化PNG图形文件:
- 使用颜色索引而不是真彩色,这样每个像素在图形数据中会更小
- 压缩之前对数据做预处理
- 使用压缩比更好的DEFLATE压缩算法实现,比如7-zip以及Google公布的Zopfli都是和zlib兼容的同时又有更高压缩比的DEFLATE算法实现
下面两个连接有更多关于优化PNG文件的信息: