Huffman压缩图片:
实现结果输出:
- 实现方式见注释
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;
}