第二次作业:PNG图像文件格式分析

1.PNG格式简介

png(便携式网络图形)

是一种采用无损压缩算法的位图格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。

数据块结构
PNG图像格式文件(或者称为数据流)由一个8字节的PNG文件署名(PNG file signature)域和按照特定结构组织的3个以上的数据块(chunk)组成。

PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是必需的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。

Critical Chunk(关键数据块),有四种类型:

IHDR,header chunk,包含有图像基本信息,作为第一个出现的数据块并且只出现一次。
PLTE,palette chunk,调色板数据块,必须存放在图像数据块之前。
IDAT,image data chunk,存储实际的图像数据。PNG数据包允许包含多个连续的图像数据块。
IEND,image trailer chunk,图像结束数据,表示PNG数据流结束。
Ancillary Chunk(辅助数据块),类型众多,PNG文件格式规范制定了10个辅助数据块:

背景颜色数据块bKGD(background color)。
基色和白色度数据块cHRM(primary chromaticities and white point)。
图像γ数据块gAMA(image gamma)。
图像直方图数据块hIST(image histogram)。
物理像素尺寸数据块pHYs(physical pixel dimensions)。
样本有效位数据块sBIT(significant bits)。
文本信息数据块tEXt(textual data)。
图像最后修改时间数据块tIME (image last-modification time)。
图像透明数据块tRNS (transparency)。
压缩文本数据块zTXt (compressed textual data)。
关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。

除上述中的限制外,各类chunk的出现顺序没有严格要求。同时在文件中的数据是按照:高位在前低位在后的顺寻存储的,这一点和BMP格式并不一致。
 

 2.PNG文件分析 

具体实例分析如图:

1.使用visual studio,用二进制编辑器,得到以十六进制表示的二进制文件数据流。

 PNG文件头共8个字节,用来识别该文件是否是PNG文件:

十进制表示十六进制表示
137 80 78 71 13 10 26 1089 50 4E 47 0D 0A 1A 0A

2.在PNG文件中,每个数据块都由下面四部分组成,所以,每次只需要读取前8个字节,就可以大致判断出数据块的类型和长度。

 

(1) IHDR(文件头数据块),由13字节组成,格式如下图所示:

 · 0x0D表示长度,13字节,后面四字节表明该数据块是HIDR数据块:

 · 图像宽度为0x400,即1024像素;长度为0x0358,即856像素:

 

 · 图像深度为8位,颜色类型为0x06,即图像为带a通道数据的真彩色图像,压缩方法,滤波器方法,隔行扫描方法:0x00,循环校验码 CRC:5A 67 9F 97:

(2)PLTE(调色版数据块),包含有与索引彩色图像相关的彩色变换数据,它仅与索引彩色图像有关,而且要放在图像数据块之前。PLTE可以包含1~256个调色板信息,每一个调色板信息由3个字节组成,因此,调色板的长度应该是3的倍数,否则,这将是一个非法的调色板。

 

这个案例是带有a通道数据的真彩色图像,没有调色板数据块。

(3)IDAT(图像数据块),存储实际的数据,在数据流中可包含多个连续顺序的图像数据块。

(4)IEND(图像结束数据),用来标记PNG文件或者数据流已经结束,并且必须要放在文件的尾部
十六进制下表示为:00 00 00 00 49 45 4E 44 AE 42 60 82

 辅助数据块

代码:

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
using namespace std;
struct fileHeader
{
    unsigned char head[8];
    void GetHead( ifstream &in ) { in.read((char *)head,8); }
    void Print()
    {
        for(auto i : head ) cout << (int) i<< " ";
        cout << endl;
    }
}file_header;
struct chunks
{
    unsigned int length = 0;
    char type[4];
    string s_type = "";
    unsigned char *data;
    unsigned char CRC[4];
    void GetChunk( ifstream &in )
    {
        unsigned char* buffer = new unsigned char[4];
        in.read((char*)buffer,4);
        for( int i = 0; i < 4; ++ i ) { length = (length << 8) + buffer[i]; }
        in.read(type,4);
        if( length != 0 ) {
            data = new unsigned char[length];
            in.read((char *) data, length);
        }
        in.read((char*)CRC,4);
        for( auto i : type ) s_type += (int)i;
        return;
    }
};
const string path = "0.png";
map< string, vector<int> > mp;
set<string> ancillary_types;
int main()
{
    ifstream in(path,ios::binary);
    file_header.GetHead(in);
    vector<chunks> v;
    int pos = -1;
    while(1)
    {
        chunks c_tmp;
        c_tmp.GetChunk(in);
        v.push_back(c_tmp); pos ++;
        mp[c_tmp.s_type].push_back(pos);
        if(c_tmp.s_type=="IEND") break;
        if(c_tmp.s_type != "IDAT"&& c_tmp.s_type != "IHDR" && c_tmp.s_type != "PLTE")
        {
            ancillary_types.insert(c_tmp.s_type);
        }
    }
    cout << "All Chunks information:" << endl;
    for( auto &i : mp )
    {
        cout << "Chunk type: " << i.first << ", Chunk position: ";
        auto &v = i.second;
        for( auto &j : v ) cout << j << " ";
        cout << endl;
    }
    cout << "ancillary chunks:" << endl;
    for( auto i : ancillary_types )
        cout << i << endl;
}

运行结果如下:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值