PNG文件解读(2):PNG格式文件结构与数据结构解读—解码PNG数据
PNG文件识别
之前写过《JPEG/Exif/TIFF格式解读(1):JEPG图片压缩与存储原理分析》,JPEG文件是以,FFD8开头,FFD9结尾,中间存储着以0xFFE0~0xFFEF 为标志的数据段。
对于一个PNG文件来说,其文件头总是由位固定的字节来描述的,HEX: 89 50 4E 47 0D 0A 1A 0A

其中第一个字节0x89超出了ASCII字符的范围,这是为了避免某些软件将PNG文件当做文本文件来处理。文件中剩余的部分由3个以上的PNG的数据块(Chunk)按照特定的顺序组成,因此,一个标准的PNG文件结构应该如下:
|
PNG文件标志 |
PNG数据块 |
…… |
PNG数据块 |

与JPEG格式如下
| SOI Marker | Marker XX size=SSSS | Marker YY size=TTTT | SOS Marker size=UUUU | Image stream | EOI Marker | ||||||
| FFD8 | FFXX | SSSS | DDDD...... | FFYY | TTTT | DDDD...... | FFDA | UUUU | DDDD.... | I I I I.... | FFD9 |

何其相似,但却相差甚远。png数据结构个人觉得比jpeg复杂
jpeg:段标识(FF)+段类型(1字节)+段长度(2字节)+数据块
png:段长度(4字节)+段类型/类型标志(4字节)+数据块+校验码(4字节)
png的每一段,称之为数据块。
PNG数据块(Chunk)
PNG定义了两种类型的数据块,
关键数据块(critical chunk),这是标准的数据块
辅助数据块(ancillary chunks),这是可选的数据块。
关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
下表就是PNG中数据块的类别,其中,关键数据块部分我们使用深色背景加以区分。
| 数据块符号 | 数据块名称 | 多数据块 | 可选否 | 位置限制 |
|---|---|---|---|---|
| IHDR | 文件头数据块 | 否 | 否 | 第一块 |
| cHRM | 基色和白色点数据块 | 否 | 是 | 在PLTE和IDAT之前 |
| gAMA | 图像γ数据块 | 否 | 是 | 在PLTE和IDAT之前 |
| sBIT | 样本有效位数据块 | 否 | 是 | 在PLTE和IDAT之前 |
| PLTE | 调色板数据块 | 否 | 是 | 在IDAT之前 |
| bKGD | 背景颜色数据块 | 否 | 是 | 在PLTE之后IDAT之前 |
| hIST | 图像直方图数据块 | 否 | 是 | 在PLTE之后IDAT之前 |
| tRNS | 图像透明数据块 | 否 | 是 | 在PLTE之后IDAT之前 |
| oFFs | (专用公共数据块) | 否 | 是 | 在IDAT之前 |
| pHYs | 物理像素尺寸数据块 | 否 | 是 | 在IDAT之前 |
| sCAL | (专用公共数据块) | 否 | 是 | 在IDAT之前 |
| IDAT | 图像数据块 | 是 | 否 | 与其他IDAT连续 |
| tIME | 图像最后修改时间数据块 | 否 | 是 | 无限制 |
| tEXt | 文本信息数据块 | 是 | 是 | 无限制 |
| zTXt | 压缩文本数据块 | 是 | 是 | 无限制 |
| fRAc | (专用公共数据块) | 是 | 是 | 无限制 |
| gIFg | (专用公共数据块) | 是 | 是 | 无限制 |
| gIFt | (专用公共数据块) | 是 | 是 | 无限制 |
| gIFx | (专用公共数据块) | 是 | 是 | 无限制 |
| IEND | 图像结束数据 | 否 | 否 | 最后一个数据块 |
我们目前只需关注标红的关键数据块即可。
数据块中有 4 个关键数据块:
文件头数据块 IHDR(header chunk):包含有图像基本信息,作为第一个数据块出现并只出现一次。
调色板数据块 PLTE(palette chunk):必须放在图像数据块之前。
图像数据块 IDAT(image data chunk):存储实际图像数据。PNG 数据允许包含多个连续的图像数据块。
图像结束数据 IEND(image trailer chunk):放在文件尾部,表示 PNG 数据流结束。
数据块连起来,大概这个样子:
| PNG 标识符 | PNG 数据块(IHDR) | PNG 数据块(其他类型数据块) | … | PNG 结尾数据块(IEND) |
|---|
就是一段段数据按照固定格式填充,头尾必要,中间填充图片的压缩数据。所以解读png的所有数据,就需要解读每个数据块
PNG数据块结构
PNG文件中,每个数据块由4个部分组成,如下:
| 名称 | 字节数 | 说明 |
| Length (长度) | 4字节 | 指定数据块中数据域的长度,其长度不超过(231-1)字节 |
| Chunk Type Code (数据块类型码) | 4字节 | 数据块类型码由ASCII字母(A-Z和a-z)组成 |
| Chunk Data (数据块数据) | 可变长度 | 存储按照Chunk Type Code指定的数据 |
| CRC (循环冗余检测) | 4字节 | 存储用来检测是否有错误的循环冗余码 |
Length 值的是除:length 本身,Chunk Type Code,CRC 外的长度,也就是 Chunk Data 的长度。
CRC: 一种校验算法。仅仅用来校验数据的正确性的
CRC(cyclic redundancy check)域中的值是对Chunk Type Code域和Chunk Data域中的数据进行计算得到的。
CRC具体算法定义在ISO 3309和ITU-T V.42中,其值按下面的CRC码生成多项式进行计算:
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
下面,我们依次来了解一下各个关键数据块的结构吧。
文件头数据块IHDR
文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。
文件头数据块由13字节组成,它的格式如下表所示。
|
域的名称
|
字节数
|
说明
|
| Width | 4 bytes | 图像宽度,以像素为单位 |
| Height | 4 bytes | 图像高度,以像素为单位 |
| Bit depth | 1 byte | 图像深度: 索引彩色图像:1,2,4或8 灰度图像:1,2,4,8或16 真彩色图像:8或16 |
| ColorType | 1 byte | 颜色类型: 0:灰度图像, 1,2,4,8或16 2:真彩色图像,8或16 3:索引彩色图像,1,2,4或8 4:带α通道数据的灰度图像,8或16 6:带α通道数据的真彩色图像,8或16 |
| Comdivssion method | 1 byte | 压缩方法(LZ77派生算法) |
| Filter method | 1 byte | 滤波器方法 |
| Interlace method | 1 byte | 隔行扫描方法: 0:非隔行扫描 1: Adam7(由Adam M. Costello开发的7遍隔行扫描方法) |
由于我们研究的是手机上的PNG,因此,首先我们看看MIDP1.0对所使用PNG图片的要求吧:
在MIDP1.0中,我们只可以使用1.0版本的PNG图片。并且,所以的PNG关键数据块都有特别要求:IHDR
文件大小:MIDP支持任意大小的PNG图片,然而,实际上,如果一个图片过大,会由于内存耗尽而无法读取。
ColorType/颜色类型:所有颜色类型都有被支持,虽然这些颜色的显示依赖于实际设备的显示能力。同时,MIDP也能支持alpha通道,但是,所有的alpha通道信息都会被忽略并且当作不透明的颜色对待。
Bit depth/色深:所有的色深都能被支持。
Comdivssion method/压缩方法:仅支持压缩方式0(deflate压缩方式),这和jar文件的压缩方式完全相同,所以,PNG图片数据的解压和jar文件的解压可以使用相同的代码。(其实这也就是为什么J2ME能很好的支持PNG图像的原因:))
Filter method/滤波器方法:尽管在PNG的白皮书中仅定义了方法0,然而所有的5种方法都被支持!
隔行扫描:虽然MIDP支持0、1两种方式,然而,当使用隔行扫描时,MIDP却不会真正的使用隔行扫描方式来显示。
PLTE chunk:支持
IDAT chunk:图像信息必须使用5种过滤方式中的方式0 (None, Sub, Up, Average, Paeth)
IEND chunk:当IEND数据块被找到时,这个PNG图像才认为是合法的PNG图像。
可选数据块:MIDP可以支持下列辅助数据块,然而,这却不是必须的。
关于更多的信息,可以参考http://www.w3.org/TR/REC-png.html
用十六进制查看器打开一个 PNG 文件:

