[实验]无失真信源压缩编码

本文详细介绍了无失真信源压缩编码的实验,包括Huffman编码、游程编码、算术编码和字典码的原理、过程及结合应用。通过实验分析,得出字典码在无失真压缩编码中表现最佳,尤其适用于中文文本。实验展示了不同编码方法对英文和中文文本的压缩效果,并提出改进字典码以提高中文文本压缩效率的建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验一 无失真信源压缩编码


此文系后续整理,懒得copy,对应的方法可以根据需要可以直接下载代码,附件:

https://download.youkuaiyun.com/download/weixin_40744915/10296130

https://download.youkuaiyun.com/download/weixin_40744915/10296133

https://download.youkuaiyun.com/download/weixin_40744915/10296135

https://download.youkuaiyun.com/download/weixin_40744915/10296154


目录

一、实验目的及要求... 1

二、任务分析及方案选择... 1

(一)Huffman编码... 1

(二)游程编码... 1

(三)算术编码... 2

(四)字典码... 3

三、实验原理及过程... 5

(一)Huffman编码实现信源无失真压缩... 5

(二)游程编码与Huffman编码结合实现信源无失真压缩... 13

(三)字典码实现信源无失真压缩... 18

四、实验结果分析与思考... 20

(一)Huffman编码实现信源无失真压缩... 20

(二)游程编码与Huffman编码结合实现信源无失真压缩... 23

(三)字典码实现信源无失真压缩... 24

五、实验小结... 27

 


 


一、实验目的及要求

1、对文本信源,寻求最佳压缩方案,现完整的无失真压缩的编译码算法,完成对文本文件的压缩及解压。

2、构建性能分析模块,实现对信源熵的统计、压缩后的传输率(bits/symbol),以及恢复文本的完整情况进行分析。

二、任务分析及方案选择

香农第一定理从理论上给出了进行无失真信源压缩的理论极值,并论证了理想最佳信源编码的存在性,也就是说,总能找到某种合适的编码方法使编码后信源的信息传输率任意逼近信源的信息熵而不存在任何失真,这样的无失真信源编码在数据压缩技术中又称为熵编码。典型的代表有霍夫曼编码、游程编码、算术编码和字典码,霍夫曼编码主要适用于多元独立的信源,游程编码和算术编码主要使用与二元信源及具有一定相关性的有记忆信源,字典码针对信源的统计特性未知时有较好的性能。本次实验给定的是一个大小较小、无记忆文本文件,所以Huffman编码是应该一个比较合适的方案,下面具体进行原理分析与比较:

(一)Huffman编码

Huffman编码是一种即时码,二元Huffman编码步骤如下:

(1)将q个信源符号按递减概率排列起来,设

(2)用0和1码符号分别分配给概率最小的两个信源符号,并将这两个概率最小的信源符号合并成一个新符号,并用这两个最小概率之和作为新符号的概率,从而得打只包含个符号的新信源,作为原信源的缩减信源;

(3)把缩减信源的符号仍按照概率大小递减排列,再将其最后两个概率最小的符号合并成一个新符号,依次递推,直至缩减信源最后只剩下两个符号为止。将这最后两个符号分别用0和1码表示,最后他们俩的概率之和为1。然后从最后一级缩减信源开始,依照编码路径从后向前返回,就得出各个信源符号所对应的码符号序列,即对应的码字。

但Huffman编码得到的码并非唯一的,因为每次对缩减信源最后两个概率最小的符号,用0还是1表示可以是任意的,所以可能得到码长相同但码字不同的码;另外当缩减信源中缩减合并后的符号的概率与其他信源符号相同时,概率次序也不一定,所以其Huffman编码并不唯一。但由于平均码长没变,所以有相同的编码效率。

后续将给出实验(一)中给出具体过程及步骤。

(二)游程编码

游程编码又称“运行长度编码”或“行程编码”,是一种统计编码,该编码属于无损压缩编码,是栅格数据压缩的重要编码方法。对于二元信源,其输出只有两个符号,‘0’或‘1’。连续出现‘1’符号或者连续出现‘0’符号的个数称为这一段的游程长度,这个游程长度是一个随机变量,可取值从1……无穷,且‘0’和‘1’游程一定是交替出现的。用自然数标记游程长度的这种映射是可逆的,无失真的,而且游程长度越长以及长游程频繁出现时压缩效率就会越高。

同样,由于通过游程的映射变换已经减弱了原来二元序列符号间的相关性,并把它变成了多元序列,这样可以在游程编码的基础上进行其他的变长编码方式,就能进一步压缩信源,提高通信效率。当然要首先测定‘0’游程长度和‘1’游程长度的概率分布,即以游程长度为元素,构造一个新的多元信源进行霍夫曼编码,得到不同游程长度映射的码字,就可以将游程序列变换成码字序列,使信源得到进一步压缩。若二元序列的概率特性已知,由于二元序列和映射所得的游程序列是一一对应的,就可以计算出游程序列的概率特性,根据信息熵的定义分别计算‘0’游程长度信源和‘1’游程长度信源的熵,以及平均游程长度,原信源的二元序列是由‘0’游程和‘1’游程交替出现而组成的,所以原二元序列的熵等于两个游程长度信源熵之和除以它们的平均游程长度之和,即游程变换后的信源信息熵没有变,当‘0’游程和‘1’游程的编码效率都很高时,采用游程编码的编码效率也很高。因此要想编码效率尽可能高,应该尽量提高熵值较大的游程的编码效率。

