对bmp灰度图像的Huffman编解码

环境:Win10 (x64)
编译器:VS2019 (x64)


encode输入图像文件 gray.bmp
生成文件file.dat 和 code.dat


decode输入文件file.dat 和 code.dat
生成图像gary.bmp


encode:

#include"HXLBMPFILE.h"
#include
#include
#include
#include
using namespace std;

class HuffmanNode
{
public:
    unsigned char symbol;
    unsigned int freq;
    unsigned int codeword;
    unsigned short codewordLen;//二进制长度
    HuffmanNode* left, * right;

    HuffmanNode()
    {
        left = right = 0; 
    }
    HuffmanNode(unsigned char s, unsigned int f, HuffmanNode* lf = 0, HuffmanNode* rt = 0)
    {
        symbol = s;
        freq = f;
        left = lf, right = rt;
    }
};

class ListNode
{
public:
    HuffmanNode* tree;
    ListNode* prev, * next;
 
    ListNode()
    {
        next = prev = 0;
    }
    ListNode(ListNode* p, ListNode* n)
    {
        prev = p, next = n;
    }
};//包括HuffmanNode 用来作为链表的节点

class DataRec
{
public:
    unsigned char symbol;
    unsigned int freq; 

    DataRec()
    {
    }

    bool operator == (const DataRec& dr) const
    {
        return symbol == dr.symbol;
    }
    bool operator < (const DataRec& dr)const
    {
        return freq < dr.freq;
    }
};

class HuffmanCoding
{
public:
    const unsigned int ASCII;
    const unsigned int bits;
    const unsigned short mask = 0xff;
    HuffmanNode* HuffmanTree, ** chars;//编码序列
    unsigned short dataCnt;
    unsigned int bitsCnt;
    vector<DataRec> data;

    HuffmanCoding(): ASCII(256), bits(8)
    {
        chars = new HuffmanNode * [ASCII + 1];
        for (int i = 0; i < ASCII; i++)
             chars[i] = 0;
    } 

    void encode(HXLBMPFILE*, const char*, const char*);
    void input(HXLBMPFILE*);
    void writebytes(ofstream&, unsigned int, int);
    void createHuffmanTree();
    void createCodewords(HuffmanNode*, unsigned int, unsigned short);
    void transformTree2Lists(HuffmanNode*);
    void outputHeader(HXLBMPFILE*, const char*);
    void outputData(HXLBMPFILE*, const char*);
    void saveCode(const char*);
};

void  HuffmanCoding::encode(HXLBMPFILE* bmpfile, const char* FileName1, const char* FileName2)
{
    input(bmpfile);
    createHuffmanTree();
    createCodewords(HuffmanTree, 0, 0);
    transformTree2Lists(HuffmanTree);
    outputHeader(bmpfile, FileName1);
    outputData(bmpfile, FileName1);
    saveCode(FileName2);
} 

void HuffmanCoding::writebytes(ofstream& fout, unsigned int pack, int bytes)
{
    unsigned char* s = new unsigned char[bytes];
    for (int i = bytes - 1; i >= 0; i--)
    {
        s[i] = pack & mask;
        pack >>= bits;
    }
    for (int i = 0; i < bytes; i++)
        fout.put(s[i]);
} 

void HuffmanCoding::input(HXLBMPFILE* bmpfile)
{
    unsigned char ch;
    DataRec r;//当前的数据
    vector<DataRec>::iterator iter;//迭代器
    r.freq = 1; 

    for (int i = 0; i < (*bmpfile).imageh; i++)
        for (int j = 1; j < (*bmpfile).imagew; j++)
        {
             ch = (*bmpfile).pDataAt(i)[j];
             r.symbol = ch;
             iter = find(data.begin(), data.end(), r);
             if (iter == data.end())
                 data.push_back(r);
             else
                 iter->freq++;
        }
    sort(data.begin(), data.end());
    dataCnt = data.size();
} 

void HuffmanCoding::createHuffmanTree()
{
    //先创建ListNode为节点的双向链表
    //再将其改为HuffmanTree
    ListNode* head, * tail;//链表
    unsigned int newFreq;
    head = tail = new ListNode;
    //创建第一个节点
    head->tree = new HuffmanNode(data[0].symbol, data[0].freq);
    //创建双向链表的剩余部分
    for (int i = 1; i < data.size(); i++)
    {
        tail->next = new ListNode(tail, 0);
        tail = tail->next;
        tail->tree = new HuffmanNode(data[i].symbol, data[i].freq);
    }
    //创建HuffmanTree
    while (head != tail)
    {
        newFreq = head->tree->freq + head->next->tree->freq;
        ListNode* p, * newNode;
        for (p = tail; p != 0 && p->tree->freq > newFreq; p = p->prev)
             ;//在链表中找到新节点插入的位置
        newNode = new ListNode(p, p->next);
        p->next = newNode;
        if (p == tail)//p->next == 0
             tail = newNode;
        else
             newNode->next->prev = newNode;
        newNode->tree = new HuffmanNode('\0', newFreq, head->tree, head->next->tree);
        head = head->next->next;
        delete head->prev->prev;
        delete head->prev;
        head->prev = 0;
    }
    HuffmanTree = head->tree;
    delete head;
} 

