文章目录

  • 前置知识
    • PNG格式
    • zlib文件格式
    • deflate文件格式
    • LZ77压缩算法
    • 霍夫曼编码
    • 字典树
  • 综述
  • 过程
    • 获取文件信息
    • 获取调色板信息
    • 对IDAT数据块进行解码
      • 编写位处理工具
      • 解码zlib和Defalte格式的文件头
      • 构建霍夫曼树
      • 获取霍夫曼码表
      • 进行解码
    • 格式转换
  • 实验结果
  • 完整代码

前置知识

综述

png是由多个chunk组成,每个chunk保存了该png文件的某项信息。

其中数据部分被称作IDAT,里面存有png文件的图像信息。为了减小数据量,该块使用LZ77的衍生算法Deflate算法进行压缩,并使用zlib头进行包裹。

大致结构为:zlib头+deflate头+数据+校验码

被压缩压缩数据部分按照如下方式进行存储。
先存储长度数组,这个数组用于生成一颗霍夫曼树。
用生成的霍夫曼树对接下来的数据进行解码,获得长度码表和距离码表。
最后用长度码表和距离码表对数据进行恢复。

由于找不到合适图片且时间有限,该版本代码只适用于8bit带调色板并使用动态霍夫曼编码且IDAT仅有一块的PNG文件。

本次实验使用的文件如下:

过程

获取文件信息

声明所需要的信息。

struct fileHeader
{unsigned char head[8];void GetHead(ifstream& in) { in.read((char*)head, 8); }
};
struct iHDR
{unsigned int width, height;unsigned char bit_depth, color_type, compression_method, filter_method, interlace_method;void GetIHDR(unsigned char* buffer);void Print();
};
struct chunks
{unsigned int length;char type[4];string s_type = "";unsigned char* data;unsigned char CRC[4];chunks();void GetChunk(ifstream& in);
};

添加如下实现:

#include "header.h"void iHDR::Print()
{cout << "width: " << (int)width << " height: " << height << endl;cout << "bit depth: " << (int)bit_depth << endl;cout << "color type: " << (int)color_type << endl;cout << "compression method: " << (int)compression_method << endl;cout << "interlace method : " << (int)interlace_method << endl;
}
void iHDR::GetIHDR(unsigned char* buffer)
{for (int i = 0; i < 4; ++i) { width = (width << 8) + buffer[i]; }for (int i = 0; i < 4; ++i) { height = (height << 8) + buffer[i + 4]; }int pos = 8;for (auto i : { &bit_depth, &color_type, &compression_method, &filter_method, &interlace_method })*i = buffer[pos++];
}
chunks::chunks()
{length = 0;memset(type, 0, sizeof(type));data = nullptr;s_type = "";memset(CRC, 0, sizeof(CRC));
}
void chunks::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;
}

运行如下代码:

int main()
{ifstream in(in_path, ios::binary);if (!in.is_open()){cout << in_path << " open failed." << endl;exit(1);}fileHeader file_header;file_header.GetHead(in);int pos = 0;while(true){chunks tmp;tmp.GetChunk(in);chunks_vector.push_back(tmp);mp[tmp.s_type].push_back(pos++);if (tmp.s_type == "IEND") break;}for (auto &i : mp){cout << i.first << ": ";for (auto& j : i.second)cout << j << " ";cout << endl;}int position = mp["IHDR"][0];iHDR ihdr; ihdr.GetIHDR(chunks_vector[position].data);cout << endl;  ihdr.Print();
}

结果如下:

可以看到该png文件所拥有的数据块以及基本信息。
具体如下:

名称 取值
Width 80
Height 80
Bit depth 8位
ColorType 3,带有调色板
Compression method 0 ,deflate压缩算法
Filter method 0,即滤波方法 0
Interlace method 0,非隔行扫描

获取调色板信息

添加定义:

struct pLTE
{int r, g, b;static pLTE* GetPLTE(unsigned char* buffer, iHDR ihdr);
};

进行实现:

