哈夫曼图片压缩及解压
Huffman |
哈夫曼编码 |
compress |
解压 |
//Compress.h
#ifndef COMPRESS_H
#define COMPRESS_H
typedef unsigned char * buffer;
int Compress(const char *pFilename);
unsigned char Str2byte(const char *pBinStr);
int Encode(const char*pFilename, const HuffmanCode pHC,buffer &pBuffer, const int nSize);
struct HEAD
{
char type[4];//文件类型
int length;//原文件长度
int weight[256];//权值数值
};
int WriteFile(const char*pFilename, const HEAD sHead, unsigned char * pBuffer, const int nSize);
int InitHead(const char *pFilename, HEAD &sHead);
int UnCompress(const char*pFilename);
#endif
//Huffman.h
#ifndef HUFFMAN_H
#define HUFFMAN_H
#define OK 1
#define SIZE 256
struct HTNode {
int weight;//权值
int parent;//父节点
int lchild;//左孩子
int rchild;//右孩子
};
typedef HTNode *HuffmanTree;//动态分配数组存储Huffman树
typedef char **HuffmanCode;//动态分配哈夫曼编码表
//void PreorderTraverse(int root, HuffmanTree pHT);
int HuffmanCoding(HuffmanCode &pHC, HuffmanTree &pHT);
int Select(HuffmanTree pHT, int nSize);
void TestHufTree(HuffmanTree pHT);
void TestHufCode(int root, HuffmanTree pHT, HuffmanCode pHC);
void TestHufTreeN(int root, HuffmanTree pHT);
int HfmTree(HuffmanTree &pHT, int *w, int n);
#endif
//huffman.cpp
#include
#include
#include"huffman.h"
#pragma warning( disable : 4996)
using namespace std;
/*
void PreorderTraverse(int root, HuffmanTree pHT)
{
cout << pHT[root].weight << " ";//访问节点
if (pHT[root].lchild)//左孩子
{
PreorderTraverse(pHT[root].lchild, pHT);
}
if (pHT[root].rchild)//右孩子
{
PreorderTraverse(pHT[root].rchild, pHT);
}
}
*/
int HuffmanCoding(HuffmanCode &pHC, HuffmanTree &pHT)
{
pHC = (HuffmanCode)malloc((SIZE + 1) * sizeof(char*));
//无栈非递归遍历
char cd[SIZE] = { '\0' };//记录访问路径
int cdlen = 0;//记录当前路径长度
for (int i = 1; i < 512; i++)
{
pHT[i].weight = 0;//遍历 Huffman树时用作节点的状态标志
}
int p = 2*SIZE-1;//根节点
while (p != 0)
{
if (pHT[p].weight == 0)//向左
{
pHT[p].weight = 1;
if (pHT[p].lchild != 0)
{
p = pHT[p].lchild;
cd[cdlen++] = '0';
}
else if (pHT[p].rchild == 0)//登记叶子节点的字符编码
{
pHC[p] = (char*)malloc((cdlen+1) * sizeof(char));
cd[cdlen] = '\0';
strcpy(pHC[p], cd);//复制编码
}
}
else if (pHT[p].weight == 1)//向右
{
pHT[p].weight = 2;
if (pHT[p].rchild != 0)//右孩子为叶子节点
{
p = pHT[p].rchild;
cd[cdlen++] = '1';
}
}
else
{
//退回父节点,编码长度减1
pHT[p].weight = 0;
p = pHT[p].parent;
--cdlen;
}
// printf("*");
}
return OK;
}
int Select(HuffmanTree pHT, int nSize)
{
int minValue = 0x7FFFFFFF;//最小值
int min = 0;
//找到最小权值的元素序号
for (int i = 1; i <= nSize; i++)
{
if (pHT[i].parent == 0 && pHT[i].weight < minValue)
{
minValue = pHT[i].weight;
min = i;
}
}
return min;
}
void TestHufTree(HuffmanTree pHT)
{
for (int i = 1; i < 2*SIZE; i++)
{
printf("pHT[%d]\t%d\t%d\t%d\t%d\n", i, pHT[i].weight, pHT[i].parent,pHT[i].lchild,pHT[i].rchild);
}
}
int HfmTree(HuffmanTree &pHT, int *w, int n)
{
int m = 2 * n - 1;
pHT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));
if (!pHT)
{
cerr << "内存分配失败! " << endl;
return -1;
}
//初始化树
HuffmanTree p = pHT + 1;//0号单元不使用
for (int i = 0; i < m; i++)
{
p->weight = (i < n) ? w[i] : 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
p++;
}
for (int i = n + 1; i <= m; i++)
{
//第一个最小元素
int s1 = Select(pHT, i - 1);//找出前i-1个中最小元素
pHT[s1].parent = i;
//第二个最小元素
int s2 = Select(pHT, i - 1);
pHT[s2].parent = i;
pHT[i].weight = pHT[s1].weight + pHT[s2].weight;
pHT[i].lchild = s1;
pHT[i].rchild = s2;
}
return 0;
}
void TestHufCode(int root, HuffmanTree pHT, HuffmanCode pHC)
{
if (pHT[root].lchild == 0 && pHT[root].rchild == 0)
{
printf("0x%02X %s\n", root - 1, pHC[root]);
}
if (pHT[root].lchild)//访问左孩子
{
TestHufCode(pHT[root].lchild, pHT, pHC);
}
if (pHT[root].rchild)
{
TestHufCode(pHT[root].rchild, pHT, pHC);
}
}
void TestHufTreeN(int root, HuffmanTree pHT)
{
cout << pHT[root].weight << "\t"<
//Compress.cpp
#include"huffman.h"
#include"Compress.h"
#include
#pragma warning( disable : 4996)
using namespace std;
//Compress
//InitHead
//Encode
//Str2byte
//WriteFile
unsigned char Str2byte(const char *pBinStr)
{
unsigned char b = 0x00;
for (int i = 0; i < 8; i++)
{
b = b << 1;
if (pBinStr[i] == '1')
{
b = b | 0x01;
}
}
return b;
}
int Compress(const char *pFilename)
{
int weight[256] = { 0 };
//以二进制打开文件
FILE* in = fopen(pFilename, "rb");
if (in == NULL)
{
cout << "Failed to open the file!" << endl;
exit(0);
}
cout << "成功打开文件 " << pFilename << endl;
int ch;
while ((ch = getc(in)) != EOF)
{
weight[ch]++;
}
fclose(in);
//cout << "Byte Weight" << endl;
//for (int i = 0; i < SIZE; i++)
//{
// printf("0x%02X %d\n", i, weight[i]);
//}
HuffmanTree hfmt;
HfmTree(hfmt, weight, SIZE);
cout << "成功生成哈夫曼树" << endl;
// TestHufTree(hfmt);
// TestHufTreeN(511, hfmt);
HuffmanCode hfmc=(HuffmanCode)malloc((SIZE+1)*sizeof(char*));
// for (int i = 1; i <= SIZE; i++)
// hfmt[i].weight = weight[i - 1]
//根据哈夫曼树进行编码
HuffmanCoding(hfmc, hfmt);
cout << "成功完成哈夫曼编码" << endl;
// cout << "先序遍历哈夫曼树输出编码信息:" << endl;
// TestHufCode(2 * SIZE - 1, hfmt, hfmc);//测试哈夫曼编码
// cout << "压缩后的文件编码:" << endl;
//计算编码缓冲区大小
int nSize = 0;
for (int i = 0; i < 256; i++)
{
nSize += weight[i] * strlen(hfmc[i+1]);
}
nSize = (nSize % 8) ? nSize / 8 + 1 : nSize / 8;
// cout <<"nSize = "<> n;
//将编码写入缓冲区
Encode(pFilename, hfmc, pBuffer, nSize);
// cout << "after: " << strlen(pBuffer) << endl;
// cout << "len of puf = " << strlen(pBuffer) << endl;
// cout << "!pBuffer = " << !pBuffer << endl;
if (!pBuffer)
{
cout << "!pBuffer = " << !pBuffer << endl;
return -1;
}
cout << "\n压缩完毕" << endl;
//for (int i = 1; i < nSize; i++)
//{
// printf("%X\n", pBuffer[i]);
//}
HEAD sHead;
InitHead(pFilename, sHead);
cout <<"原文件"<< pFilename<<"大小为:" << sHead.length << "Byte" << endl;
int len_after = WriteFile(pFilename, sHead, pBuffer, nSize);
cout << "大小为:" << len_after << "Byte \n头文件sHead大小为:" << sizeof(sHead)<<"Byte"<= 8)
{
//截取字符串左边的8个字符,编码成字节
pBuffer[pos++] = Str2byte(cd);
//字符串整体左移8个字节
for (int i = 0; i < SIZE - 8; i++)
{
cd[i] = cd[i + 8];
}
}
}
if (strlen(cd) > 0)
{
pBuffer[pos++] = Str2byte(cd);
}
fclose(in);
// printf("%", pBuffer);
return OK;
}
int InitHead(const char *pFilename, HEAD &sHead)
{
//初始化文件头
strcpy(sHead.type, "HUF");//文件类型
sHead.length = 0;//原文件长度
for (int i = 0; i < SIZE; i++)
{
sHead.weight[i] = 0;
}
FILE *in = fopen(pFilename, "rb");
int ch;
while ((ch = fgetc(in)) != EOF)
{
sHead.weight[ch]++;
sHead.length++;
}
fclose(in);
in = NULL;
return OK;
}
int WriteFile(const char*pFilename, const HEAD sHead, unsigned char * pBuffer, const int nSize)
{
//生成文件名
char filename[256] = { 0 };
strcpy(filename, pFilename);
strcat(filename, ".huf");
//以二进制流形式打开文件
FILE *out = fopen(filename, "wb");
//写文件头
fwrite(&sHead, sizeof(sHead), 1, out);
//写压缩后的编码
fwrite(pBuffer, sizeof(char), nSize, out);
//关闭文件,释放文件指针
fclose(out);
out = NULL;
cout << "生成压缩文件:" << filename << endl;
int len = sizeof(HEAD) + strlen(pFilename) + 1 + nSize;
return len;
}
//解压函数
int UnCompress(const char*pFilename)
{
char outputfile[255];
FILE *ifp, *ofp;
ifp = fopen(pFilename, "rb");//打开.huf文件
if (!ifp)
{
cerr << "Failed to open this file!" << endl;
return -1;
}
cout << "输入解压后的文件名,(原文件名为" << pFilename << "): " << endl;
cin >> outputfile;
// strcpy(outputfile, "d:\\aaa.png");
ofp = fopen(outputfile, "wb");
if (!ofp)
{
cerr << "Failed to open this file!" << endl;
return -1;
}
HEAD sHead;
//读文件头
fread(&sHead, sizeof(HEAD), 1, ifp);
HuffmanTree hfmt = NULL;
HfmTree(hfmt, sHead.weight, 256);
HuffmanCode hfmc = NULL;
HuffmanCoding(hfmc, hfmt);
int max = 0;
for (int k = 1; k <= 256; k++) {
if (strlen(hfmc[k]) > max) {
max = strlen(hfmc[k]);
}//找出最长的编码
}
//
int nSize = 0;
for (int i = 0; i < 256; i++)
{
nSize += sHead.weight[i] * strlen(hfmc[i + 1]);
}
nSize = (nSize % 8) ? nSize / 8 + 1 : nSize / 8;
cout << "nsize = " << nSize << endl;
// unsigned char *pBuffer = NULL;
unsigned char* pBuffer = NULL;
pBuffer = (unsigned char *)malloc(nSize * sizeof(unsigned char));
memset(pBuffer, 0, (nSize) * sizeof(unsigned char));
// pBuffer = (unsigned char*)malloc(nSize*sizeof(unsigned char));
fread(pBuffer, sizeof(unsigned char), nSize, ifp);
//for (int i = 0; i < nSize; i++)
//{
// printf("%x\n", pBuffer[i]);
//}
char ch[SIZE] = { 0 };
unsigned char c;
for (int i = 0; i < nSize; i++)
{
// printf("%x\n", pBuffer[i]);
char s[SIZE] = { 0 };
char buf[SIZE] = { 0 };
_itoa(pBuffer[i], buf, 2);
for (int k = 8; k > strlen(buf); k--)
{
strcat(s, "0");
}
strcat(s, buf);
strcat(ch, s);
// cout << "ch = " << ch << endl;
int m = 2 * SIZE - 1;
while (strlen(ch) >= max)
{
// cout << "**";
int j = 0;
while (ch[j] != '\0')
{
// cout << "....";
int t = m;
while (hfmt[t].lchild != 0 && hfmt[t].rchild != 0)
{
if (ch[j] == '0')
{
t = hfmt[t].lchild;
}
else
{
t = hfmt[t].rchild;
}
j++;
}
c = (unsigned char)(t - 1);
// cout << (t - 1) << endl;
fwrite(&c, 1, 1, ofp);
for (int b = 0; b < SIZE - j; b++)
{
ch[b] = ch[b + j];
}
break;
}
// cout << "::::";
}
}
cout << "解压成功" << endl;
fclose(ifp);
fclose(ofp);
return OK;
}
//main.cpp
#include"huffman.h"
#include"Compress.h"
#include
#include
using namespace std;
#pragma warning( disable : 4996)
int main()
{
cout << "= = = = = = = =Huffman 文件压缩= = = = = = = =" << endl;
cout << "**1.压缩文件" << endl;
cout << "**2.解压文件" << endl;
cout << "= = = = = = = = = = = = = = = = = = = = = = =" << endl;
int choose = 0;
cout << "----选择功能:";
cin >> choose;
while (choose == 1 || choose == 2)
{
if (choose == 1)
{
cout << "请输入文件名:";
char filename[256];
cin >> filename;
Compress(filename);
}
else if (choose == 2)
{
cout << "输入你要解压的文件名(.huf后缀): ";
char newfilename[SIZE] = { 0 };
cin >> newfilename;
// strcpy(newfilename, "d:\\t.png.huf");
UnCompress(newfilename);
}
else
break;
cout << "----选择功能:";
cin >> choose;
}
// system("pause");
return 0;
}