256色灰度图哈夫曼编码压缩

来源:互联网 发布:ps切片淘宝上传有缝隙 编辑:程序博客网 时间:2024/05/16 07:15
哈夫曼编码原理:设256种颜色在图片中各出现了a1、a2、…、an次,于是可以得到一个对应的权重数组。将权重数组以以下范例形式建立哈夫曼树。
范例:假设一个含有6个数值的权重数组9、8、3、6、7、1:
1. 首先选出两个最小的权值1、3。建立一颗二叉树:(注意小数在左)


2. 将1、3合并为4,加入原权重数组,即此时权重数组为:9、8、6、7、4
3. 再选出两个最小权值4、6,此时二叉树为:



4.依照上述方法,直到最后建立一颗哈夫曼树:(即每次从权重集合找出两个最小的数值,合并后再加入权重数组。并且同时建立二叉树,直到权重数组只剩下所有数值之和。)



建立颜色数组的哈夫曼树之后,从根节点进行遍历,做字数编码加0,右子树编码加1。(例如范例中7的哈夫曼编码为000,1的哈夫曼编码为1100)此处为变长编码。遍历整棵哈夫曼树之后即可得到256种颜色的哈夫曼编码。读取256灰度的bmp图片对映即可进行压缩。
代码部分:
类中构造器的参数为bmp解码部分,因与本讲述无关故不上传

Compression.h


#pragma once
#include "BMPbase.h"
#include <iostream>
using namespace std;
struct HuffmanTree {
HuffmanTree* parent;//parent节点
HuffmanTree* LTree;//左节点,即为1节点 应为变短编码
HuffmanTree* RTree;//右节点,即为0节点 遵循左清右重原则
int TreeIndex;//树的下标
bool isSorted;//判断是否被选取
int weight;//本节点的权值
int index;//本节点的原编码数位
};
class Compression
{
public:
Compression(BMPbase* pic, int size);
~Compression();
void BuildHuffmanTree(int min1,int min2);
void showTree(HuffmanTree * root, string str);
string* getHuffmanCode();
void choseMin(int & min);
private:
int* weightArray;
int size;//图色数
HuffmanTree** TreeArray;
int SortPoint;//排序静态指针
//int unUsed;//未被用到的颜色数
string* huffmanCode;//生成的哈夫曼编码
HuffmanTree* root;//根节点
};



Compression.cpp


#include "stdafx.h"
#include "Compression.h"


Compression::Compression(BMPbase* pic, int size)
{
this->size = size;
weightArray = new int[size];
huffmanCode = new string[size];
for (int i = 0; i < size; i++)
{
weightArray[i] = 0;
}
for (int i = 0; i < pic->pixNum; i++)//计算权值数组
{
weightArray[pic->getPicPix()[i]] += 1;//将第i个像素的颜色的权值加一
}

TreeArray = new HuffmanTree*[12000];
SortPoint = 0;
for (int i = 0; i < size; i++)//初始化树指针数组
{
if (weightArray[i] == 0)
{
huffmanCode[i] = "-1";
}
else {
TreeArray[SortPoint] = new HuffmanTree();
TreeArray[SortPoint]->index = i;
TreeArray[SortPoint]->TreeIndex = SortPoint;
TreeArray[SortPoint]->weight = weightArray[i];
TreeArray[SortPoint]->isSorted = false;
SortPoint++;
}
}

int min1, min2;
int sort = SortPoint - 1;
for (int i = 0; i < sort; i++)
{
choseMin(min1);//min1为小数 min2为大数
choseMin(min2);//min1为小数 min2为大数
BuildHuffmanTree(min1, min2);
}
int AllTreeNum = SortPoint;
string tmp1;
showTree(root, tmp1);

ofstream haffmanOut("huffman.code");
haffmanOut << AllTreeNum << endl;
for (int i = 0; i < SortPoint; i++) {
if (TreeArray[i]->LTree && TreeArray[i]->RTree){
haffmanOut <<TreeArray[i]->index << " " << TreeArray[i]->LTree->TreeIndex << " " << TreeArray[i]->RTree->TreeIndex << endl;
}
else {
haffmanOut << TreeArray[i]->index << -1 << " " << -1 << endl;
}
}

}