因此,在后续将给出游程编码和Huffman编码结合的无失真压缩编码,具体在实验(二)中给出具体过程及步骤。

(三)算术编码

虽然Huffman编码是最佳码,但是对于二元信源,必须对二元信源的N次扩展信源进行Huffman编码时,才能使得平均码长接近信源的熵,编码效率才高。必须要计算出所有N长信源序列的概率分布,并构造相应的完整的码树,所以较为复杂。算术编码可以无需计算出所有N长信源序列的概率分布及编出码表,可以直接对输入的信源符号序列进行编码输出,对于很长的信源符号序列来说是一个简单有效的编码方法。算术编码的基本原理是将编码的消息表示成实数0和1之间的一个间隔(Interval),消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。

算术编码用到两个基本的参数:符号的概率和它的编码间隔。信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0到1之间。编码过程中的间隔决定了符号压缩后的输出。

给定事件序列的算术编码步骤如下:

(1)编码器在开始时将“当前间隔” [ L, H) 设置为[0,1)。

(2)对每一事件,编码器按步骤a和b进行处理

a.编码器将“当前间隔”分为子间隔,每一个事件一个。

b.个子间隔的大小与下一个将出现的事件的概率成比例,编码器选择子间隔对应于下一个确切发生的事件相对应,并使它成为新的“当前间隔”。

(3)最后输出的“当前间隔”的下边界就是该给定事件序列的算术编码。

设Low和High分别表示“当前间隔”的下边界和上边界,CodeRange为编码间隔的长度,LowRange(symbol)和HighRange(symbol)分别代表为了事件symbol分配的初始间隔下边界和上边界。上述过程的实现可用伪代码描述如下:

set Low to 0

set High to 1

while there are inputsymbols do

    take a symbol

    CodeRange = High – Low

    High = Low + CodeRange *HighRange(symbol)

    Low = Low + CodeRange * LowRange(symbol)

end of while

output Low

算术码解码过程用伪代码描述如下:

get encoded number

do

    find symbol whose range straddles theencoded number

    output the symbol

    range = symbo.LowValue – symbol.HighValue

    substracti symbol.LowValue from encodednumber

    divide encoded number by range

until no more symbols

由于算术编码可以对于输入的信源符号序列不需计算概率,只需要计算累积分布函数即可进行编码输出,因此编码效率很高。当信源符号序列很长时,平均码长接近信源的信源熵。由于算数编码的效率高、编译码速度快,因此在图像压缩等方面有广泛应用,在方案设计部分给出了其实现的伪代码,由于算术编码有关信源符号序列的累积分布函数的计算是将累积分布函数写成二进位的小数,取小数后的l位(若后面有尾数,就进位到第l位)得到的,所以这样根据二进位小数截去位数可能对于精度有一定影响;算术编码也是一种对错误很敏感的编码方法,如果有一位发生错误就会导致整个消息译错,这是算术编码在实际应用中需要考虑的问题。

(四)字典码

Huffman编码、游程编码、算术编码都是基于统计思想的编码方法,但当统计参数发生了变化的时候,这种静态的统计编码就会产生性能下降。因此在1980年代提出了自适应或者半自适应的动态方法,对于工程实践中无法测定信源的统计概率或者产生统计失配时,具有较好的性能。1977-1978年由以色列的研究人员Ziv和Lempel提出的,习惯称为LZ码,由于把已经编码的字符串存储作为字典使用,又称为字典码。典型的字典码是上述二人于1977、1978年提出的LZ-77码、LZ-78码,以及1984年由韦尔奇改进的LZW码,随后又出现了对LZ码和LZW码的改进或变形的多种字典码,如LZSS码,LZRW1-4码,LZP1-4码等。这类字典码,编译简捷,易于软件实现,使其得到广泛的应用。LZ码是一种通用编码方法,就是指在信源概率统计特性不知或不存在时,对信源进行编码,而且使编码效率仍为很高的一种码。

我们先举例说明字典码的思想。设A,B两人约定在用完全相同的字典下互相通信,A将信文中的每个单词用二元标识符(a,b)代替,a,b是表示单词所在字典中的页数和在此页的位置次序,当逐个单词被替代后,就将标识符序列发给B。B收到标识符序列后可按标识符恢复单词和信文。标志符号的个数显然少于单词符号个数,就实现了文本压缩的目的。另外若对字符序列进行压缩编码,其效率要高于对单个字符的压缩。而且字符串越长,平均码长越短。在压缩编码中又常把字符串或字符序列称为数据流。

