hufman编码是一种无损最优编码
编程思路是:先 统计像素概率,再构建hufman树,遍历树,取出全部像素和对应编码,在构建缓冲区,以八位二进制数据写入文件。
预先定义的一些结构体和函数:
struct TreeNode {
float pval;
short pixval;
TreeNode *left;
TreeNode *right;
TreeNode(float x1,int x2) : pval(x1),pixval(x2), left(NULL), right(NULL) {}
TreeNode(float x1) : pval(x1), pixval(-1), left(NULL), right(NULL) {}
TreeNode(TreeNode* x1) : pval(x1->pval), pixval(x1->pixval), left(x1->left), right(x1->right) {}
};
TreeNode* CreatHuffman(float *a, int n) {
int i, j;
struct TreeNode **b, *q;
int size=0; //图像存在的像素值的个数(可能小于256)
for (i = 0;i < n;++i) {
if (a[i] != 0)
size++;
}
b = (TreeNode**)malloc(size * sizeof(TreeNode));
for (i = 0,j = 0; i < n; i++) //初始化b指针数组,使每个指针元素指向a数组中对应的元素结点
if(a[i]!=0){
b[j] = new TreeNode(a[i],i);
b[j]->left = b[j]->right = NULL;
j++;
}
for (i = 1; i < size; i++)//进行 n-1 次循环建立哈夫曼树
{
//k1表示森林中具有最小权值的树根结点的下标,k2为次最小的下标
int k1 = -1, k2;
for (j = 0; j < size; j++)//让k1初始指向森林中第一棵树,k2指向第二棵
{
if (b[j] != NULL && k1 == -1)
{
k1 = j;
continue;
}
if (b[j] != NULL)
{
k2 = j;
break;
}
}
for (j = k2; j < size; j++)//从当前森林中求出最小权值树和次最小
{
if (b[j] != NULL)
{
if (b[j]->pval < b[k1]->pval)
{
k2 = k1;
k1 = j;
}
else if (b[j]->pval < b[k2]->pval)
k2 = j;
}
}
//由最小权值树和次最小权值树建立一棵新树,q指向树根结点
q = (TreeNode* )malloc(sizeof(struct TreeNode));
q->pval = b[k1]->pval + b[k2]->pval;
q->pixval = -1;
q->left = b[k1];
q->right = b[k2];
b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置
b[k2] = NULL;//k2位置为空
}
free(b); //删除动态建立的数组b
return q;
}
map<unsigned char, string> pixencode;
vector<int> inorderTraversal(TreeNode* root,string x) {
vector<int> val;
if (!root)
return val;
vector<int> tmp = inorderTraversal(root->left,x+'0');
if (!tmp.empty())
for (int i = 0;i < tmp.size();i++)
val.push_back(tmp[i]);
if (root->pixval != -1) {
pixencode.insert(pair<unsigned char, string>(root->pixval, x));
}
val.push_back(root->pval);
vector<int> tmp1 = inorderTraversal(root->right,x+'1');
if (!tmp1.empty())
for (int i = 0;i < tmp1.size();i++)
val.push_back(tmp1[i]);
return val;
}
pair<unsigned char, unsigned int> StringBin2Int(string tmp) {
unsigned char size=0;
unsigned int num=0;
while (tmp[0] == '0') {
size++;
tmp = string(tmp.begin() + 1, tmp.end());
}
while (tmp.size()) {
if (tmp[0] == '1') {
num = num * 2;
num++;
}
else {
num = num * 2;
}
tmp = string(tmp.begin() + 1, tmp.end());
}
pair<unsigned char, unsigned int> data;
data.first = size;
data.second = num;
return data;
}
string intnum2string(unsigned char zeronum, unsigned int intnum) {
string tmp = "";
while (intnum) {
if (intnum % 2 == 0) {
tmp = '0' + tmp;
}
else {
tmp = '1' + tmp;
}
intnum = intnum / 2;
}
while (zeronum--) {
tmp = '0' + tmp;
}
return tmp;
}
解码程序:
void CDemoView::Onhufencode()
{
// TODO: 在此添加命令处理程序代码
CDemoDoc *pDoc = GetDocument();
HDIB dib = pDoc->GetHDIB();
LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)dib);
int width = ::DIBWidth(lpDIB);
int height = ::DIBHeight(lpDIB);
LPBITMAPINFOHEADER phead = (LPBITMAPINFOHEADER)lpDIB;
int biBitCount = phead->biBitCount / 8;
int lineByte = (width *biBitCount + 3) / 4 * 4;
unsigned char *lpDIBBits = (unsigned char *)::FindDIBBits(lpDIB);
int palSize = ::PaletteSize((LPSTR)lpDIB); //调色板尺寸
float pixnum[256];
for (int i = 0;i < 256;++i) {
pixnum[i] = 0;
}
for (int i = 0;i < height;++i) {
for (int j = 0;j < width;++j) {
for (int z = 0;z < biBitCount;++z) {
pixnum[*(lpDIBBits + i * lineByte + j * biBitCount + z)]++;
}
}
}
int pixsum = width * height * biBitCount;
for (int i = 0;i < 256;++i) {
pixnum[i] = pixnum[i] / pixsum;
}
TreeNode *Node = CreatHuffman(pixnum, 256); //构建huffman二叉树
string binst;
inorderTraversal(Node, binst);
int sumByte = 0; //原图像Byte大小
int EncodeByte = 0; //压缩后图像Byte大小
sumByte += 40 + palSize;
sumByte += height * lineByte;
ofstream imagefile("D:\\Courseware\\数字图像处理\\图象编程\\图象编程\\image.bin", ios::binary);
if (!imagefile) {
cerr << "open error" << endl;
abort();//退出程序
}
short tablesize = (short)pixencode.size();
imagefile.write((char*)&height,sizeof(int));
imagefile.write((char*)&width, sizeof(int));
imagefile.write((char*)&biBitCount, sizeof(int));
imagefile.write((char*)&tablesize, sizeof(short)); //huffman树数据大小
EncodeByte += 14; //加上写入数据大小
map<unsigned char, string>::iterator iter;
//存储huffman数数据 数据格式: 像素数据 字符串前零的数量 后面字符串对应的int
for (iter = pixencode.begin();iter != pixencode.end();++iter) {
pair<unsigned char, unsigned int> data;
data = StringBin2Int(iter->second);
unsigned char size = data.first;
unsigned int num = data.second;
imagefile.write((char*)&iter->first, sizeof(unsigned char));
imagefile.write((char*)&size, sizeof(unsigned char));
imagefile.write((char*)&num, sizeof(unsigned int));
EncodeByte += 6;//加上写入数据大小
}
int i = 0, j = 0, z = 0;
int a = *(lpDIBBits + i * lineByte + j * biBitCount + z);
string datastring= pixencode[*(lpDIBBits + i * lineByte + j * biBitCount + z)];
z++;
if (biBitCount == 1) {
z = 0;
j++;
}
int size; //bit数,需凑齐八位写入文件
while (1) {
a= *(lpDIBBits + i * lineByte + j * biBitCount + z);
datastring = datastring + pixencode[*(lpDIBBits + i * lineByte + j * biBitCount + z)];//像素1。。2。。
size = datastring.size();
while (size >= 8) {
string tmpdata(datastring.begin() , datastring.begin() + 8);
char todata = 0;
for (int k = 0;k < 8;++k) {
if (tmpdata[k] == '1') {
todata *=2;
todata++;
}
else {
todata *= 2;
}
}
imagefile.write((char*)&todata, sizeof(char));
EncodeByte++;
datastring = string(datastring.begin() + 8, datastring.end());
size = datastring.size();
}
z++;
if (z == biBitCount) {
z = 0;
j++;
if (j == width) {
j = 0;
i++;
if (i == height) {
break;
}
}
}
}
imagefile.close();
CString string;
float ratio = (float)sumByte / EncodeByte;
string.Format("图像压缩率:%3f%%", ratio * 100);
::MessageBox(0, string, "dataMessage", MB_OKCANCEL);
}
解码程序:
map<string, unsigned char> pixdecode;
void CDemoView::Onhuffmandecode()
{
// TODO: 在此添加命令处理程序代码
CDemoDoc *pDoc = GetDocument();
HDIB dib = pDoc->GetHDIB();
LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL)dib);
LPBITMAPINFOHEADER phead = (LPBITMAPINFOHEADER)lpDIB;
ifstream imagefile("D:\\Courseware\\数字图像处理\\图象编程\\图象编程\\image.bin", ios::binary);
int height, width, biBitcount;
/*读入长,高,像素字节数*/
imagefile.read((char*)&height, sizeof(int));
imagefile.read((char*)&width, sizeof(int));
imagefile.read((char*)&biBitcount, sizeof(int));
int palSize = 0;
if (biBitcount == 1) //灰度图像调色板尺寸1024
palSize = 1024;
int lineByte = (width * biBitcount + 3) / 4 * 4;
unsigned char *lpDIBBits = (unsigned char *)::FindDIBBits(lpDIB);
HANDLE dibNew = ::GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER) + palSize + height * lineByte);
LPSTR lpDIBNew = (LPSTR) ::GlobalLock(dibNew);
::memcpy((unsigned char*)lpDIBNew, (unsigned char*)lpDIB, sizeof(BITMAPINFOHEADER) );
LPBITMAPINFOHEADER pheadNew = (LPBITMAPINFOHEADER)lpDIBNew;
/*修改信息头数据*/
pheadNew->biWidth = width;
pheadNew->biHeight = height;
pheadNew->biSizeImage = height * lineByte;
unsigned char *lpDIBBitsNew = (unsigned char *)::FindDIBBits(lpDIBNew);
if (biBitcount == 1) { //如果是灰度图像,加入调色板
unsigned char palette[1024];//定义调色板
for (int i = 0;i < 256;i++)
{
*(palette + i * 4 + 0) = i;
*(palette + i * 4 + 1) = i;
*(palette + i * 4 + 2) = i;
*(palette + i * 4 + 3) = 0;
}
::memcpy((unsigned char*)lpDIBNew + sizeof(BITMAPINFOHEADER), (unsigned char*)palette, 1024);
}
/*读取huffman数据*/
unsigned short tablesize;
imagefile.read((char*)&tablesize, sizeof(short));
unsigned char pixnum, zeronum;
unsigned int intnum;
string tmpdata;
for (int i = 0;i < tablesize;++i) {
imagefile.read((char*)&pixnum, sizeof(unsigned char));
imagefile.read((char*)&zeronum, sizeof(unsigned char));
imagefile.read((char*)&intnum, sizeof(unsigned int));
tmpdata = intnum2string(zeronum, intnum);
pixdecode.insert(pair<string, unsigned char>(tmpdata, pixnum));
}
//读文件赋值//
char data;
int i = 0, j = 0, z = 0;
int num = 0;
string strdata;
string pixstring; //存储字节编码对应的字符串
string tmp; //读取的八位数据对应的string值
while (1) {
imagefile.read((char*)&data, sizeof(char));
unsigned char dataint = (unsigned char)data;
tmp = "";
for (int i = 0;i < 8;++i) { //转化为string,并加到strdata中
if (dataint % 2 == 0) {
tmp = '0' + tmp;
}
else {
tmp = '1' + tmp;
}
dataint /= 2;
}
strdata = strdata + tmp;
int size = strdata.size();
while (1) {
pixstring = pixstring + strdata[num]; //从最后一位向前扩充
num++;
if (pixdecode.count(pixstring)) { //解码成功,赋值
int a = pixdecode[pixstring];
*(lpDIBBitsNew + i * lineByte + j * biBitcount + z) = pixdecode[pixstring];
z++;
if (z == biBitcount) {
z = 0;
j++;
if (j == width) {
j = 0;
i++;
if (i == height)
break;
}
}
strdata = string(strdata.begin() + pixstring.size(), strdata.end()); //更新pixstring和num、size
pixstring = "";
size = strdata.size();
num = 0;
}
if (num == size) //搜索到最高位时,退出循环
break;
}
if (i == height)
break;
}
imagefile.close();
::GlobalUnlock(dib);
::GlobalUnlock(dibNew);
CMainFrame* pFrame = (CMainFrame *)(AfxGetApp()->m_pMainWnd);
pFrame->SendMessage(WM_COMMAND, ID_FILE_NEW);
CDemoView* pView = (CDemoView*)pFrame->MDIGetActive()->GetActiveView();
CDemoDoc* pDocNew = pView->GetDocument();
pDocNew->ReplaceHDIB((HDIB)dibNew);
pDocNew->InitDIBData();
pDocNew->UpdateAllViews(pView);
Invalidate();
}