分析如下:
| 十六进制 | 说明 |
|---|---|
| 00 00 00 0D | 数据块长度 13 字节 |
| 49 48 44 52 | 数据块类型码 “IHDR” 的 ASCII 字母 |
| 00 00 04 1D | 图像宽度 1053 |
| 00 00 02 B3 | 图像高度 691 |
| 08 | 图像深度 8 |
| 06 | 带α通道数据的真彩色图 |
| 00 | 压缩方法 |
| 00 | 滤波器方法 |
| 00 | 隔行扫描方法:00非隔行扫描 |
| 52 C3 75 3A | CRC (循环冗余检测) |
数据块-调色板数据块PLTE
调色板数据块PLTE(palette chunk)包含有与索引彩色图像(indexed-color image)相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块(image data chunk)之前。
PLTE数据块是定义图像的调色板信息,PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节组成:
| 颜色 | 字节 | 意义 |
|---|---|---|
|
Red |
1 byte |
0 = 黑色, 255 = 红 |
|
Green |
1 byte |
0 = 黑色, 255 = 绿色 |
|
Blue |
1 byte |
0 = 黑色, 255 = 蓝色 |
因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。
对于索引图像,调色板信息是必须的,调色板的颜色索引从0开始编号,然后是1、2……,调色板的颜色数不能超过色深中规定的颜色数(如图像色深为4的时候,调色板中的颜色数不可以超过2^4=16),否则,这将导致PNG图像不合法。
真彩色图像和带α通道数据的真彩色图像也可以有调色板数据块,目的是便于非真彩色显示程序用它来量化图像数据,从而显示该图像。
用十六进制查看器打开一个索引图像 PNG 文件:

分析如下:
| 十六进制 | 说明 |
|---|---|
| 00 00 00 27 | 数据块长度 39 字节 |
| 50 4C 54 45 | 数据块类型码 “PLTE” 的 ASCII 字母 |
|
B7 00 34 FF 99 00 60 00 73 FF 0F 00 FF ED 00 09 00 B2 FF 66 00 FF 3B 00 E2 00 15 8B 00 54 FF C1 00 33 00 99 FF FF 00 |
调色板颜色 13 个 |
| 48 29 75 2C | CRC (循环冗余检测) |

pHYs
物理像素数据块,它表示了图片的像素尺寸,或者是高宽比,它的结果如下
| Pixels per unit, X axis | 4 bytes (PNG unsigned integer) |
|---|---|
|
Pixels per unit, Y axis |
4 bytes (PNG unsigned integer) |
|
Unit specifier |
1 byte |
unit specifier的定义如下:
0:unit is unknown
1:unit is the metre
数据块-图像数据块 IDAT
图像数据块IDAT(image data chunk):它存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。
IDAT存放着图像真正的数据信息,因此,如果能够了解IDAT的结构,我们就可以很方便的生成PNG图像。
用十六进制查看器打开一个索引图像 PNG 文件:

| 十六进制 | 说明 |
|---|---|
| 00 00 00 D3 | 数据块长度 211 字节 |
| 49 44 41 54 | 数据块类型码 “IDAT” 的 ASCII 字母 |
| 78 9C ...... | 压缩的数据 211 字节,LZ77 派生压缩方法 |
| 52 98 5D 9D | CRC (循环冗余检测) |
数据块-图像结束数据IEND
图像结束数据IEND(image trailer chunk):它用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部。
如果我们仔细观察PNG文件,我们会发现,文件的结尾12个字符看起来总应该是这样的:
00 00 00 00 49 45 4E 44 AE 42 60 82