LZ-77编码算法的主要思想是把已输入的数据流存储起来,作为字典使用。编码器为输入流开设一个滑动窗口,其长可达几千字节,将输入的数据存在窗内,做字典使用,窗口右侧是待编码数据流的缓存器,长只有几十字节。用三元标识(K,Ɩ,d)给数据编码,K是窗内从尾自又向左搜索的字符位数,称为移位数,Ɩ是窗内可以与窗外有相同符号的字符串的长,称为匹配串长度,d是窗外已找到匹配的串的后面串的首字符。以自然对数2.71828182845为例:

(1)开始时,窗内是空的,要输入当前位置的2,窗内没有与2匹配的串,故K,Ɩ皆为0,把2编码为(0,0,2),输入2。下面输入的数据为.718也与2的情况相同,窗内只有刚输入的2,没有与.718分别相匹配的串,故只能将.718分别编码为(0,0,.),(0,0,7),(0,0,1),(0,0,8),分别将其输入窗内。

(2)现窗内已有2.718存入,当前位置是e的小数第4位2,在窗内从尾由右向左搜索到第5位处(包括小数点),有窗内左边的第5个数字2与当前位置2相同,即匹配串长Ɩ为1,当前位置2后面的串的首字符为8,得K=5,Ɩ=1,故编码为(5,1,8),表示窗内字典从右向左第5位有一个长为1的匹配串“2”和当前位置的数据串“2”相匹配,后面串的首字符为8,并将28同时输入。

(3)现窗内字典为2.71828,当前输入数据为1,编码器在窗内从尾自右向左搜索,移动4位,搜索到长度Ɩ=4的与当前位置相匹配的串:1828,下面串的首位为4,故编码为(4,4,4)。将18284输入窗内;

(4)由于字典内没有与5,9,0相匹配的串,故均是0移位,0匹配串长的无匹配字符,编码为(0,0,5),(0,0,9),(0,0,0),并将5,9,0输入窗内。

(5)在窗内搜索到第4位,有“45”与当前位置45匹配,串长Ɩ=2,后续段首字符为2。故编码为(4,2,2)。并又将452输入窗内。以下重复上述步骤,可逐步将数据流编码并输入窗内。所有的标识(K,Ɩ,d)序列就是所得的压缩编码序列。

LZ-77码的译码器要有一个与编码器字典窗口等长的缓存器,可以边译码,边建立译码字典,即编码字典不用传送,译码器可自动生成。这种码隐含了假设条件:输入数据产生模式是相距很近的。

因此,在后续将字典码的无失真压缩编码,具体在实验(三)中给出具体过程及步骤。

三、实验原理及过程

(一)Huffman编码实现信源无失真压缩

1、构建Huffman编码树的过程:

(1)创建n个初始化的Huffman树,每个树只包含单一的叶节点,叶节点纪录对应的字母和该字母出现的频率(weight);

(2)按照weight从小到大对其进行所有的Huffman树进行排序,取出其中weight最小的两棵树,构造一个新的Huffman树,新的Huffman树的weight等于两棵子树的weight之和,然后再加入到原来的Huffman树数组当中;

(3)反复上面的2中的操作,直到该数组当中只剩下一棵Huffman树,那么最后剩下来的那棵Huffman树就是我们构造好的Huffman编码树;

2、文本压缩过程:

(1)对需要被压缩的文件以二进制方式读入,然后以byte为单位读取里面的值,然后将其转为int值,一共有[0~255]共256个值,遍历文件所有的byte,统计出现每个值的次数,作为后面叶子节点的权重(weight);

(2)使用上面统计的每个字符对应的权重,构造对应的Huffman编码树,然后分配每个字符对应的Huffman编码;

(3)在压缩文件的开始处,将每个字符及其对应的权重(weight)以二进制的方式保存到压缩文件中去,这样在对压缩文件解压是就可以根据源文件中每个字符出现的频率去构造相应的Huffman树,便于对压缩文件进行解压缩的操作;

(4)遍历文件的每一个byte,然后以步骤(2)中生成的Huffman编码,将该字节替换为编码后对应的01组合;

(5)将步骤(4)中生成的编码后的字符串,每8位作为一个单位,转为相应的byte,然后以二进制的方式写入到压缩文件中;

python实现如下:

def compress(inputfilename,outputfilename):

    """

    压缩文件,参数有

    inputfilename:被压缩的文件的地址和名字

    outputfilename:压缩文件的存放地址和名字

    """

    #1. 以二进制的方式打开文件

    f = open(inputfilename,'rb')

    filedata = f.read()

    # 获取文件的字节总数

    filesize = f.tell()

    print('原文件大小为:%d' % filesize)

 

    # 2. 统计 byte的取值[0-255] 的每个值出现的频率

    # 保存在字典 char_freq中

    char_freq = {}

    for x in range(filesize):

        tem = filedata[x]

        if tem in char_freq.keys():

            char_freq[tem] = char_freq[tem] + 1

        else:

            char_freq[tem] = 1

 

    # 3. 开始构造原始的huffman编码树 数组,用于构造Huffman编码树

    list_hufftrees = []

    for x in char_freq.keys():

        # 使用 HuffTree的构造函数 定义一棵只包含一个叶节点的Huffman树

        tem = HuffTree(0, x, char_freq[x],None, None)

       

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值