【数据压缩】Huffman编解码器

本文介绍了Huffman编码的基本原理,包括编码方法和基于概率统计的优化策略。实验中通过递归的中序遍历构建Huffman树,并分析了数据结构和getopt()函数的应用。此外,还详细阐述了实验流程,包括文件频率统计、码字生成和编码输出。最终,实验结果显示了对moon.yuv文件的高效压缩。
摘要由CSDN通过智能技术生成

一.实验原理

1.Huffman编码

1)Huffman Coding(霍夫曼编码)是一种无失真编码的编码方式,Huffman编码是可编长编码(VLC)的一种。
2)Huffman编码基于信源的概率统计模型,它的基本思路是:出现概率大的信源符号编短码,出现概率小的编长码。从而实现平均码长最小。
3)在程序实现中常使用一种叫做树的数据结构实现Huffman编码,由它编出的码是即时码。

2.Huffman编码的方法

2.1 统计符号的发生概率;
2.2 把频率从小到大的顺序排列
2.3 每一次选出最小的两个值,作为二叉树的两个叶子节点,将和作为它们的根节点,这两个叶子节点不再参与比较,新的根节点参与比较;
2.4 重复3,知道最后得到和为1的根节点。
2.5 将形成的二叉树的左节点标为0,右节点标为1,把从最上面的根节点到最下面的叶子节点途中遇到的0,1序列串起来,就得到了各个符号的编码。

3.实验中遇到的递归式遍历方法

  • 先序遍历
    先序遍历

  • 中序遍历
    中序变绿

  • 后序遍历
    后序遍历

    综上,Huffman编码结构中, build_symbol_encoder()的函数,实现功能为:对存在的每个字符计算码字。该函数使用的遍历方法应该为递归”中序遍历”;假设已经遍历到最左边的叶节点,由if判断到叶节点的子节点为空,故Return。此时输出一个码子,然后进入下一个右节点。其对节点的遍历顺序应为,中序遍历。仅单纯观察代码易混淆为先序遍历

     if(subtree == NULL)
        return;
    
    if(subtree->isLeaf)
        (*pSF)[subtree->symbol] = new_code(subtree);
    else
    {
        //如果不是叶字节点,就需要往下遍历
        build_symbol_encoder(subtree->zero, pSF);//先遍历其左子树
        build_symbol_encoder(subtree->one, pSF);//再遍历其右子树
    }

4.实验中所用到的数据结构分析

 typedef struct huffman_node_tag
{
    unsigned char isLeaf;//判断该节点是否是叶节点的标志
    unsigned long count;//信源中出现频数
    struct huffman_node_tag *parent;//父节点指针

    union
    {
        struct
        {
            struct huffman_node_tag *zero, *one;
        };
        unsigned char symbol;
    };
} huffman_node;

huffman_node节点中的有union数据结构,这么设计的优点是节约了空间。当节点为叶节点时,必定不包含子节点指针,当节点非叶节点时,必存在子节点指针。

typedef struct huffman_code_tag
{
    /* The length of this code in bits. */
    unsigned long numbits;

    /* The bits that make up this code. The first
       bit is at position 0 in bits[0]. The second
       bit is at position 1 in bits[0]. The eighth
       bit is at position 7 in bits[0]. The ninth
       bit is at position 0 in bits[1]. */
    unsigned char *bits;
} huffman_code;

huffman_code是Huffman码表数据结构,用以储存符号对应的码字。

    typedef struct huffman_info_tag
{
    double freq;//符号出现频率
    unsigned long len_code;//符号对应的码长
    unsigned char *code;//符号对应的码字
}huffman_info;

huffman_info数据结构,用于储存每个符号的符号频率、码长、码字信息。用于压缩后统计压缩数据,计算压缩效率,以及验证压缩正确性。

  • 分别定义三个数组,数组中储存的是指向结构体的指针。
 #define MAX_SYMBOLS 256
typedef huffman_node* SymbolFrequencies[MAX_SYMBOLS];
typedef huffman_code* SymbolEncoder[MAX_SYMBOLS];
//cai add 
typedef huffman_info* SymbolInfo[MAX_SYMBOLS];

举例,SymbolFrequencies[MAX_SYMBOLS]储存着256个指向huffman_node这个结构体的指针。

5.getopt()函数简单分析

传入的参数,例如:"-i input.rgb -o output.rgb -c"  "i"与“o”在下列注释中称为key,其对应的“input.rgb”称为value。
以及调用getopt时,opt = getopt(argc, argv, "i:o:cdhvm")。"i:o:cdhvm"在下列注释中称为规范字符串
extern char *optarg;
extern int optind;
extern int opterr;

int opterr = 1,     /* if error message should be printed */
    optind = 1,     /* index into parent argv vector */
    optopt,         /* character checked for validity */
    optreset;       /* reset getopt */
char    *optarg;        /* argument associated with option */

#define BADCH   (int)'?'
#define BADARG  (int)':'
#define EMSG    ""
int getopt(int nargc, char * const *nargv, const char* ostr)
{
    static char *place = EMSG;      /* place向量先置为空 */
    char *oli;              /* 用于指向 “规范字符串”的指针 */

    if (optreset || !*place) {      /* 如果optreset为1,或者place指向空,都会进入这个判断 */
        optreset = 0;               /*进入后,将optreset重置为0*/
        if (optind >= nargc || *(place = nargv[optind]) != '-') {       /*判断optind是否比总argc数大,以及argv[opind]的第一个字符是否为'-' .*/
            place = EMSG;                                               /*若满足其上任一项,说明字符读取到了最后一步,或者输入有误*/
            return (EOF);                                               /*将place指向 argv[optind]*/
        }
        if (place[1] && *++place == '-') {  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值