您的位置:首页 > 其它

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

2015-12-02 22:56 417 查看
哈夫曼编码原理:设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文件

bmpBitR
9778
ight = 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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  bmp 编码 压缩