zoukankan      html  css  js  c++  java
  • 哈夫曼图片压缩及解压

    哈夫曼图片压缩及解压

    文件 功能
    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] = { '' };//记录访问路径
    	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] = '';
    				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]	%d	%d	%d	%d
    ", 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
    ", 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 << "	"<<pHT[root].lchild<<"	"<<pHT[root].rchild<<"	"<<pHT[root].parent<<"
    ";
    	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
    ", 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 << "
    压缩完毕" << endl;
    	//for (int i = 1; i < nSize; i++)
    	//{
    	//	printf("%X
    ", 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 
    头文件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
    ", pBuffer[i]);
    	//}
    	char ch[SIZE] = { 0 };
    	unsigned char c;
    	for (int i = 0; i < nSize; i++)
    	{
    //		printf("%x
    ", 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] != '')
    			{
    //				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;
    }
    
    
  • 相关阅读:
    一个小时学会MySQL数据库
    4种解决json日期格式问题的办法
    一个小时学会Git
    docker 常用操作
    Fine-Grained Image (细粒度图像) – Papers, Codes and Datasets
    Pytorch在colab和kaggle中使用TensorBoard/TensorboardX可视化
    训练集,验证集,测试集比例
    深度学习模型评估指标
    注意力机制(Attention Mechanism)应用——自然语言处理(NLP)
    自注意力机制(Self-attention Mechanism)——自然语言处理(NLP)
  • 原文地址:https://www.cnblogs.com/sgawscd/p/10846284.html
Copyright © 2011-2022 走看看