Compression::~Compression()
{
delete[] TreeArray;
}


void Compression::BuildHuffmanTree(int min1,int min2)//min1为小数 min2为大数
{
HuffmanTree* node = new HuffmanTree();
this->root = node;
node->index = -1;
node->LTree = TreeArray[min1];//权值小在左边
TreeArray[min1]->parent = node;
node->RTree = TreeArray[min2];//权值大在右边
TreeArray[min2]->parent = node;
node->weight = TreeArray[min1]->weight + TreeArray[min2]->weight;
node->isSorted = false;
node->TreeIndex = SortPoint;
TreeArray[SortPoint] = node;
SortPoint++;
}

void Compression::showTree(HuffmanTree* root, string str) {
//如果有左孩子就加个0,有右孩子就加个1,直到遍历完成为止
if (root->LTree != NULL)
{
showTree(root->LTree, str + '0');//如果左孩子不为空,就给编码加个0
}
if (root->RTree != NULL)
{
showTree(root->RTree, str + '1');//如果右孩子不为空,就给编码加个1
}
if(root->LTree == NULL && root->RTree == NULL) {
huffmanCode[root->index] = str;
return;
}
return;
}

string* Compression::getHuffmanCode()
{
return huffmanCode;
}

void Compression::choseMin(int &min)
{

int minA;
int i = 0;
bool flag = false;
while(i < SortPoint && !flag) {
if (!TreeArray[i]->isSorted) {
minA = i;
flag = true;
}
i++;
}
for (i = minA + 1; i < SortPoint; i++) {
if (!TreeArray[i]->isSorted) {
if (TreeArray[i]->weight < TreeArray[minA]->weight) {
minA = i;
}
}
}
TreeArray[minA]->isSorted = true;
min = minA;

}


嗯 想了想还是把bmp解码部分放出来好了,保持一下代码的完整性,代码中的注释部分是因为部分256色bmp图片不是太过严格
BMPbase.h


#pragma once
#include <fstream>
#include <iostream>
#include <iomanip>
using namespace std;
struct BitmapFileHeader {
WORD bmpHeader;//文件头“BM”
DWORD fileSize;//文件大小,字节为单位
WORD reservedWord1;//文件保留字1 必为0
WORD reservedWord2;//文件保留字2 必为0
DWORD offSet;//位图数据起始位置
};
struct BitmapFileInfo {
DWORD bmpInfoSize;//即本结构所占的大小 为28 即4字节
LONG bmpWidth;//位图的宽度
LONG bmpHeight;//位图的高度
WORD bmpPlanes;//目标设备的级别(必须为1)
WORD bmpBitCount;//每个像素的尾数,必须为1(双色)4(16色) 8(256色) 24(真彩)
DWORD bmpCompression;//必为0(BI_RGB未压缩) 1(BI_RLEB) 2(BI_RLE4)
DWORD bmpSizeImage;//位图的大小 单位为字节
LONG bmpXpelsPerMeter;//水平分辨率 每米像素数
LONG bmpYpelsPerMeter;//垂直分辨率
DWORD bmpColorUsed;//位图显示所需颜色数
DWORD bmpColorImportant;//重要的颜色数 若为0 则都重要
};
class BMPbase
{
public:

BMPbase(const char* fileName);
~BMPbase();
private:
bool fileSuccess;//判断文件是否打开成功的标志
bool fileCompression;//判断文件是否被压缩
bool bmpFileflag;//判断是否为BMP文件
bool bmpBitRight;//判断文件位数是否正确
BitmapFileHeader bmpHeader;//bmp文件头
BitmapFileInfo BmpPic;//bmp文件基本信息
BYTE* picPix;//图片数据数组

public:
bool isReadFileSuccess();
bool isBmpFile();
BitmapFileHeader getBmpHedader();//获取文件头
BitmapFileInfo getBmpInfo();//获取信息结构体
bool isBmpBitRight();//获取位图是否正确
bool isFileCompression();//获取文件是否被压缩
BYTE* getPicPix();//获取颜色数组
int pixNum;
};


BMPbase.cpp
#include "stdafx.h"
#include "BMPbase.h"


