文章目录
一、PNG格式基础
PNG格式文件结构很清晰,仅由两部分构成:(1)文件头 (2)文件数据块(chunk)
(1)文件头
PNG的文件头为8个字节:
89 50 4E 47 0D 0A 1A 0A (16进制)
文件头是固定的,用以标明该文件是一个PNG格式文件。
(2)Chunk数据块
PNG格式文件中,除了文件头都是chunk数据块,不同的数据块在文件中起到不同的作用。
数据块结构
每个数据块由以下结构组成:
名称 | 字节数 | 备注 |
---|---|---|
Length | 4 | Chunk Data字段的长度 |
Chunk Type Code | 4 | 标志数据块的类型(ASCII码) |
Chunk Data | 由Length决定 | 数据 |
CRC | 4 | 循环冗余检测 |
因此,一个数据块的长度等于
12
+
L
e
n
g
t
h
12+Length
1
2
+
L
e
n
g
t
h
数据块分类:
按照标准分为
关键数据块
和
辅助数据块
两种。只有
关键数据块
是必要的。
下表列出了关键数据块:
数据块符号 | 数据块名称 |
---|---|
IHDR | 文件头数据块 |
IDAT | 图像数据块 |
IEND | 图像结束数据块 |
PLTE | 调色板数据块 |
一个真彩色PNG文件必须包含以上关键数据块的前三种,而伪彩色PNG图片还必须包含PLTE数据块,以携带调色板信息。
其它非关键数据块写到备忘里。
文件头数据块
IHDR是文件头数据块,描述了图片的一些元数据,13字节的数据分布如下:
域名 | 字节数 | 备注 |
---|---|---|
Width | 4 | 图像宽度 |
Height | 4 | 图像高度 |
Bit depth | 1 | 比特深度 |
ColorType | 1 |
表明图像的颜色类型:
0:灰度图像;2:真彩色图像;3:索引彩色图像 |
Compression method | 1 | 压缩方法 |
Filter method | 1 | 滤波器方法 |
Interlace method | 1 | 非隔行扫描为0、隔行为1 |
图像数据块
IDAT是图像数据块,存放着图像真正的数据信息。
图像结束数据块
IEND是图像结束数据块,它标志着图像的结束。
没有真正的数据内容,因此Length字段为0,所以,IEND数据块总是由以下字符组成:
00 00 00 00 49 45 4E 44 AE 42 60 82
调色板数据块
一个8bit伪彩色图像必须携带调色板数据块,它的数据部分由
2
8
2^8
2
8
个以下结构构成:
颜色 | 字节 |
---|---|
Red | 1 byte |
Green | 1 byte |
Blue | 1 byte |
根据调色板信息,图像数据可以索引得到对应的24bit RGB颜色信息。
二、案例分析
使用opencv-python得到一张10*10的PNG图像以便于分析:
import cv2
img=cv2.imread("input.bmp")
img1=cv2.resize(img,(10,10))
cv2.imwrite("output.png",img1)
(1)二进制文件分析
使用FlexHEX软件分析其组成,如图:
可以看到该png文件由四部分构成:
- 文件头——红色背景
- Chunk1——绿色背景
- Chunk2——白色背景
- Chunk3——蓝色背景
根据png数据块的知识,可以判断出三个chunk分别对应着IHDR(文件头数据块)、IDAT(图片信息数据块)和IEND(图片结束数据块),且本图为真彩色图片,没有调色板数据块。
(2)编程验证和元数据读取
使用C++编写程序,读上面的PNG文件。
主函数读取文件头和数据块
int main()
{
//打开文件、获取文件长度
FILE* file1;
fopen_s(&file1, "2.png", "rb");
fseek(file1, 0, SEEK_END);
int len = ftell(file1);
fseek(file1, 0, SEEK_SET);
//读入buffer
unsigned char* buffer = new unsigned char[len];
fread(buffer, sizeof(unsigned char),len , file1);
fclose(file1);
unsigned char* p = buffer;
//读文件头
cout << "文件头:" << endl;
unsigned char* head = new unsigned char[8];
for (int i = 0; i < 8; i++)
{
head[i] = *(p + i);
cout << int(head[i]) << " ";
}
cout << endl;
p += 8;
//读chunks
int count = 0;
while (p < len+buffer)
{
//Length字段:
cout << "------------------" << endl;
count += 1;
cout << "Chunk" << count<<":"<<endl;
int chunkLen = 0;
for (int i = 0; i < 4; i++)
{
chunkLen += *(p + i) << ((3 - i) * 8);
}
cout << "Chunk Data Length:" << chunkLen<<endl;
p += 4;
//Chunk Type Code字段:
string chunkType;
for (int i = 0; i < 4; i++)
{
char ch = *(p + i);
string st="";
st = ch;
chunkType.append(st);
}
cout << "Chunk Type:"<< chunkType << endl;
p += 4;
//Chunk Data字段
unsigned char* chunkData = new unsigned char[chunkLen];
for (int i = 0; i < chunkLen; i++)
{
chunkData[i] = *(p + i);
}
if (chunkType == "IHDR")
{
readMetaData(chunkData);
}
p += chunkLen;
//CRC字段
//没有写
p += 4;
}
return 0;
}
如果数据块类型是”IHDR“则读进入
readMetaData
函数读元数据
元数据读取readMetaData()
根据第一部分中png格式IHDR数据块的规则读取图片信息并输出。
void readMetaData(unsigned char *buf) {
cout << "=========Meta Data======="<<endl;
unsigned char *p = buf;
int width = 0;
for (int i = 0; i < 4; i++)
{
width += *(p + i) << ((3 - i) * 8);
}
p += 4;
int height = 0;
for (int i = 0; i < 4; i++)
{
height += *(p + i) << ((3 - i) * 8);
}
p += 4;
int bitDepth = *p;
p += 1;
int colorType = *p;
p += 1;
string colorT="";
switch (colorType)
{
case 0:
colorT = "灰度图像";
break;
case 2:
colorT = "真彩图像";
break;
case 3:
colorT = "索引图像";
break;
}
int compressionMethod = *p;
p += 1;
int filterMethod = *p;
p += 1;
int interlaceMethod = *p;
string interM = "";
switch (interlaceMethod)
{
case 0:
interM = "非隔行扫描";
break;
case 1:
interM = "隔行扫描";
break;
}
cout << "宽度(Width):" << width << endl;
cout << "高度(Height):" << height << endl;
cout << "比特位深(Bit Depth):" << bitDepth << endl;
cout << "色彩类型(Color Type):" << colorType <<" "<< colorT<< endl;
cout << "压缩类型(Compression Method):" << compressionMethod << endl;
cout << "滤波方法(Filter Method):" << filterMethod << endl;
cout << "隔行方法(Interlace Method):" << interlaceMethod <<" "<<interM<< endl;
cout << "======================="<<endl;
}
验证结果:
执行程序,得到结果如下:
根据结果,该PNG文件由文件头和三个chunk数据块构成,分别是IHDR、IDAT、IEND。
没有调色板数据块,元数据中色彩类型为2,即真彩色图像。
宽度为10,高度为10,位深为8,非隔行扫描。
实验结果与二进制文件的分析一致。
三、作业总结:
- png文件的数据结构比较简单有条理,方便读取信息
- 元数据中还有压缩类型和滤波方法这些暂时还没弄清楚
备忘:PNG其它可能数据块的信息
来源:
PNG文件格式详解