pLTE* pLTE::GetPLTE(unsigned char* buffer, iHDR ihdr)
{int plte_size = 1 << ihdr.bit_depth;pLTE* plte = new pLTE[plte_size];int pos = 0;for (int i = 0; i < plte_size; ++i){for (auto j : { &plte[i].r, &plte[i].g, &plte[i].b })*j = buffer[pos++];}return plte;
}

继续添加如下代码获取调色板信息:

    position = mp["PLTE"][0];pLTE* plte = pLTE::GetPLTE(chunks_vector[position].data, ihdr);

对IDAT数据块进行解码

编写位处理工具

由于PNG文件的数据部分并不按照字节为单位而是以bit为单位进行信息的存储,所以需要编写一个为处理工具来对这些数据进行解码。
添加如下声明:

class dataReader
{private:unsigned char* buffer;unsigned int bit_pos, byte_pos, pos_in_byte, length;
public:dataReader();void GetData(ifstream& in);void GetData(unsigned char* in_buffer, int length);unsigned int ReadNextBit();unsigned int ReadBits(int n);unsigned int ReadBitsR(int n);unsigned int ReadBytes(int n);void ReadBytes(unsigned char* buffer, int length);void GetPos();
};

进行实现:

#include "header.h"void dataReader::GetPos()
{cout << "Byte pos: " << byte_pos << endl;cout << "Bit pos: " << bit_pos << endl;cout << "Pos in Byte: " << pos_in_byte << endl;cout << "total length: " << length << endl;
}
dataReader::dataReader()
{buffer = nullptr;bit_pos = byte_pos = pos_in_byte = length = 0;
}void dataReader::GetData(ifstream& in)
{in.seekg(0, ios::end);length = in.tellg();in.seekg(0, ios::beg);buffer = new unsigned char[length];in.read((char*)buffer, length);
}
void dataReader::GetData( unsigned char* in_buffer, int length )
{ buffer = in_buffer; this->length = length;
}unsigned int dataReader::ReadNextBit()
{if (byte_pos >= length){cout << "Out of boundary." << endl;exit(1);}unsigned int ans = 0;ans = (buffer[byte_pos] & (1 << pos_in_byte)) >> pos_in_byte;bit_pos++; pos_in_byte++;if (pos_in_byte == 8){pos_in_byte = 0;byte_pos++;}return ans;
}
unsigned int dataReader::ReadBits(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i)ans = ans | (ReadNextBit() << i);return ans;
}
unsigned int dataReader::ReadBitsR(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i)ans = (ans << 1) | ReadNextBit();return ans;
}
unsigned int dataReader::ReadBytes(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i){ans = (ans << 8) | ReadBits(8);}return ans;
}
void dataReader::ReadBytes(unsigned char* buffer, int length)
{for (int i = 0; i < length; ++i)buffer[i] = this->buffer[byte_pos++];
}

解码zlib和Defalte格式的文件头

为了方便,将二者合在一起进行处理。
添加如下定义:

struct IDATheader
{unsigned int CM, CINFO, FCHECK, FDICT, FLEVEL, DICTCHECK, CHECKSUM;unsigned int BFINAL, BTYPE;unsigned int LEN, NLEN;unsigned int NLIT, NDIST, NCLEN;void GetZlibHeader(dataReader& data_reader);void GetDeflateHeader(dataReader& data_reader);void Print();
};

进行实现:

void IDATheader::GetZlibHeader(dataReader &data_reader)
{CM = data_reader.ReadBits(4);CINFO = data_reader.ReadBits(4);FCHECK = data_reader.ReadBits(5);FDICT = data_reader.ReadBits(1);FLEVEL = data_reader.ReadBits(2);if (FDICT) DICTCHECK = data_reader.ReadBytes(4);}
void IDATheader::GetDeflateHeader(dataReader& data_reader)
{BFINAL = data_reader.ReadNextBit();BTYPE = data_reader.ReadBits(2);switch (BTYPE){case 0:break;case 1:break;case 2:{NLIT = data_reader.ReadBits(5) + 257;NDIST = data_reader.ReadBits(5) + 1;NCLEN = data_reader.ReadBits(4) + 4;break;}}
}
void IDATheader::Print()
{cout << "CM: " << CM << endl;cout << "CINFO: " << CINFO << endl;cout << "FDICT: " << FDICT << endl;cout << "BIFNAL: " << BFINAL << endl;cout << "BTYPE: " << BTYPE << endl;cout << "NLIt: " << NLIT << endl;cout << "NDIST: " << NDIST << endl;cout << "NCLEN: " << NCLEN << endl;
}

