哈夫曼图片压缩及解压

哈夫曼图片压缩及解压

文件 功能
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<iostream>
#include<cstring>
#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"<<pHT[root].lchild<<"\t"<<pHT[root].rchild<<"\t"<<pHT[root].parent<<"\n";
	if (pHT[root].lchild != 0)
	{
		TestHufTreeN(pHT[root].lchild, pHT);
	}
	if (pHT[root].rchild != 0)
	{
		TestHufTreeN(pHT[root].rchild, pHT);
	}
}
//Compress.cpp
#include"huffman.h"
#include"Compress.h"
#include<iostream>
#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 = "<<nSize << endl << endl;

	//对原文件进行压缩编码
	unsigned char* pBuffer = NULL;
	pBuffer = (unsigned char *)malloc(nSize*sizeof(unsigned char));
	memset(pBuffer, 0, (nSize) * sizeof(unsigned char));
//	cout << "begin: " << strlen(pBuffer) << endl;
////	cout << "----";
//	int n;
//	cout << "input n:";
//	cin >> 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"<<endl;
	cout << "压缩比率:" << (double)len_after * 100 / sHead.length << "%" << endl;
	free(hfmt);
	free(hfmc);
	free(pBuffer);
	return OK;
}


int Encode(const char*pFilename, const HuffmanCode pHC,buffer &pBuffer, const int nSize)
{
	//开辟缓冲区
//	cout << "+++++";
	FILE* in = fopen(pFilename, "rb");
	if (in == NULL)
	{
		cout << "Failed to open the file!" << endl;
		exit(0);
	}
	pBuffer = (unsigned char*)malloc(nSize * sizeof(unsigned char));
	if (!pBuffer)
	{
		cerr << "开辟缓冲区失败" << endl;
		return -1;
	}
	cout << "loading";
	int sign = 0;//用于控制小数点输出
	char cd[SIZE] = { 0 };//工作区
	int pos = 0;//缓冲区指针
	int ch;
	//扫描文件,根据huffmman编码表对其进行压缩,压缩结果暂存到缓冲区中
	while ((ch = getc(in)) != EOF)
	{
		if (sign % 1000 == 1)
			printf(".");
		sign++;
		strcat(cd, pHC[ch+1]);//从HC复制编码串到cd


        //打印压缩后的文件编码
//		printf("%s", pHC[ch + 1]);


		//压缩编码
		while (strlen(cd) >= 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<iostream>
#include<cstdlib>
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;
}

posted @ 2019-05-10 19:16  shadowgully  阅读(3711)  评论(0编辑  收藏  举报