哈夫曼编码实现图片压缩与解压

本文介绍了使用哈夫曼编码进行图片压缩和解压的流程。首先,通过读取文件获取权值列表,构建哈夫曼树,并生成编码。接着,将编码写入.huf压缩文件,包括文件头信息。在解压时,重建哈夫曼树,根据编码读取并写入解压文件。关键在于权值数组的设计以及利用位运算处理编码的存储和读取。代码中包含了BitIO和HuffmanTree的相关实现。
摘要由CSDN通过智能技术生成
  1. 整体思路:
  • 读原文件,获得权值列表,生成哈夫曼树,遍历树生成哈夫曼编码。
  • 写入压缩文件,后缀.huf,先写入文件头(包括文件类型,权值列表,文件长度,以便于从压缩文件中再次生成哈夫曼编码来解压,也可以用其他的方式,比如直接将哈夫曼编码以先序遍历的方式写入压缩文件的开始,代码中有体现)。
  • 将原文件编码后写入压缩文件。
  • 读取压缩文件,生成哈夫曼树,每次读取到0则向左子树移动,读取到1向右子树移动,直到遇到叶子结点,将叶子结点存储的编码写入解压文件。
  • 值得注意的的是,权值数组的运作。图片文件按二进制读取,一个字节一个字节读,那么每个字节有8位二进制,则用unsigned char读取,最多只会有0~255种可能的值,即权值数组的大小设成256。
  1. 数据结构并没有使用真正意义上的树,即包括指向结点的指针,而使用一个结点数组来保存树,结点中存储的左右孩子和双亲都是数组下标。~然而这样非常难用,在需要遍历树的地方代码异常恶心~,同时哈夫曼树的一个性质是总结点数 = 叶子结点数 * 2 - 1,这样得到了开辟的存储树的数组大小。
  2. 其中用哈夫曼编码将原文件编码时,不能直接将编码(0101…)按1个char(即一个字节的大小)存1位编码来写入压缩文件,否则文件反而会变更大,而是用1个char的8位(即1byte中的8bits)来存8位的编码。在代码中有BitIO.h专门定义了做这些位运算的结构和函数,也有HuffmanTree.h中定义了Str2Byte函数,也有#define定义的GET_BYTE等函数。这三处功能是基本相同的,实现各有不同,这里我使用的是较简单的Str2Byte。

代码的注释比较详细

BitIO.h

#ifndef HUFFMANCOMPRESSCPRO1_BITIO_H
#define HUFFMANCOMPRESSCPRO1_BITIO_H

#include <iostream>
#define BITBUFFSIZE 1024
#define SHIFT 3
struct BIT{
   
    char b[BITBUFFSIZE];//位数组 bit数组
    int p; //指示数组填到哪一位的下一位
};

//向位数组栈顶推入一位
bool pushBit(BIT *buffer, const bool istrue);

//从文件加载多位
int fPushBit(BIT* buffer, FILE* fp);

//修改position位置的一位
int changeBit(BIT* buffer, const int istrue, const int position);

//读取一位
int readBit(BIT* buffer, const int position);

//栈顶弹出一位
int popBit(BIT* buffer);

#endif //HUFFMANCOMPRESSCPRO1_BITIO_H

BitIO.cpp

#include "BitIO.h"
bool pushBit(BIT *buffer, const bool istrue){
   
    if(buffer->p >= BITBUFFSIZE * 8)
        return EOF;
    else if(istrue)
        buffer->b[buffer->p >> SHIFT] |= 128u >> buffer -> p % 8; //p所指位置填1
    else
        buffer->b[buffer->p >> SHIFT] &= ~(128u >> buffer -> p % 8); //p所指位置填0
    buffer->p++;
    return istrue;
}
int fPushBit(BIT* buffer, FILE* fp){
   
    memset(buffer, 0, sizeof(BIT));
    if(buffer -> p = fread(buffer->b, sizeof(char), BITBUFFSIZE, fp) && feof(fp)){
   
        buffer->p = (buffer->p - 2) * 8 + buffer -> b[buffer -> p - 1] + 1;
    } else
        buffer -> p *= 8;
    return buffer -> p;
}
int changeBit(BIT* buffer, const int istrue, const int position){
   
    if(position >= buffer -> p)
        return EOF;
    else if (istrue)
        buffer -> b[position >> SHIFT] |= 128u >> position % 8;
    else
        buffer -> b[position >> SHIFT] &= ~(128u >> position % 8);
    return istrue;
}
int readBit(BIT* buffer, const int position){
   
    if(position >= buffer -> p)
        return EOF;
    else
        return buffer->b[position >> SHIFT] & (128u >> position % 8);
}
int popBit(BIT* buffer){
   
    if(buffer -> p >= BITBUFFSIZE || buffer -> p < 0)
        return EOF;
    buffer->p--;
    return buffer->b[(buffer->p + 1) >> SHIFT] & (128u >> (buffer->p + 1) % 8);
}

HuffmanTree.h

#ifndef HUFFMANCOMPRESSCPRO1_HUFFMANTREE_H
#define HUFFMANCOMPRESSCPRO1_HUFFMANTREE_H
#include <string>
#include <iostream>
#include "BitIO.h"
#define SIZE 256
#define NODES 2*SIZE - 1

//各种各样的编码方法
//取出index位,若取出的index位为0,则GET_BYTE值为假,否则为真
#define GET_BYTE(vByte, index) (((vByte) & (1 << ((index) ^ 7))) != 0)
//把index位设置为‘1’
#define SET_BYTE(vbyte, index) ((vbyte) |= (1 << ((index) ^ 7)))
//把index位设置为‘0’
#define CLR_BYTE(vbyte, index) ((vbyte) &= (~(1 << ((index) ^ 7))))

//n个叶子结点的哈夫曼树共有2n-1个结点
//一个字节8位 读取文件的char可以从0-255 用weight[]数组记录每个char出现的频率
//HuffmanTree 保存在HTNode[2n-1]的数组中
struct Node{
   
    bool isLeaf();
    int ch = 0;
    int weight = 0;
    int lChild = 0, rChild = 0, parent = 0;
};
typedef Node HTNode, *HuffTree;

struct HEAD{
   
    char type
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值