主程序中添加如下代码:

    IDATheader idat_header;position = mp["IDAT"][0];data_reader.GetData(chunks_vector[position].data, chunks_vector[position].length);idat_header.GetZlibHeader(data_reader); idat_header.GetDeflateHeader(data_reader);idat_header.Print();

可以获得IDAT数据块的信息:

构建霍夫曼树

添加声明:

class huffmanTree
{private:int value;huffmanTree* left, * right;
public:huffmanTree() { value = -1; left = right = nullptr; }void Update(int index, unsigned int codes, unsigned int len);void Build(vector< unsigned int > codes, vector<unsigned int> length);int Decode();void Print(string s = "");
};

进行实现:

huffmanTree::huffmanTree() { value = -1; left = right = nullptr; }
void huffmanTree::Update(int index, unsigned int codes, unsigned int len)
{if (len == 0){value = index;left = right = nullptr;return;}if (codes & (1u << (len - 1))){if (nullptr == right)right = new huffmanTree;right->Update(index, codes, len - 1);}else{if (nullptr == left)left = new huffmanTree;left->Update(index, codes, len - 1);}
}
void huffmanTree::Build(vector< unsigned int > codes, vector<unsigned int> length)
{int len = codes.size();for (int i = 0; i < len; ++i) if (length[i]) Update(i, codes[i], length[i]);
}
int huffmanTree::Decode()
{if (value != -1) return value;auto sym = data_reader.ReadNextBit();if (sym) right->Decode();else left->Decode();
}
void huffmanTree::Print(string s)
{if (value != -1){cout << value << "\t" << s << endl;return;}if (nullptr != right)right->Print(s + '1');if (nullptr != left)left->Print(s + '0');
}

获取霍夫曼码表

添加如下函数声明:

vector< unsigned int > GetCodes(vector<unsigned int> length);
vector< unsigned int > DecodeLength(int num);
void GetCodeLengthAphabet(int NCLEN);
void GetOherAlphabet(int NLIT, int NDIST);

定义三个霍夫曼表:

huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;

进行实现:

vector< unsigned int > GetCodes(vector<unsigned int> length)
{int len = length.size();vector< unsigned int > frequent(len), next_code(len), codes(len);for (auto i : length) if (i) frequent[i] ++;for (int i = 1; i < len; ++i) next_code[i] = (next_code[i - 1] + frequent[i - 1]) << 1;for (int i = 0; i < len; ++i) if (length[i]) codes[i] = next_code[length[i]] ++;return codes;
}
vector< unsigned int > DecodeLength(int num)
{vector<unsigned int> length;while (length.size() != num){auto sym = code_len_alphabet.Decode();unsigned int t = 0;if (sym <= 15) length.push_back(sym);else if (sym == 16){t = data_reader.ReadBits(2) + 3;auto back = length.back();while (t--) length.push_back(back);}else if (sym == 17){t = data_reader.ReadBits(3) + 3;while (t--) length.push_back(0);}else{t = data_reader.ReadBits(7) + 11;while (t--) length.push_back(0);}}return length;
}
void GetCodeLengthAphabet( int NCLEN )
{vector< int > index = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };vector< unsigned int > length(19);for (int i = 0; i < NCLEN; ++i)length[index[i]] = data_reader.ReadBits(3);auto codes = GetCodes(length);code_len_alphabet.Build(codes, length);
}
void GetOherAlphabet( int NLIT, int NDIST )
{auto len_length = DecodeLength(NLIT);auto dis_length = DecodeLength(NDIST);auto len_codes = GetCodes(len_length);auto dis_codes = GetCodes(dis_length);len_alphabet.Build(len_codes, len_length);dis_alphabet.Build(dis_codes, dis_length);
}

