数据结构-Huffman压缩图片-Huffman编码

Huffman压缩图片:

github项目地址

实现结果输出:
运行结果

  • 实现方式见注释

HuffmanCode.cpp

// HuffmanCode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
/*
创建工程。
2、读取源文件。
3、生成哈夫曼树。
4、生成哈夫曼编码。
5、压缩原文件。
6、保存压缩文件。
7、扩展功能。
*/
#include <iostream>
#include<string>
#include"Compress.hpp"
using namespace std;
int main(){
    int sign = 0;
    Compress *compress=new Compress();
    cout << "-----------------------哈夫曼图片压缩实现-----------------------"<<endl;
    cout << "输入文件名(路径):" << endl;
    char path[100] = { "C:\\Users\\HUANGYAOHUI\\Desktop\\Pic.bmp" };
    cout << path << endl;
    //cin >> path;
    compress->CompressFile(path);
    return 0;
}

** Compress.hpp**

#pragma once
#include"HuffmanTree.hpp"
#include<stdlib.h>
#include<iostream>
struct  HEAD{
	char type[4];	//文件类型
	int length;		//源文件长度
	int wigth[256];	//权值数组
};
class Compress {
public:
	Compress();
	void CompressFile(char filename[]);		//压缩函数
	void SaveFile(char filename[]);
private:
	void readFileNum(char filename[]);			//读取统计字符数
	void InitHead();
private:
	HuffmanTree hfmTree;
	int wigth[256];		//256个字符的权值
	int n;
	bool isOK;
	char* temp;	//压缩结果
	int NumberOld ;
	int NumberNow;
	HEAD head;
};
void Compress::SaveFile(char filename[]) {
	this->InitHead();
	char path[256] = { 0 };
	strcpy(path, filename);
	strcat(path,".huf");
	//打开文件
	FILE* out = fopen(path, "wb");
	fwrite(&head, sizeof(HEAD), 1, out);
	fwrite(temp, sizeof(char),this->NumberNow, out);
	fclose(out);

	cout << "生成压缩文件:" << path << endl;
	cout << "压缩比:" << 100*double(this->NumberNow + sizeof(HEAD) + strlen(path) + 1) / this->NumberOld <<" %"<< endl;
}
//初始化文件头
void Compress::InitHead() {
	strcat(head.type, "HUF");
	head.length = this->NumberOld;
	for(int i=0;i<this->n;i++)
		head.wigth[i] = this->wigth[i];
}
//构造函数
Compress::Compress() {
	memset(this->wigth,0,sizeof(wigth));
	this->isOK = false;
	this->n = 256;
	this->NumberOld = 0;
	this->NumberNow = 0;
}
//压缩函数
void Compress::CompressFile(char filename[]) {
	//读取数据
	this->readFileNum(filename);
	//源文件大小
	cout << "原文件大小:" << this->NumberOld << endl;

	//构建哈夫曼树
	this->hfmTree.createHFM(this->wigth, this->n);
	
	//生成编码表
	this->hfmTree.createCode();

	//获取大小
	this->NumberNow = this->hfmTree.getSize(this->wigth,this->n);
	
	//压缩数据
	this->temp=this->hfmTree.Encode(filename,this->NumberNow);
	
	//保存
	cout << "输入保存路径:" << endl;
	//cin >> path;
	char path[100] = { "C:\\Users\\HUANGYAOHUI\\Desktop\\Pic.bmp" };
	this->SaveFile(path);
	cout << "压缩成功" << endl;
}
//读取统计字符数
void Compress::readFileNum(char filename[]) {
	//以二进制流的方式打开文件
	int ch;
	FILE* in = fopen(filename, "rb");
	if (in == NULL){
		cout << "打开文件失败" << endl;
	}
	//扫描文件,获得权重
	while ((ch = getc(in) )!= EOF){
		wigth[ch]++;
		this->NumberOld++;
	}
	//关闭文件
	fclose(in);
	/*
	for (int i = 0; i < 256; i++)
		cout <<i<<"  :  "<< wigth[i] << endl;
	*/
}

HuffmanTree.hpp

