首先道个歉:昨天写着写着突然熄灯,以至于写了80%的这篇文章不得不灰飞烟灭,我可怜的电脑也被裸关一次,没有办法,校有校规啊,所以只好今天从头开始再写一遍。
言归正传。
写着篇文章之前,我县假定你已经对于什么是GIF很清楚了,如果你不清楚的话,建议你到网上搜索一下“GIF文件简介”看看。
GIF作为一种面向网络的图像格式,他的种种设计都是以“便于在网络上使用”为原则的。网络使用的第一个原则就是文件大小。如何使用有效的压缩算法使文件的大小得以减小,是GIF文件要解决的首要问题。对于这个问题,GIF采用的是压缩效率很好的LZW压缩算法。这个算法的压缩效率、压缩比都是极高的,但是有一点不好:它是有版权的。
因此我的第一部分,是介绍LZW算法,与GIF文件格式的内容无关,只是一种算法,但是它是LZW-GIF压缩算法(GIF使用的压缩算法)的基础。
1、概述
LZW算法的基本思想很简单:在一个输入的未压缩的字节流中,一定有很多的重复出现的字符串,那么用一个字节来表示这个字符串,不就起到了压缩的作用了吗?
下面我们先看几个概念,这几个概念将会在下面的介绍中反复出现,因此请记住他们。
Char Stream(未编码的字节流):作为编码的输入和解码的输出,使未经压缩的字节流,我们要处理的对象。
Code Stream(已编码的流):我没有写作字节流,因为不一定输出的是字节,事实上大多数时候也不是字节。
String Table(编码表):作为编码和解码的字典,它的格式是一个index对应一个string,在初始化的时候,从0开始的index依次对应字符集中的相应的字符。LZW的编码表有一个很大的优势就是他不需要很大的存储空间,它是在编码、解码的过程中动态生成的,而生成他指需要一个变量:初始的根项的数目。
Root Item(编码表根项):生成编码表时初始的只有一个字符的那些项,他们有一系列的特点,我在算法描述的时候再详述。
好了,概念说完了。现在开始介绍编码的算法:
2、编码算法
首先是算法的伪码描述:
initial_string_table(root_number);
current_prefix = '' ; //空字符串
while (能够从Char Stream中取出一个Char c) {
current_string = current_prefix+c ;
if (current_string在string_table中存在) {
current_prefix = current_string;
} else {
把current_string加入到string_table的末端;
向Code Stream中输出current_prefix在string_table中对应的index;
current_prefix = c;
}
}
向Code Stream中输出current_prefix在string_table中对应的index;
// 编码结束
现在我们看看每一步是怎么做的:
第一步:初始化我们的编码表。刚才说了,初始化编码表只需要一个参数:root_number,也就是根项的数目。其实他就是我们要编码的Char Stream中的字符集的数目。初始化也很简单:根据一个事先的约定对所有的Char进行排序,然后从零开始对其编上一个index,形成一个item,再把这些items从小到大排列好,就形成了初始的编码表。
第二步:初始化一些局部变量:主要是current_prefix和current_string,把他们都设为空字符串。
第三步:开始循环编码,方法如下:
从Char Stream中读出一个Char c,然后把它接到current_prefix的后面形成current_string。
在string_table中进行查找,看有没有这个current_string。
有:说明这个current_string是“老的”,于是什么都不做,仅仅把current_string赋给current_prefix;
无:说明这个current_string是“新的”,因此第一步把它变成“老的”,也就是把它插入到string_table的末端;紧接着输出current_prefix在string_table中的index到Code Stream;最后重新开始找重复的字符串,也就是把c赋给current_prefix。
这样循环结束,最后肯定还余下一个prefix,输出它的index即可。
举个例子吧。
假设我们的Code Stream是BBBCBBA,字符集是{A,B,C},我们约定排列方式是ASCII升序。
初始化的string_table如下:
-----------------------
index string
00 'A'
01 'B'
02 'C'
-----------------------
这三项就是所谓的Root Items。
然后开始编码:简单期间,我只是列出各个量的值,并作简单说明。
Step Prefix Char c CurrentString Exist? Operation Code Stream(OUT)
01 [] B []B=B yes no X
02 [B] B [B]B=BB no insert BB index(B) = 01
03 [B] B [B]B=BB yes no X
04 [BB] C [BB]C=BBC no insert BBC index(BB)=03
05 [C] B [C]B=CB no insert CB index(C)=02
06 [B] B [B]B=BB yes no X
07 [BB] A [BB]A=BBA no insert BBA index(BB)=03
08 [A] no
Output [A]'s index: index(A)=00
这样,编码结束了,Code Stream为:01 03 02 03 00
最终的编码表如下:
-------------------------
index string
00 'A'
01 'B'
02 'C'
03 'BB'
04 'BBC'
05 'CB'
06 'BBA'
-------------------------
由此可见,编码压缩率的大小,取决于其中重复字符串的多少。
编码就讲到这里,下面是解码。
(To be continue...)
GIF图像格式(一)——基础算法(上)
最新推荐文章于 2026-04-21 08:25:19 发布
本文介绍了GIF图像格式所采用的LZW压缩算法,该算法以高效率和高压缩比著称,但存在版权问题。首先讲解了LZW算法的基本思想和关键概念,如Char Stream、Code Stream和String Table。接着详细阐述了编码过程,包括初始化编码表、循环编码的步骤,并通过实例展示了编码过程,强调了编码效率与重复字符串数量的关系。下篇将讲述解码算法。
6726

被折叠的 条评论
为什么被折叠?