在主函数中添加如下代码:

    GetCodeLengthAphabet(idat_header.NCLEN);GetOherAlphabet(idat_header.NLIT, idat_header.NDIST);dis_alphabet.Print();

以distance表为例,可以看到编码后的码表:

进行解码

添加声明:

vector< unsigned char > DecodeData();

进行实现:

unsigned int len_ex_bits[] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 };
unsigned int len_base[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,0 };
unsigned int dis_ex_bits[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
unsigned int dis_base[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 };
vector< unsigned char > DecodeData()
{vector< unsigned char > data;while (1){auto sym = len_alphabet.Decode();if (sym == 256)return data;else{if (sym < 256) { data.push_back((unsigned char)sym); continue; }sym -= 257;auto len = len_base[sym] + data_reader.ReadBits(len_ex_bits[sym]);sym = dis_alphabet.Decode();auto dis = dis_base[sym] + data_reader.ReadBits(dis_ex_bits[sym]);for (int i = 0; i < len; ++i){auto t = data[data.size() - dis];data.push_back(t);}}}
}

这是就得到了每个像素对应的调色板索引。

格式转换

添加声明:

void png2yuv(const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte);

进行实现:

void png2yuv( const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte)
{unsigned char* y, * u, * v;int y_size = width * height;int uv_size = y_size / 4;y = new unsigned char[y_size];u = new unsigned char[uv_size];v = new unsigned char[uv_size];int buffer_size = data_buffer.size();int uv_pos = 0, y_pos = 0;for (int i = 0; i < buffer_size; ++i){if ((i + 1) % (width + 1) == 0) continue;int r, g, b; auto plte_tmp = plte[data_buffer[i]];r = plte_tmp.r, g = plte_tmp.g, b = plte_tmp.b;int h = i / (width + 1), w = i % (width + 1);y[y_pos++] = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;if ((h & 1) || (w & 1)) continue;u[uv_pos] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;v[uv_pos++] = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;}ofstream out(out_yuv_path, ios::binary);out.write((char*)y, y_size); out.write((char*)u, uv_size); out.write((char*)v, uv_size);out.close();for (auto i : { &y, &u, &v }) delete[] * i;
}

在主程序中添加:

    auto data = DecodeData(); png2yuv(out_path, ihdr.width, ihdr.height, data, plte);

即可完成解码工作。

实验结果


如图,可见转换时成功的。

完整代码

头文件header.h

#pragma once
#include <iostream>
#include <cstdio>
#include <fstream>
#include <vector>
#include <map>
#include <algorithm>
#include <set>
using namespace std;struct fileHeader
{unsigned char head[8];void GetHead(ifstream& in) { in.read((char*)head, 8); }
};
struct iHDR
{unsigned int width, height;unsigned char bit_depth, color_type, compression_method, filter_method, interlace_method;void GetIHDR(unsigned char* buffer);void Print();
};
struct chunks
{unsigned int length;char type[4];std::string s_type = "";unsigned char* data;unsigned char CRC[4];chunks();void GetChunk(ifstream& in);
};
struct pLTE
{int r, g, b;static pLTE* GetPLTE(unsigned char* buffer, iHDR ihdr);
};
class dataReader
{private:unsigned char* buffer;unsigned int bit_pos, byte_pos, pos_in_byte, length;
public:dataReader();void GetData(ifstream& in);void GetData(unsigned char* in_buffer, int length);unsigned int ReadNextBit();unsigned int ReadBits(int n);unsigned int ReadBitsR(int n);unsigned int ReadBytes(int n);void ReadBytes(unsigned char* buffer, int length);void GetPos();
};
struct IDATheader
{unsigned int CM, CINFO, FCHECK, FDICT, FLEVEL, DICTCHECK, CHECKSUM;unsigned int BFINAL, BTYPE;unsigned int LEN, NLEN;unsigned int NLIT, NDIST, NCLEN;void GetZlibHeader(dataReader& data_reader);void GetDeflateHeader(dataReader& data_reader);void Print();
};class huffmanTree
{private:int value;huffmanTree* left, * right;
public:huffmanTree();void Update(int index, unsigned int codes, unsigned int len);void Build(vector< unsigned int > codes, vector<unsigned int> length);int Decode();void Print(string s = "");
};vector< unsigned int > GetCodes(vector<unsigned int> length);
vector< unsigned int > DecodeLength(int num);
void GetCodeLengthAphabet(int NCLEN);
void GetOherAlphabet(int NLIT, int NDIST);vector< unsigned char > DecodeData();void png2yuv(const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte);

data_reader.cpp

#include "header.h"void dataReader::GetPos()
{cout << "Byte pos: " << byte_pos << endl;cout << "Bit pos: " << bit_pos << endl;cout << "Pos in Byte: " << pos_in_byte << endl;cout << "total length: " << length << endl;
}
dataReader::dataReader()
{buffer = nullptr;bit_pos = byte_pos = pos_in_byte = length = 0;
}void dataReader::GetData(ifstream& in)
{in.seekg(0, ios::end);length = in.tellg();in.seekg(0, ios::beg);buffer = new unsigned char[length];in.read((char*)buffer, length);
}
void dataReader::GetData( unsigned char* in_buffer, int length )
{ buffer = in_buffer; this->length = length;
}unsigned int dataReader::ReadNextBit()
{if (byte_pos >= length){cout << "Out of boundary." << endl;exit(1);}unsigned int ans = 0;ans = (buffer[byte_pos] & (1 << pos_in_byte)) >> pos_in_byte;bit_pos++; pos_in_byte++;if (pos_in_byte == 8){pos_in_byte = 0;byte_pos++;}return ans;
}
unsigned int dataReader::ReadBits(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i)ans = ans | (ReadNextBit() << i);return ans;
}
unsigned int dataReader::ReadBitsR(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i)ans = (ans << 1) | ReadNextBit();return ans;
}
unsigned int dataReader::ReadBytes(int n)
{unsigned int ans = 0;for (int i = 0; i < n; ++i){ans = (ans << 8) | ReadBits(8);}return ans;
}
void dataReader::ReadBytes(unsigned char* buffer, int length)
{for (int i = 0; i < length; ++i)buffer[i] = this->buffer[byte_pos++];
}

