zoukankan      html  css  js  c++  java
  • 哈夫曼图片压缩

    本实验构建最优二叉树来实现哈夫曼编码

    使用VS2017完成

    关于哈夫曼编码的头文件huffman.h

    //huffman.h
    
    #ifndef HUFFMAN_H
    #define HUFFMAN_H
    #define OK 1
    #define SIZE 256
    struct HTNode {
        int weight;//权值
        int parent;//父节点
        int lchild;//左孩子
        int rchild;//右孩子
    };
    typedef HTNode *HuffmanTree;//动态分配数组存储Huffman树
    typedef char **HuffmanCode;//动态分配哈夫曼编码表
    
    
    //void PreorderTraverse(int root, HuffmanTree pHT);
    int HuffmanCoding(HuffmanCode &pHC, HuffmanTree &pHT);
    int Select(HuffmanTree pHT, int nSize);
    void TestHufTree(HuffmanTree pHT);
    void TestHufCode(int root, HuffmanTree pHT, HuffmanCode pHC);
    void TestHufTreeN(int root, HuffmanTree pHT);
    
    int HfmTree(HuffmanTree &pHT, int *w, int n);
    
    #endif

    相关实现huffman.cpp

    //Huffman.cpp
    
    #include<iostream>
    #include<cstring>
    #include"huffman.h"
    #pragma warning( disable : 4996)
    using namespace std;
    /*
    void PreorderTraverse(int root, HuffmanTree pHT)
    {
        cout << pHT[root].weight << " ";//访问节点
        if (pHT[root].lchild)//左孩子
        {
            PreorderTraverse(pHT[root].lchild, pHT);
        }
        if (pHT[root].rchild)//右孩子
        {
            PreorderTraverse(pHT[root].rchild, pHT);
        }
    }
    */
    int HuffmanCoding(HuffmanCode &pHC, HuffmanTree &pHT)
    {
    //    pHC = (HuffmanCode)malloc((SIZE + 1) * sizeof(char*));
        //无栈非递归遍历 
        char cd[SIZE] = { '' };//记录访问路径
        int cdlen = 0;//记录当前路径长度
        for (int i = 1; i < 512; i++)
        {
            pHT[i].weight = 0;//遍历 Huffman树时用作节点的状态标志
        }
    
        int p = 2*SIZE-1;//根节点
        while (p != 0)
        {
            if (pHT[p].weight == 0)//向左
            {
                pHT[p].weight = 1;
                if (pHT[p].lchild != 0)
                {
                    p = pHT[p].lchild;
                    cd[cdlen++] = '0';
                }
                else if (pHT[p].rchild == 0)//登记叶子节点的字符编码
                {
                    pHC[p] = (char*)malloc((cdlen+1) * sizeof(char));
                    cd[cdlen] = '';
                    strcpy(pHC[p], cd);//复制编码
                }
            }
            else if (pHT[p].weight == 1)//向右
            {
                pHT[p].weight = 2;
                if (pHT[p].rchild != 0)//右孩子为叶子节点
                {
                    p = pHT[p].rchild;
                    cd[cdlen++] = '1';
                }
            }
            else
            {
                //退回父节点,编码长度减1
                pHT[p].weight = 0;
                p = pHT[p].parent;
                --cdlen;
            }
    //        printf("*");
        }
        return OK;
    }
    
    int Select(HuffmanTree pHT, int nSize)
    {
        int minValue = 0x7FFFFFFF;//最小值
        int min = 0;
        //找到最小权值的元素序号
        for (int i = 1; i <= nSize; i++)
        {
            if (pHT[i].parent == 0 && pHT[i].weight < minValue)
            {
                minValue = pHT[i].weight;
                min = i;
            }
        }
        return min;
    }
    
    void TestHufTree(HuffmanTree pHT)
    {
        for (int i = 1; i < 2*SIZE; i++)
        {
            printf("pHT[%d]	%d	%d	%d	%d
    ", i, pHT[i].weight, pHT[i].parent,pHT[i].lchild,pHT[i].rchild);
        }
    }
    
    int HfmTree(HuffmanTree &pHT, int *w, int n)
    {
        int m = 2 * n - 1;
        pHT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));
        if (!pHT)
        {
            cerr << "内存分配失败! " << endl;
            return -1;
        }
        //初始化树
        HuffmanTree p = pHT + 1;//0号单元不使用
        for (int i = 0; i < m; i++)
        {
            p->weight = (i < n) ? w[i] : 0;
            p->parent = 0;
            p->lchild = 0;
            p->rchild = 0;
            p++;
        }
        for (int i = n + 1; i <= m; i++)
        {
            //第一个最小元素
            int s1 = Select(pHT, i - 1);//找出前i-1个中最小元素
            pHT[s1].parent = i;
    
            //第二个最小元素
            int s2 = Select(pHT, i - 1);
            pHT[s2].parent = i;
    
            pHT[i].weight = pHT[s1].weight + pHT[s2].weight;
            pHT[i].lchild = s1;
            pHT[i].rchild = s2;
        }
        return 0;
    }
    
    void TestHufCode(int root, HuffmanTree pHT, HuffmanCode pHC)
    {
        if (pHT[root].lchild == 0 && pHT[root].rchild == 0)
        {
            printf("0x%02X %s
    ", root - 1, pHC[root]);
        }
        if (pHT[root].lchild)//访问左孩子
        {
            TestHufCode(pHT[root].lchild, pHT, pHC);
        }
        if (pHT[root].rchild)
        {
            TestHufCode(pHT[root].rchild, pHT, pHC);
        }
    }
    
    void TestHufTreeN(int root, HuffmanTree pHT)
    {
        cout << pHT[root].weight << "	"<<pHT[root].lchild<<"	"<<pHT[root].rchild<<"	"<<pHT[root].parent<<"
    ";
        if (pHT[root].lchild != 0)
        {
            TestHufTreeN(pHT[root].lchild, pHT);
        }
        if (pHT[root].rchild != 0)
        {
            TestHufTreeN(pHT[root].rchild, pHT);
        }
    }

    压缩相关操作的头文件Compress.h

    //Compress.h
    
    #ifndef COMPRESS_H
    #define COMPRESS_H
    int Compress(const char *pFilename);
    char Str2byte(const char *pBinStr);
    int Encode(const char*pFilename, const HuffmanCode pHC, char *pBuffer, const int nSize);
    
    struct HEAD
    {
        char type[4];//文件类型
        int length;//原文件长度
        int weight[256];//权值数值
    };
    int WriteFile(const char*pFilename, const HEAD sHead, const char * pBuffer, const int nSize);
    int InitHead(const char *pFilename, HEAD &sHead);
    #endif

    具体实现Compress.cpp

      1 //Compress.cpp
      2 
      3 #include"huffman.h"
      4 #include"Compress.h"
      5 #include<iostream>
      6 #pragma warning( disable : 4996)
      7 using namespace std;
      8 //Compress
      9 //InitHead
     10 //Encode
     11 //Str2byte
     12 //WriteFile
     13 char Str2byte(const char *pBinStr)
     14 {
     15     char b = 0x00;
     16     for (int i = 0; i < 8; i++)
     17     {
     18         b = b << 1;
     19         if (pBinStr[i] == '1')
     20         {
     21             b = b | 0x01;
     22         }
     23     }
     24     return b;
     25 }
     26 
     27 int Compress(const char *pFilename)
     28 {
     29     int weight[256] = { 0 };
     30     //以二进制打开文件
     31     FILE* in = fopen(pFilename, "rb");
     32     if (in == NULL)
     33     {
     34         cout << "Failed to open the file!" << endl;
     35         exit(0);
     36     }
     37     cout << "成功打开文件 " << pFilename << endl;
     38     int ch;
     39     while ((ch = getc(in)) != EOF)
     40     {
     41         weight[ch]++;
     42     }
     43     fclose(in);
     44     //cout << "Byte Weight" << endl;
     45     //for (int i = 0; i < SIZE; i++)
     46     //{
     47     //    printf("0x%02X %d
    ", i, weight[i]);
     48     //}
     49 
     50     HuffmanTree hfmt;
     51     HfmTree(hfmt, weight, SIZE);
     52     cout << "成功生成哈夫曼树" << endl;
     53 //    TestHufTree(hfmt);
     54     //    TestHufTreeN(511, hfmt);
     55     HuffmanCode hfmc=(HuffmanCode)malloc((SIZE+1)*sizeof(char*));
     56 //    for (int i = 1; i <= SIZE; i++)
     57 //        hfmt[i].weight = weight[i - 1]
     58     //根据哈夫曼树进行编码
     59     HuffmanCoding(hfmc, hfmt);
     60     cout << "成功完成哈夫曼编码" << endl;
     61 //    cout << "先序遍历哈夫曼树输出编码信息:" << endl;
     62 //    TestHufCode(2 * SIZE - 1, hfmt, hfmc);//测试哈夫曼编码
     63 //    cout << "压缩后的文件编码:" << endl;
     64 
     65     //计算编码缓冲区大小
     66     int nSize = 0;
     67     for (int i = 0; i < 256; i++)
     68     {
     69         nSize += weight[i] * strlen(hfmc[i+1]);
     70     }
     71     nSize = (nSize % 8) ? nSize / 8 + 1 : nSize / 8;
     72 
     73 //    cout <<"nSize = "<<nSize << endl << endl;
     74 
     75     //对原文件进行压缩编码
     76     char* pBuffer = NULL;
     77     pBuffer = (char *)malloc(nSize*sizeof(char));
     78     memset(pBuffer, 0, (nSize) * sizeof(char));
     79 //    cout << "begin: " << strlen(pBuffer) << endl;
     80 ////    cout << "----";
     81 //    int n;
     82 //    cout << "input n:";
     83 //    cin >> n;
     84     //将编码写入缓冲区
     85     Encode(pFilename, hfmc, pBuffer, nSize);
     86 //    cout << "after: " << strlen(pBuffer) << endl;
     87 //    cout << "len of puf  = " << strlen(pBuffer) << endl;
     88 //    cout << "!pBuffer = " << !pBuffer << endl;
     89     if (!pBuffer)
     90     {
     91         cout << "!pBuffer = " << !pBuffer << endl;
     92         return -1;
     93     }
     94     cout << "
    压缩完毕" << endl;
     95     //for (int i = 1; i < strlen(pBuffer); i++)
     96     //{
     97     //    printf("%d", pBuffer[i]);
     98     //}
     99 
    100     HEAD sHead;
    101     InitHead(pFilename, sHead);
    102     cout <<"原文件"<< pFilename<<"大小为:" << sHead.length << "Byte" << endl;
    103     int len_after = WriteFile(pFilename, sHead, pBuffer, nSize);
    104     cout << "大小为:" << len_after << "Byte 
    头文件sHead大小为:" << sizeof(sHead)<<"Byte"<<endl;
    105     cout << "压缩比率:" << (double)len_after * 100 / sHead.length << "%" << endl;
    106     free(hfmt);
    107     free(hfmc);
    108     free(pBuffer);
    109     return OK;
    110 }
    111 
    112 
    113 int Encode(const char*pFilename, const HuffmanCode pHC, char *pBuffer, const int nSize)
    114 {
    115     //开辟缓冲区
    116 //    cout << "+++++";
    117     FILE* in = fopen(pFilename, "rb");
    118     if (in == NULL)
    119     {
    120         cout << "Failed to open the file!" << endl;
    121         exit(0);
    122     }
    123     pBuffer = (char*)malloc(nSize * sizeof(char));
    124     if (!pBuffer)
    125     {
    126         cerr << "开辟缓冲区失败" << endl;
    127         return -1;
    128     }
    129     cout << "loading";
    130     int sign = 0;//用于控制小数点输出
    131     char cd[SIZE] = { 0 };//工作区
    132     int pos = 0;//缓冲区指针
    133     int ch;
    134     //扫描文件,根据huffmman编码表对其进行压缩,压缩结果暂存到缓冲区中
    135     while ((ch = getc(in)) != EOF)
    136     {
    137         if (sign % 1000 == 1)
    138             printf(".");
    139         sign++;
    140         strcat(cd, pHC[ch+1]);//从HC复制编码串到cd
    141 
    142 
    143         //打印压缩后的文件编码
    144 //        printf("%s", pHC[ch + 1]);
    145 
    146 
    147         //压缩编码
    148         while (strlen(cd) >= 8)
    149         {
    150             //截取字符串左边的8个字符,编码成字节
    151             pBuffer[pos++] = Str2byte(cd);
    152             //字符串整体左移8个字节
    153             for (int i = 0; i < SIZE - 8; i++)
    154             {
    155                 cd[i] = cd[i + 8];
    156             }
    157         }
    158     }
    159     if (strlen(cd) > 0)
    160     {
    161         pBuffer[pos++] = Str2byte(cd);
    162     }
    163     fclose(in);
    164     //for (int i = 1; i < nSize; i++)
    165     //{
    166     //    printf("%d ", pBuffer[i]);
    167     //}
    168 //    cout << endl<<"before: " << strlen(pBuffer) << endl;
    169     return OK;
    170 }
    171 
    172 int InitHead(const char *pFilename, HEAD &sHead)
    173 {
    174     //初始化文件头
    175     strcpy(sHead.type, "HUF");//文件类型
    176     sHead.length = 0;//原文件长度
    177     for (int i = 0; i < SIZE; i++)
    178     {
    179         sHead.weight[i] = 0;
    180     }
    181     FILE *in = fopen(pFilename, "rb");
    182     int ch;
    183     while ((ch = fgetc(in)) != EOF)
    184     {
    185         sHead.weight[ch]++;
    186         sHead.length++;
    187     }
    188     fclose(in);
    189     in = NULL;
    190     return OK;
    191 }
    192 
    193 int WriteFile(const char*pFilename, const HEAD sHead, const char * pBuffer, const int nSize)
    194 {
    195     //生成文件名
    196     char filename[256] = { 0 };
    197     strcpy(filename, pFilename);
    198     strcat(filename, ".huf");
    199     //以二进制流形式打开文件
    200     FILE *out = fopen(filename, "wb");
    201     //写文件头
    202     fwrite(&sHead, sizeof(char), 1, out);
    203     //写压缩后的编码
    204     fwrite(pBuffer, sizeof(char), nSize, out);
    205     //关闭文件,释放文件指针
    206     fclose(out);
    207     out = NULL;
    208     cout << "生成压缩文件:" << filename << endl;
    209     int len = sizeof(HEAD) + strlen(pFilename) + 1 + nSize;
    210     return len;
    211 }

    主函数Main.cpp

    //Main.cpp
    
    #include"huffman.h"
    #include"Compress.h"
    #include<iostream>
    #include<cstdlib>
    using namespace std;
    #pragma warning( disable : 4996)
    
    int main()
    {
        cout << "= = = = = = = =Huffman 文件压缩= = = = = = = =" << endl;
        cout << "请输入文件名:";
        char filename[256];
        cin>>filename;
        Compress(filename);
    
    
    //    system("pause");
        return 0;
    }
  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/sgawscd/p/10774386.html
Copyright © 2011-2022 走看看