void HuffmanCoding::createCodewords(HuffmanNode* p, unsigned int codeword, unsigned short codewordLen)
{
    if (p->left == 0 && p->right == 0)
    {
        p->codeword = codeword;
        p->codewordLen = codewordLen;
    }
    else
    {
        createCodewords(p->left, codeword << 1, codewordLen + 1);//左0
        createCodewords(p->right, codeword << 1 | 1, codewordLen + 1);//右1
    }
} 

void HuffmanCoding::transformTree2Lists(HuffmanNode* p)
{
    if (p->left == 0 && p->right == 0)
    {
        p->right = 0;
        p->left = 0;
        chars[unsigned char(p->symbol)] = p;
    }
    else
    {
        transformTree2Lists(p->left);
        transformTree2Lists(p->right);
    }
}

void HuffmanCoding::outputHeader(HXLBMPFILE* bmpfile, const char* FileName)
{
    FILE* f;
    f = fopen(FileName, "w+b");
 
    BITMAPFILEHEADER fh;
    BITMAPINFOHEADER ih;
    memset(&ih, 0, sizeof(BITMAPINFOHEADER)); 

    fh.bfType = 0x4d42;
    fh.bfReserved1 = fh.bfReserved2 = 0;
    fh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (((*bmpfile).iYRGBnum == 1) ? 256 * sizeof(RGBQUAD) : 0); 

    ih.biSize = 40; ih.biPlanes = 1;
    ih.biWidth = (*bmpfile).imagew;
    ih.biHeight = (*bmpfile).imageh;
    ih.biBitCount = 8 * (*bmpfile).iYRGBnum; 

    int w4b = ((*bmpfile).imagew * (*bmpfile).iYRGBnum + 3) / 4 * 4;
    ih.biSizeImage = ih.biHeight * w4b;
    fh.bfSize = fh.bfOffBits + ih.biSizeImage; 

    fwrite(&fh, sizeof(BITMAPFILEHEADER), 1, f);
    fwrite(&ih, sizeof(BITMAPINFOHEADER), 1, f);
    fwrite((*bmpfile).palette, sizeof(RGBQUAD), ASCII, f);

    fclose(f);
}

void HuffmanCoding::outputData(HXLBMPFILE* bmpfile, const char* FileName)
{
    ofstream fout;
    fout.open(FileName, ios::app | ios::out | ios::binary);
    const int bytes = 3;//codewordLen最大18

    unsigned int packCnt = 0, maxPack = bytes * bits, pack = 0;
    unsigned int nowcodeword = 0, nowcodewordLen = 0;
    unsigned int bitsLeft = 0, hold = 0;
    unsigned char ch;
    bitsCnt = 0; 

    for (int i = (*bmpfile).imageh - 1; i >= 0; i--)
    {
        for (int j = 0; j < (*bmpfile).imagew; j++)
        {
             //以ASCII码形式存储编码后的文件数据
             nowcodeword = chars[(*bmpfile).pDataAt(i)[j]]->codeword;
             nowcodewordLen = chars[(*bmpfile).pDataAt(i)[j]]->codewordLen;
             if (nowcodewordLen < maxPack - packCnt)
             {
                 pack = (pack << nowcodewordLen) | nowcodeword;//左移并记录当前codeword
                 packCnt += nowcodewordLen;//更新长度
             }
             else
             {
                 bitsLeft = maxPack - packCnt;
                 pack <<= bitsLeft;//左移
                 if (bitsLeft != nowcodewordLen)
                 {
                     hold = nowcodeword;
                     hold >>= nowcodewordLen - bitsLeft;//右移去掉不能进入的位
                     pack |= hold;//记录hold
                 }
                 else
                     pack |= nowcodeword;//记录当前codeword
                 writebytes(fout, pack, bytes);
                 bitsCnt += bytes * bits;
                 if (bitsLeft != nowcodewordLen)//处理当前codeword剩下的位
                 {
                     pack = nowcodeword;
                     packCnt = nowcodewordLen - bitsLeft;
                     pack &= (1 << packCnt) - 1;
                 }
                 else
                 {
                     pack = 0;
                     packCnt = 0;
                 }
             }
        }
    }
    if (packCnt != 0)
    {
        pack <<= maxPack - packCnt;//左移补0
        writebytes(fout, pack, bytes);
        bitsCnt += packCnt;
    }

    fout.close();
} 