png_tools.cpp

#include "header.h"void iHDR::Print()
{cout << "width: " << (int)width << " height: " << height << endl;cout << "bit depth: " << (int)bit_depth << endl;cout << "color type: " << (int)color_type << endl;cout << "compression method: " << (int)compression_method << endl;cout << "interlace method : " << (int)interlace_method << endl;
}
void iHDR::GetIHDR(unsigned char* buffer)
{for (int i = 0; i < 4; ++i) { width = (width << 8) + buffer[i]; }for (int i = 0; i < 4; ++i) { height = (height << 8) + buffer[i + 4]; }int pos = 8;for (auto i : { &bit_depth, &color_type, &compression_method, &filter_method, &interlace_method })*i = buffer[pos++];
}
chunks::chunks()
{length = 0;memset(type, 0, sizeof(type));data = nullptr;s_type = "";memset(CRC, 0, sizeof(CRC));
}
void chunks::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;
}
pLTE* pLTE::GetPLTE(unsigned char* buffer, iHDR ihdr)
{int plte_size = 1 << ihdr.bit_depth;pLTE* plte = new pLTE[plte_size];int pos = 0;for (int i = 0; i < plte_size; ++i){for (auto j : { &plte[i].r, &plte[i].g, &plte[i].b })*j = buffer[pos++];}return plte;
}void png2yuv( const string out_yuv_path, int width, int height, vector<unsigned char> data_buffer, pLTE* plte)
{unsigned char* y, * u, * v;int y_size = width * height;int uv_size = y_size / 4;y = new unsigned char[y_size];u = new unsigned char[uv_size];v = new unsigned char[uv_size];int buffer_size = data_buffer.size();int uv_pos = 0, y_pos = 0;for (int i = 0; i < buffer_size; ++i){if ((i + 1) % (width + 1) == 0) continue;int r, g, b; auto plte_tmp = plte[data_buffer[i]];r = plte_tmp.r, g = plte_tmp.g, b = plte_tmp.b;int h = i / (width + 1), w = i % (width + 1);y[y_pos++] = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;if ((h & 1) || (w & 1)) continue;u[uv_pos] = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;v[uv_pos++] = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;}ofstream out(out_yuv_path, ios::binary);out.write((char*)y, y_size); out.write((char*)u, uv_size); out.write((char*)v, uv_size);out.close();for (auto i : { &y, &u, &v }) delete[] * i;
}