#pragma once
#include<iostream>
#include<cstdio>
#define MAX 1000
#define SIZE 256
using namespace std;
//哈夫曼树节点表
struct HFNode {
	int wight;	//权值
	int parent;	//父节点
	int Lchild;	//左孩子
	int Rchild;	//右孩子
};
//哈夫曼树
class HuffmanTree {
public:
	void createHFM(int wight[],int n);		//构建哈夫曼树
	void createCode();						//生成编码表
	int getSize(int wigth[], int n);		//得到压缩数据字节数
	char* Encode(char filename[], int nSize);			//压缩编码
private:
	int selectMin(int n);					//找到最小权值的位置
	char str2byte(char *s);				//字符串转码
private:
	int n;	//wight数
	HFNode hfTree[MAX];
	string code[MAX];
};
//得到压缩数据字节数
char* HuffmanTree::Encode(char filename[],int nSize) {
	//分配空间
	char *out =(char*)malloc(nSize*sizeof(char));
	//缓存区域
	char temp[SIZE] = {0};
	int pos = 0;
	int ch;
	FILE* in = fopen(filename, "rb");
	if (in == NULL) {
		cout << "打开文件失败" << endl;
	}
	//扫描文件,获得编码
	while ((ch = fgetc(in)) != EOF) {
		strcat(temp, this->code[ch].c_str());
		while (strlen(temp) >= 8) {
			out[pos++] = str2byte(temp);
			for (int i = 0; i < SIZE - 8; i++) {
				temp[i] = temp[i + 8];
			}
		}
	}
	if (strlen(temp) > 0)
		out[pos++]= str2byte(temp);
	//关闭文件
	fclose(in);
	return out;
}
//字符串转码,每八个字节
char HuffmanTree::str2byte(char *s) {
	char b = 0x00;
	for (int i = 0; i <  8; i++) {
		b = b << 1;
		if (s[i] =='1') {
			b = b | 0x01;
		}
	}
	return b;
}
//得到压缩数据字节数
int HuffmanTree::getSize(int wigth[], int Number) {
	int nSize = 0;
	for (int i = 0; i < Number; i++) {
		nSize += wigth[i] * code[i].length();
	}
	nSize = (nSize % 8) ? nSize / 8 + 1 : nSize / 8;
	return nSize;
}
//构造编码表
void HuffmanTree::createCode() {
	//由底向上找
//	cout << "编码序号\t编码表" << endl;
	for (int i = 0; i < this->n; i++) {
		string s;
		int temp=i;
		while (this->hfTree[temp].parent != -1) {
			if (this->hfTree[this->hfTree[temp].parent].Lchild == temp)
				s += '0';
			else
				s += '1';
			temp = this->hfTree[temp].parent;
		}
		reverse(s.begin(), s.end());
		//cout <<i<<"\t"<< s << endl;
		this->code[i] = s;
	}
}
//创建哈夫曼树
void HuffmanTree::createHFM(int wight[], int Number) {
	//哈夫曼树初始化,n个叶子节点有 2n-1个节点
	//如果是小于n的结点(叶子结点),则把每个字节的重复次数作为权值赋给这些该结点
	//其他非叶子结点权值设为0
	this->n = Number;
	for (int i = 0; i < 2 * n - 1; i++){
		if (i < n)	
			this->hfTree[i].wight = wight[i];
		else
			this->hfTree[i].wight = 0;	
		hfTree[i].parent = -1;
		hfTree[i].Lchild = -1;
		hfTree[i].Rchild = -1;
	}
	//构造哈夫曼树的非叶子节点
	for (int i = 0; i < n-1 ; i++) {
		//找到第一第二小的权值
		int minF = this->selectMin(n+i);
		this->hfTree[minF].parent = n+i;	//挂在新节点上
		int minS = this->selectMin(n+i);
		this->hfTree[minS].parent = n+i;	//挂在新节点上
		//合并两个生成新的节点
		this->hfTree[n + i].wight = this->hfTree[minF].wight + this->hfTree[minS].wight;
		this->hfTree[n + i].Lchild = minF;
		this->hfTree[n + i].Rchild = minS;
	}
	//测试输出
	/*
	cout <<  "权值\t"  << "左子树\t"  << "右子树\t" << "父亲节点"<< endl;
	for (int i = 0; i < 2*n-1; i++) {
		cout << this->hfTree[i].wight << "\t" << this->hfTree[i].Lchild << "\t" << this->hfTree[i].Rchild << "\t" << this->hfTree[i].parent << endl;
	}
	*/
}
//找到权值最小的位置
int HuffmanTree::selectMin(int n) {
	int minValue = INT_MAX;
	int minIndex = -1;
	for (int i = 0; i < n; i++) {
		if (this->hfTree[i].wight < minValue && this->hfTree[i].parent == -1) {
			minIndex = i;
			minValue = this->hfTree[i].wight;
		}
	}
	return minIndex;
}

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值