void HuffmanCoding::saveCode(const char* FileName)
{
    ofstream fout;
    fout.open(FileName, ios::out | ios::binary);
    char ch, ch2;
    unsigned long long totalLen = 0;
    unsigned long long totalFreq = 0;
    writebytes(fout, dataCnt, 1);
    writebytes(fout, bitsCnt, 3); 

    for (int i = data.size() - 1; i >= 0; i--)
    {
        fout.put(data[i].symbol);
        writebytes(fout, chars[data[i].symbol]->codeword, 3);
        fout.put(unsigned char(chars[data[i].symbol]->codewordLen));
        totalLen += unsigned long long(chars[data[i].symbol]->codewordLen) * chars[data[i].symbol]->freq;
        totalFreq += unsigned long long(chars[data[i].symbol]->freq);
    }
    printf("bpp: %lf \n", totalLen * 1.0 / totalFreq); 

    fout.close();
}

调用:

#include"HuffmanCoding.h"
#include"HXLBMPFILE.h"
using namespace std; 

int main()
{
    HXLBMPFILE originalfile;
    HXLBMPFILE source;
    HuffmanCoding Huffman; 

    if (!originalfile.LoadBMPFILE("gray.bmp"))
        exit(1); 

    source.imagew = originalfile.imagew;
    source.imageh = originalfile.imageh;
    source.iYRGBnum = originalfile.iYRGBnum;
    *source.palette = *originalfile.palette; 

    if (!source.AllocateMem())
        exit(1); 

    for (int i = 0; i < originalfile.imageh; i++)
        for (int j = 0; j < originalfile.imagew; j++)
             source.pDataAt(i)[j] = originalfile.pDataAt(i)[j]; 

    Huffman.encode(&source, "file.dat", "code.dat"); 

    return 0;
}

decode:

#pragma once
#include"HXLBMPFILE.h"
#include
#include
#include
#include
using namespace std; 

class HuffmanNode
{
public:
    unsigned char symbol;
    unsigned int codeword;
    unsigned short codewordLen;//二进制长度
    HuffmanNode* left, * right; 

    HuffmanNode()
    {
        left = right = 0;
    }
    HuffmanNode(unsigned char s, HuffmanNode* lf = 0, HuffmanNode* rt = 0)
    {
        symbol = s;
        left = lf, right = rt;
    }
}; 

class ListNode
{
public:
    HuffmanNode* tree;
    ListNode* prev, * next; 

    ListNode()
    {
        next = prev = 0;
    }
    ListNode(ListNode* p, ListNode* n)
    {
        prev = p, next = n;
    }
};//包括HuffmanNode 用来作为链表的节点 

class HuffmanCoding
{
public:
    const unsigned int ASCII;
    const unsigned int bits;
    const unsigned short mask = 0xffff;
    HuffmanNode* HuffmanTree, ** chars;//编码序列
    unsigned short dataCnt;
    unsigned int bitsCnt;

    HuffmanCoding() : ASCII(256), bits(8)
    {
        chars = new HuffmanNode * [ASCII + 1];
        for (int i = 0; i < ASCII; i++)
             chars[i] = 0;
    } 

    void decode(HXLBMPFILE*, const char*, const char*);
    void loadCode(const char*);
    unsigned int readbytes(ifstream&, int);
    void rebuildHuffmanNode(unsigned char, HuffmanNode*, unsigned int, unsigned short);
    void rebuildHuffmanTree();
    void inputHeader(HXLBMPFILE*, const char*);
    void inputData(HXLBMPFILE*, const char*);
}; 

void HuffmanCoding::decode(HXLBMPFILE* bmpfile, const char* FileName1, const char* FileName2)
{
    loadCode(FileName2);
    rebuildHuffmanTree();
    inputHeader(bmpfile, FileName1);
    inputData(bmpfile, FileName1);
} 

unsigned int HuffmanCoding::readbytes(ifstream& fin, int bytes)
{
    unsigned int num;
    num = unsigned int(fin.get());
    for (int i = 1; i < bytes; i++)
    {
        num <<= bits;
        num |= unsigned int(fin.get());
    }
    return num;
} 