IDAT_tools.cpp

#include "header.h"
extern dataReader data_reader;
huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;
void IDATheader::GetZlibHeader(dataReader &data_reader)
{CM = data_reader.ReadBits(4);CINFO = data_reader.ReadBits(4);FCHECK = data_reader.ReadBits(5);FDICT = data_reader.ReadBits(1);FLEVEL = data_reader.ReadBits(2);if (FDICT) DICTCHECK = data_reader.ReadBytes(4);}
void IDATheader::GetDeflateHeader(dataReader& data_reader)
{BFINAL = data_reader.ReadNextBit();BTYPE = data_reader.ReadBits(2);switch (BTYPE){case 0:break;case 1:break;case 2:{NLIT = data_reader.ReadBits(5) + 257;NDIST = data_reader.ReadBits(5) + 1;NCLEN = data_reader.ReadBits(4) + 4;break;}}
}
void IDATheader::Print()
{cout << "CM: " << CM << endl;cout << "CINFO: " << CINFO << endl;cout << "FDICT: " << FDICT << endl;cout << "BIFNAL: " << BFINAL << endl;cout << "BTYPE: " << BTYPE << endl;cout << "NLIt: " << NLIT << endl;cout << "NDIST: " << NDIST << endl;cout << "NCLEN: " << NCLEN << endl;
}huffmanTree::huffmanTree() { value = -1; left = right = nullptr; }
void huffmanTree::Update(int index, unsigned int codes, unsigned int len)
{if (len == 0){value = index;left = right = nullptr;return;}if (codes & (1u << (len - 1))){if (nullptr == right)right = new huffmanTree;right->Update(index, codes, len - 1);}else{if (nullptr == left)left = new huffmanTree;left->Update(index, codes, len - 1);}
}
void huffmanTree::Build(vector< unsigned int > codes, vector<unsigned int> length)
{int len = codes.size();for (int i = 0; i < len; ++i) if (length[i]) Update(i, codes[i], length[i]);
}
int huffmanTree::Decode()
{if (value != -1) return value;auto sym = data_reader.ReadNextBit();if (sym) right->Decode();else left->Decode();
}
void huffmanTree::Print(string s)
{if (value != -1){cout << value << "\t" << s << endl;return;}if (nullptr != right)right->Print(s + '1');if (nullptr != left)left->Print(s + '0');
}vector< unsigned int > GetCodes(vector<unsigned int> length)
{int len = length.size();vector< unsigned int > frequent(len), next_code(len), codes(len);for (auto i : length) if (i) frequent[i] ++;for (int i = 1; i < len; ++i) next_code[i] = (next_code[i - 1] + frequent[i - 1]) << 1;for (int i = 0; i < len; ++i) if (length[i]) codes[i] = next_code[length[i]] ++;return codes;
}
vector< unsigned int > DecodeLength(int num)
{vector<unsigned int> length;while (length.size() != num){auto sym = code_len_alphabet.Decode();unsigned int t = 0;if (sym <= 15) length.push_back(sym);else if (sym == 16){t = data_reader.ReadBits(2) + 3;auto back = length.back();while (t--) length.push_back(back);}else if (sym == 17){t = data_reader.ReadBits(3) + 3;while (t--) length.push_back(0);}else{t = data_reader.ReadBits(7) + 11;while (t--) length.push_back(0);}}return length;
}
void GetCodeLengthAphabet( int NCLEN )
{vector< int > index = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };vector< unsigned int > length(19);for (int i = 0; i < NCLEN; ++i)length[index[i]] = data_reader.ReadBits(3);auto codes = GetCodes(length);code_len_alphabet.Build(codes, length);
}
void GetOherAlphabet( int NLIT, int NDIST )
{auto len_length = DecodeLength(NLIT);auto dis_length = DecodeLength(NDIST);auto len_codes = GetCodes(len_length);auto dis_codes = GetCodes(dis_length);len_alphabet.Build(len_codes, len_length);dis_alphabet.Build(dis_codes, dis_length);
}unsigned int len_ex_bits[] = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 };
unsigned int len_base[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,0 };
unsigned int dis_ex_bits[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };
unsigned int dis_base[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 };
vector< unsigned char > DecodeData()
{vector< unsigned char > data;while (1){auto sym = len_alphabet.Decode();if (sym == 256)return data;else{if (sym < 256) { data.push_back((unsigned char)sym); continue; }sym -= 257;auto len = len_base[sym] + data_reader.ReadBits(len_ex_bits[sym]);sym = dis_alphabet.Decode();auto dis = dis_base[sym] + data_reader.ReadBits(dis_ex_bits[sym]);for (int i = 0; i < len; ++i){auto t = data[data.size() - dis];data.push_back(t);}}}
}