不难明白,由于数据块结构的定义,IEND数据块的长度总是0(00 00 00 00,除非人为加入信息),数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。
IHDR cHRM pHYs IEND

至此,我们已经能够从一个PNG文件中识别出各个数据块了。由于PNG中规定除关键数据块外,其它的辅助数据块都为可选部分,因此,有了这个标准后,我们可以通过删除所有的辅助数据块来减少PNG文件的大小。(当然,需要注意的是,PNG格式可以保存图像中的层、文字等信息,一旦删除了这些辅助数据块后,图像将失去原来的可编辑性。)
删除了辅助数据块后的PNG文件,现在文件大小为147字节,原文件大小为261字节,文件大小减少后,并不影响图像的内容。
其实,我们可以通过改变调色板的色值来完成一些又趣的事情,比如说实现云彩/水波的流动效果,实现图像的淡入淡出效果等等,在此,给出一个链接给大家看也许更直接:http://blog.csdn.net/flyingghost/archive/2005/01/13/251110.aspx,我写此文也就是受此文的启发的。
如上说过,IDAT数据块是使用了LZ77压缩算法生成的,由于受限于手机处理器的能力,因此,如果我们在生成IDAT数据块时仍然使用LZ77压缩算法,将会使效率大打折扣,因此,为了效率,只能使用无压缩的LZ77算法,关于LZ77算法的具体实现,此文不打算深究,如果你对LZ77算法的JAVA实现有兴趣,可以参考以下两个站点:
http://jazzlib.sourceforge.net/
参考文章:
音视频入门-11-PNG文件格式详解 https://www.cnblogs.com/binglingziyu/p/audio-video-basic-11-png-file-format-detail.html
PNG文件结构 https://www.cnblogs.com/Yuuki-/p/7868858.html
转载本站文章《PNG文件解读(2):PNG格式文件结构与数据结构解读—解码PNG数据》,
请注明出处:https://www.zhoulujun.cn/html/theory/multimedia/CG-CV-IP/8410.html
PNG文件解读(2):PNG格式文件结构与数据结构解读—解码PNG数据的更多相关文章
- C#将科大讯飞语音合成文件转换为MULAW音频格式
任务描述:通过科大讯飞语音合成组件在线完成文本转语音的合成,然后再转换为电话系统IVR要求的音频格式: wave mu-law 16位 8kHZ 64kbps. 完成步骤: 首先,我们要先通过科大讯飞 ...
- Office文件的Open Xml 格式
获取Office文件的Open Xml格式文件的步骤如下: 1.修改想要得到OpenXml格式文件的文件后缀名为.rar. 2.解压即可得到. 此时可以对xml文件进行操作,要想再次把这已经解压后的文 ...
- Python:将utf-8格式的文件转换成gbk格式的文件
需求:将utf-8格式的文件转换成gbk格式的文件 实现代码如下: def ReadFile(filePath,encoding="utf-8"): with codecs.ope ...
- ASP生成静态文件编码为UTF-8格式的HTML文件
一般在ASP环境下,运行动生静操作时都用到的是FSO,FSO是专门对文件进行操作的一个组件,FSO的编码属性只有三种,系统默认,Unicode,ASCII,并没有utf-8,所以一般中文系统上使用FS ...
- 关于MySQL中使用LOAD DATA INFILE导入csv文件时的日期格式问题
在使用MySQL时,常常会用到Load Data Infile来导入数据,在遇到Date类型的列时,有时会遇到格式转换的问题: 首先创建一张简单的people表,包含名字,生日,年龄三个字段: mys ...
- 【转】warning C4819,该文件保存为 Unicode 格式以防止数据丢失,处理方法
以下的解决方案只是把错误给屏蔽掉而已,并不能真正解决这个警告.仅供参考! 当项目引用到外部源代码后,经常出现4819错误,警告信息如下: warning C4819: 该文件包含不能在当前代码页(93 ...
- Jquery调用从ashx文件返回的jsonp格式的数据处理实例
开发环境:vs2010+jquery-1.4.min.js 解决问题:网上代码比较少,好多调试不通,返回数据不用json而用jsonp主要考虑解决跨域问题 开发步骤:打开VS2010,新建一web站点 ...
- C#获取文件夹下指定格式的所有文件
C#获取文件夹下指定格式的所有文件的方法,虽然很简单,但还是分享一下吧,用到时可以稍加修改和优化就可以使用. 获取指定目录下所有文件 //最要使用 System.IO.Directory.GetFil ...
- 位图文件(BMP)格式以及Linux下C程序实现(转)
源:位图文件(BMP)格式以及Linux下C程序实现 说到图片,位图(Bitmap)当然是最简单的,它是Windows显示图片的基本格式,其文件扩展名为*.BMP.由于没有经过任何的压缩,故BMP图 ...
- warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
------问题-------------------- Qt项目使用 VC++ 编译器出现此错误. warning: C4819: 该文件包含不能在当前代码页(936)中表示的字符.请将该文件保存为 ...
随机推荐
- 服务器没有开放3306端口 远程访问MySQL数据库方法
一.前言 当装有MySQL的服务器为了防止数据库被黑,提高安全性,把3306端口禁止掉,禁止对外访问,我之前写过一篇是借助跳板机的SSH隧道来访问实现安全,这种情况依然需要开放3306端口和使用一 ...
- Session入门实例
session对象(是JSP的9大内置对象之一): (1)session代表一次用户会话:从客户端浏览器连接服务器开始,到客户端浏览器与服务器断开为止,这个过程就是一次会话. (2)session作用 ...
- CSS 单行/多行文本溢出显示省略号(...)的实现
作者:WangMin 格言:努力做好自己喜欢的每一件事 我们在项目开发的过程中也许都遇到过这样的问题:我们需要实现这样一个需求,在一个父级元素中隐藏一个可能过长的文本.而这个需求可以分解为两个,一个是 ...
- 【Unity】 ScriptableObject ——生成多个ScriptableObject作为子对象,可以点击展开并显示二级菜单
官方是这么介绍ScriptabelObject的: "ScriptableObject 是一个可独立于类实例来保存大量数据的数据容器.ScriptableObject 的一个主要用例是通过避 ...
- Android 11 后的应用数据和文件
Android应用数据的保存方式有四种,分别是应用专属存储空间.共享存储.偏好设置.数据库. 应用专属存储空间 应用专属存储空间:存放应用专属文件,主要包括两个空间,卸载后移除 内部存储空间:位于系统 ...
- 关于如何来测一款app的思考
最近工作当中需要整体测一遍app,需要全方面思考并且尽可能覆盖所有待测点,因为整理总结了这篇关于app测试的总体大纲 一.功能测试 1.1界面测试 1.1.1导航测试 ---是否易于导航.导航是否直观 ...
- 深入了解UUID:生成、应用与优势
一.引言 在当今数字化时代,唯一标识一个对象的能力变得越来越重要.UUID(Universally Unique Identifier,通用唯一标识符)应运而生,作为一种保证全球唯一性的标识方法,广泛 ...
- 用dbeaver创建一个enum类型,并讲述一部分,mysql的enum类型的知识
写这个博客的目的就是我在网上看了半天,发现没有这方面的知识,也许是老手认为这个太简单了,不过我还是告诉新人使用dbeaver来创建一个enum类型的方法: 就是enum("a",& ...
- 云端开炉,线上训练,Bert-vits2-v2.2云端线上训练和推理实践(基于GoogleColab)
假如我们一定要说深度学习入门会有一定的门槛,那么设备成本是一个无法避开的话题.深度学习模型通常需要大量的计算资源来进行训练和推理.较大规模的深度学习模型和复杂的数据集需要更高的计算能力才能进行有效的训 ...
- StringBuilder、StringBuffer
StringBuilder 1.concat() 拼接字符串 2.indexOf() 查找 3.replace() 替换 4.subString(1,2) 截取 0,1,2 1开始 2结束 Strin ...