第五节 词典编码
有许多场合,开始时不知道要编码数据的统计特性,也不一定允许我们事先知道它们的统计特性。因此,人们提出了许许多多的数据压缩方法,企图用来对这些数据进行压缩编码,在实际编码过程中以尽可能获得最大的压缩比。这些技术统称为通用编码技术。词典编码(Dictionary Encoding)技术就是属于这一类,这种技术属于无损压缩技术。
一.词典编码分类
词典编码的根据是数据本身包含有重复代码序列这个特性。例如文本文件(码词表示字符)和光栅图像(码词表示像素)就具有这种特性。
词典编码法的种类很多,归纳起来分为两类。
第一类词典法的想法是企图查找正在压缩的字符序列是否在前面的输入数据中出现过,如果是,则用指向早期出现过的字符串的“指针”替代重复的字符串。这种编码思想如图03-05-1所示。
图03-05-1 第一类词典法编码概念
这里的“词典”是隐含的,指用以前处理过的数据。这类编码中的所有算法都是以Abraham Lempel和Jakob Ziv在1977年开发和发表的算法(称为LZ77算法)为基础。此算法的一个改进算法是由Storer和Szymanski在1982年开发的,称为LZSS算法。
第二类算法的想法是企图从输入的数据中创建一个“短语词典(dictionary of the phrases)”。编码数据过程中当遇到已经在词典中出现的“短语”时,编码器就输出这个词典中的短语的“索引号”,而不是短语本身。这个概念如图03-05-2所示。
图03-05-2 第二类词典法编码概念
A.Lempel和J.Ziv在1978年首次发表了介绍这种编码方法的文章,称为LZ78。在他们的研究基础上,Terry A.Welch在1984年发表对这种编码算法进行了改进的文章,并首先在高速硬盘控制器上应用了这种算法。因此后来把这种编码方法称为LZW(Lempel-Ziv Walch)压缩编码。
二.LZ77算法 1977年,Jacob Ziv和Abraham Lempel描述了一种基于滑动窗口缓存的技术,该缓存用于保存最近刚刚处理的文本(J. Ziv and A. Lempel, “A Universal Algorithm for Sequential Data Compression”, IEEE Transaction on Information Theory, May 1977)。这个算法一般称为IZ77。 LZ77和它的变体发现,在正文流中词汇和短语(GIF中的图像模式)很可能会出现重复。当出现一个重复时,重复的序列可以用一个短的编码来代替。压缩程序扫描这样的重复,同时生成编码来代替重复序列。随着时间的过去,编码可以重用来捕获新的序列。算法必须设计成解压程序能够在编码和原始数据序列推导出当前的映射。 在研究LZ77的细节之前,先看一个简单的例子(J. Weiss and D. Schremp, “Putting Data on a Diet”, IEEE Spectrum, August 1993)。考虑这样一句话: the brown fox jumped over the brown foxy jumping frog 这个短语的长度总共是53个八位组 = 424 bit。算法从左向右处理这个文本。初始时,每个字符被映射成9 bit的编码,二进制的1跟着该字符的8 bit ASCII码。在处理进行时,算法查找重复的序列。当碰到一个重复时,算法继续扫描直到该重复序列终止。换句话说,每次出现一个重复时,算法包括尽可能多的字符。碰到的第一个这样的序列是the brown fox。这个序列被替换成指向前一个序列的指针和序列的长度。在这种情况下,前一个序列的the brown fox出现在26个字符之前,序列的长度是13个字符。对于这个例子,假定存在两种编码选项:8 bit的指针和4 bit的长度,或者12 bit的指针和6 bit的长度。使用2 bit的首部来指示选择了哪种选项,00表示第一种选项,01表示第二种选项。因此,the brown fox的第二次出现被编码为 <00b><26d><13 d >,或者00 00011010 1101。 压缩报文的剩余部分是字母y;序列<00b><27d><5 d >替换了由一个空格跟着jump组成的序列,以及字符序列ing frog。 图03-05-3演示了压缩映射的过程。压缩过的报文由35个9 bit字符和两个编码组成,总长度为35 x 9 + 2 x 14 = 343比特。和原来未压缩的长度为424比特的报文相比,压缩比为1.24。
图03-05-3 LZ77模式例 (一)压缩算法说明 LZ77(及其变体)的压缩算法使用了两个缓存。滑动历史缓存包含了前面处理过的N个源字符,前向缓存包含了将要处理的下面L个字符(图03-05-4(a))。算法尝试将前向缓存开始的两个或多个字符与滑动历史缓存中的字符串相匹配。如果没有发现匹配,前向缓存的第一个字符作为9 bit的字符输出并且移入滑动窗口,滑动窗口中最久的字符被移出。如果找到匹配,算法继续扫描以找出最长的匹配。然后匹配字符串作为三元组输出(指示标记、指针和长度)。对于K个字符的字符串,滑动窗口中最久的K个字符被移出,并且被编码的K个字符被移入窗口。 图03-05-4(b)显示了这种模式对于我们的例子的运行情况。这里假定了39个字符的滑动窗口和13个字符的前向缓存。在这个例子的上半部分,已经处理了前面的40个字符,滑动窗口中是未压缩的最近的39个字符。剩下的源字符串在前向窗口中。压缩算法确定了下一个匹配,从前向窗口将5个字符移入到滑动窗口中,并且输出了这个匹配字符串的编码。经过这些操作的缓存的状态显示在这个例子的下半部分。
(a)通用结构
(b)例子 图03-05-4 LZ77模式 尽管LZ77是有效的,对于当前的输入情况也是合适的,但是存在一些不足。算法使用了有限的窗口在以前的文本中查找匹配,对于相对于窗口大小来说非常长的文本块,很多可能的匹配就会被丢掉。窗口大小可以增加,但这会带来两个损失:(1)算法的处理时间会增加,因为它必须为滑动窗口的每个位置进行一次与前向缓存的字符串匹配的工作;(2)<指针>字段必须更长,以允许更长的跳转。 (二)压缩算法描述 为了更好地说明LZ77算法的原理,首先介绍算法中用到的几个术语:
LZ77编码算法的核心是查找从前向缓冲器开始的最长的匹配串。算法的具体执行步骤如下:
例:待编码的数据流如表03-05-1所示,编码过程如表03-05-2所示。现作如下说明:
表03-05-1 待编码的数据流
表03-05-2 编码过程
(三)解压算法 对于LZ77压缩文本的解压很简单。解压算法必须保存解压输出的最后N个字符。当碰到编码字符串时,解压算法使用<指针>,和<长度>,字段将编码替换成实际的正文字符串。
|