main.cpp

#include "header.h"const string in_path = "1.png";
const string out_path = "out.yuv";
map< string, vector<int> > mp;
vector<chunks> chunks_vector;
dataReader data_reader;
extern huffmanTree code_len_alphabet, len_alphabet, dis_alphabet;int main()
{ifstream in(in_path, ios::binary);if (!in.is_open()){cout << in_path << " open failed." << endl;exit(1);}fileHeader file_header;file_header.GetHead(in);int pos = 0;while (true){chunks tmp;tmp.GetChunk(in);chunks_vector.push_back(tmp);mp[tmp.s_type].push_back(pos++);if (tmp.s_type == "IEND") break;}for (auto& i : mp){cout << i.first << ": ";for (auto& j : i.second)cout << j << " ";cout << endl;}int position = mp["IHDR"][0];iHDR ihdr; ihdr.GetIHDR(chunks_vector[position].data);cout << endl;  ihdr.Print();position = mp["PLTE"][0];pLTE* plte = pLTE::GetPLTE(chunks_vector[position].data, ihdr);IDATheader idat_header;position = mp["IDAT"][0];data_reader.GetData(chunks_vector[position].data, chunks_vector[position].length);idat_header.GetZlibHeader(data_reader); idat_header.GetDeflateHeader(data_reader);idat_header.Print();GetCodeLengthAphabet(idat_header.NCLEN);GetOherAlphabet(idat_header.NLIT, idat_header.NDIST);dis_alphabet.Print();auto data = DecodeData(); png2yuv(out_path, ihdr.width, ihdr.height, data, plte);
}