BMPbase::BMPbase(const char* fileName)
{
fileSuccess = true;//初始化文件为成功打开
bmpFileflag = true;//初始化文件为BMP文件
bmpBitRight = true;//初始化文件位数为正确
fileCompression = true;//初始化文件为未压缩
ifstream picReader(fileName, ios::binary);
if (!picReader)
{
fileSuccess = false;//文件读取失败
}
else
{
picReader.read((char*)&bmpHeader.bmpHeader, sizeof(WORD));
if (bmpHeader.bmpHeader != 0x4D42)
{
bmpFileflag = false;//文件读取失败,类型不一致
}
else//开始读取文件信息
{
picReader.read((char*)&bmpHeader.fileSize, sizeof(DWORD));//读文件大小
picReader.read((char*)&bmpHeader.reservedWord1, sizeof(WORD));//读取保留字
picReader.read((char*)&bmpHeader.reservedWord2, sizeof(WORD));//读取保留字
/*if (bmpHeader.reservedWord1 != 0x00 || bmpHeader.reservedWord2 != 0x00)//判断文件保留字是否正确
{
fileSuccess = false;//文件读取失败
}*/
picReader.read((char*)&bmpHeader.offSet, sizeof(DWORD));//读取偏移量
picReader.read((char*)&BmpPic.bmpInfoSize, sizeof(DWORD));
/*if (BmpPic.bmpInfoSize != 0x28)//判断文件信息长度是否正确
{
fileSuccess = false;//文件读取失败
}*/
picReader.read((char*)&BmpPic.bmpWidth, sizeof(DWORD));//获取宽度
picReader.read((char*)&BmpPic.bmpHeight, sizeof(DWORD));//获取高度
picReader.read((char*)&BmpPic.bmpPlanes, sizeof(WORD));//获取目标设备的级别 必须为1
/*if (BmpPic.bmpPlanes != 0x01)//判断设备级别是否正确
{
fileSuccess = false;//文件读取失败
}*/
picReader.read((char*)&BmpPic.bmpBitCount, sizeof(WORD));//获取每像素所需位数 8位图
if (BmpPic.bmpBitCount != 8)//判断是否为8位图
{
bmpBitRight = false;//文件读取失败,非8位图
}
picReader.read((char*)&BmpPic.bmpCompression, sizeof(DWORD));//获取文件压缩
if (BmpPic.bmpCompression != 0x00)//判断是否压缩
{
fileCompression = false;//文件读取失败,已被压缩
}
picReader.read((char*)&BmpPic.bmpSizeImage, sizeof(DWORD));//获取sizeImage
picReader.read((char*)&BmpPic.bmpXpelsPerMeter, sizeof(DWORD));//获取文件水平分辨率
picReader.read((char*)&BmpPic.bmpYpelsPerMeter, sizeof(DWORD));//获取文件水平分辨率
picReader.read((char*)&BmpPic.bmpColorUsed, sizeof(DWORD));//获取文件用到颜色
picReader.read((char*)&BmpPic.bmpColorImportant, sizeof(DWORD));//获取文件重要颜色
picReader.seekg(bmpHeader.offSet, ios::beg);//从文件开头开始偏移前面读出的偏移量
pixNum = BmpPic.bmpHeight*BmpPic.bmpWidth;
picPix = new BYTE[pixNum];
for (int x = 0; x < pixNum; x++)
{
picReader.read((char*)&picPix[x], sizeof(BYTE));//获取文件数据部分
}

}
}
}


BMPbase::~BMPbase()
{
for (int x = 0; x < BmpPic.bmpWidth; x++)
{
delete[] picPix;
}
}


bool BMPbase::isReadFileSuccess()
{
return fileSuccess;
}


bool BMPbase::isBmpFile()
{
return bmpFileflag;
}


BitmapFileHeader BMPbase::getBmpHedader()
{
return bmpHeader;
}


BitmapFileInfo BMPbase::getBmpInfo()
{
return BmpPic;
}


bool BMPbase::isBmpBitRight()
{
return bmpBitRight;
}


bool BMPbase::isFileCompression()
{
return fileCompression;
}


BYTE* BMPbase::getPicPix()
{
return picPix;
}

0 0
原创粉丝点击