void HuffmanCoding::loadCode(const char* FileName)
{
    ifstream fin;
    fin.open(FileName, ios::in | ios::binary);
    unsigned char ch;
    if (!fin)
        exit(1);
    dataCnt = readbytes(fin, 1);
    bitsCnt = readbytes(fin, 3); 

    for (int i = 0; i < dataCnt; i++)
    {
        ch = unsigned char(readbytes(fin, 1));
        chars[ch] = new HuffmanNode(ch);
        chars[ch]->codeword = readbytes(fin, 3);
        chars[ch]->codewordLen = readbytes(fin, 1);
    }
} 

void HuffmanCoding::rebuildHuffmanNode(unsigned char ch, HuffmanNode* p, unsigned int codeword, unsigned short codewordLen)
{
    if (codewordLen == 0)
    {
        p->codeword = chars[ch]->codeword;
        p->codewordLen = chars[ch]->codewordLen;
        p->right = p->left = 0;
        p->symbol = ch;
    }
    else
    {
        if ((codeword >> (codewordLen - 1)) & 1)
        {
             if (p->right == NULL)
                 p->right = new HuffmanNode('\0');
             rebuildHuffmanNode(ch, p->right, codeword, codewordLen - 1);
        }
        else
        {
             if (p->left == NULL)
                 p->left = new HuffmanNode('\0');
             rebuildHuffmanNode(ch, p->left, codeword, codewordLen - 1);
        }
    }
} 

void HuffmanCoding::rebuildHuffmanTree()
{
    //根据码表中内容左0右1重建Huffman树
    ListNode* head;
    head = new ListNode;
    head->tree = new HuffmanNode;
    for (int ch = 0; ch < ASCII; ch++)
    {
        if (chars[unsigned char(ch)] != NULL)
        {
             rebuildHuffmanNode(ch, head->tree, chars[ch]->codeword, chars[ch]->codewordLen);
        }
    }
    HuffmanTree = head->tree;
} 

void HuffmanCoding::inputHeader(HXLBMPFILE* bmpfile, const char* FileName)
{
    FILE* f;
    f = fopen(FileName, "r+b"); 

    BITMAPFILEHEADER fh;
    BITMAPINFOHEADER ih; 

    fread(&fh, sizeof(BITMAPFILEHEADER), 1, f);
    if (fh.bfType != 0x4d42)
    {
        fclose(f);
        return;
    } 

    fread(&ih, sizeof(BITMAPINFOHEADER), 1, f);
    if ((ih.biBitCount != 8) && (ih.biBitCount != 24))
    {
        fclose(f);
        return;
    } 

    (*bmpfile).iYRGBnum = ih.biBitCount / 8;
    (*bmpfile).imagew = ih.biWidth;
    (*bmpfile).imageh = ih.biHeight; 

    if (!(*bmpfile).AllocateMem())
    {
        fclose(f);
        return;
    } 

    if ((*bmpfile).iYRGBnum == 1)
        fread((*bmpfile).palette, sizeof(RGBQUAD), 256, f);
    fseek(f, fh.bfOffBits, SEEK_SET);

    fclose(f);
}

void HuffmanCoding::inputData(HXLBMPFILE* bmpfile, const char* FileName)
{
    ifstream fin;
    fin.open(FileName, ios::in | ios::binary);
    unsigned char ch = 0;
    unsigned long long nowbits = 0, nowbitsCnt = 0;
    int i = (*bmpfile).imageh - 1, j = 0;
    int mask = 1 << (bits - 1); 
    if (!fin)
        exit(1);
    fin.seekg(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (((*bmpfile).iYRGBnum == 1) ? 256 * sizeof(RGBQUAD) : 0), ios::beg); 

    ch = fin.get();
    while (nowbitsCnt < bitsCnt && i >= 0)
    {
        for (HuffmanNode* p = HuffmanTree; ;)
        {
             if (p->left == 0 && p->right == 0)
             {
                 (*bmpfile).pDataAt(i)[j] = p->symbol;
                 if (++j == (*bmpfile).imagew)
                     j = 0, i--;
                 nowbitsCnt += p->codewordLen;
                 break;
             }
             else if ((ch & mask) == 0)
                 p = p->left;
             else
                 p = p->right;
             if (++nowbits == bits)
             {
                 ch = fin.get();
                 nowbits = 0;
             }
             else
                 ch <<= 1;
        }
    } 

    fin.close();
}

调用:

#include"HuffmanCoding.h"
#include"HXLBMPFILE.h"
using namespace std; 

int main()
{
    HXLBMPFILE target;
    HuffmanCoding Huffman;
    const char* FileName = "gray.bmp"; 

    Huffman.decode(&target, "file.dat", "code.dat");
    target.SaveBMPFILE(FileName); 
}

你可能感兴趣的:(对bmp灰度图像的Huffman编解码)