PNG转YUV(自造轮子版)相关推荐

  1. 不是“重复”造轮子,百度飞桨框架2.0如何俘获人心

    2016 年,百度 PaddlePaddle 打响了国产深度学习框架开源的第一枪. 2019 年 4 月,在 Wave Summit 深度学习开发者峰会上,首次发布了PaddlePaddle 的中文名 ...

  2. 51年被发现9次,陶哲轩证明的公式成了重复造轮子?事情并没有这么简单

    晓查 栗子 发自 凹非寺  量子位 报道 | 公众号 QbitAI 在科学探索的过程中,"重复造轮子"从来就不新鲜. 最知名如牛顿和莱布尼茨,各自独立发明了微积分:而计算机领域,也 ...

  3. 造轮子是什么意思_程序员发文质疑阿里天启为kpi项目,重复造轮子,阿里回应:诽谤...

    前言: 曹丕在<典论·论文>中说到,"文人相轻,自古而然".中国自古就有文人相轻的传统,文人相轻真没有什么要紧的,就像是小夫妻似的斗斗气拌拌嘴,然后还可以理直气壮地拿稿 ...

  4. 动手造轮子:写一个日志框架

    动手造轮子:写一个日志框架 Intro 日志框架有很多,比如 log4net / nlog / serilog / microsoft.extensions.logging 等,如何在切换日志框架的时 ...

  5. 动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化

    动手造轮子:实现一个简单的依赖注入(二) --- 服务注册优化 Intro 之前实现的那版依赖注入框架基本可用,但是感觉还是不够灵活,而且注册服务和解析服务在同一个地方感觉有点别扭,有点职责分离不够. ...

  6. 动手造轮子:实现一个简单的依赖注入(一)

    动手造轮子:实现一个简单的依赖注入(一) Intro 在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验,这篇文章要开始写代码了,开始实现自己的依赖注入框架. 类图 首先来温习一下上次 ...

  7. 造轮子是什么意思_程序员为什么热衷于造轮子,升职加薪吗?

    作者:小傅哥 博客: https://bugstack.cn- 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 哪个架构师没造过轮子? 你想过这样一件事吗? 是先具备能力在安排职位,还是先安排 ...

  8. 精读《怎么用 React Hooks 造轮子》

    1 引言 上周的 精读<React Hooks> 已经实现了对 React Hooks 的基本认知,也许你也看了 React Hooks 基本实现剖析(就是数组),但理解实现原理就可以用好 ...

  9. 微软 CEO 萨提亚·纳德拉:不要重复造轮子,提升技术强密度

    扫描二维码,查看精彩回顾 据中国互联网络信息中心(CNNIC)发布的第 44 次<中国互联网络发展状况统计报告>显示,截至 2019 年 6 月,我国网民规模达 8.54 亿,较 2018 ...

最新文章

  1. python 数组中第k个最大元素
  2. Python:基础操作(2)
  3. C和指针之多维数组一行存满后会轮到下一行
  4. 一起玩转CoordinatorLayout
  5. python-字符串方法
  6. 计算广告 pdf_他创业20年死磕PDF一项业务,如今上市身家76亿:所有的突然牛逼,背后都是玩命死磕...
  7. [ES6] 细化ES6之 -- async函数
  8. 链路层到网络层的数据传递
  9. 叫板苹果谷歌,微软将开发者应用分成上调至 95%
  10. matlab怎么看输出电压纹波,教你如何正确取电源的输出纹波噪声值
  11. 三星emcp型号详解_三星emcp型号详解_eMCP终将成为过去式?解析:三星、美光所推出的uMCP为何物?......
  12. 毕业设计 基于单片机的智能蓝牙密码锁设计与实现
  13. vue后台管理系统计算入职天数
  14. feign调用是否会经过网关
  15. 那个“天使”阿桑奇又回来了
  16. J-LINK放了一晚,不能识别,灯一直闪
  17. 如何利用注册表修改开机启动程序并提高电脑开机速度!
  18. 如何去除PDF中的水印,PDF去水印方法
  19. flask 智能图灵机器人
  20. int、long、long long、unsigned int、_int64的取值范围(与不同位数的编译器有关)

热门文章

  1. 苹果变了:Mac 用自研芯片、iPhone 替代车钥匙
  2. 给ubuntu18.04系统内核升级rt-patch补丁
  3. 信任、公平、梦想—新拍拍,新起点-拍拍网蒉莺春
  4. 凡客副总裁被曝离职:或因IPO受阻|凡客|王春焕|离职_互联网_新浪科技_新浪网...
  5. C++ 二叉搜索树(补充)
  6. cad2014打开出现显示驱动程序缺少或损坏
  7. html表格导入wps,怎么快速将网页数据导入WPS表格中?
  8. Android studio工具小技巧|文件日期|市场占有率|输出日志|adb安装apk
  9. Win8换徽标亮点功能全面整合大阅兵
  10. win10和Ubuntu16.04中基于anaconda安装TensorFlow(CPU+GPU)