gif编解码原理

lzw编码:

/***************************************************************************************************
以下摘自Hevcer的csdn博客:
LZW是一个字典式压缩算法,他在压缩原始数据时,对每一个新出现的原始数据串赋一个数值作为标号,那么下次又出现了这个串后,就可以用这个值来代替了。比如
 原始数据:   ABCCAABCDDAACCDB      ,
ABCD可以用0~3的数来表示。那么注意这个字符串中出现了好几个重复的字串:
AB CCA ABCDDAACCDB
那么就可以用 4来代表 AB,5来代表 CC 等等,原来的字符串就变为
压缩后的数据: 45 A 4 CDDAA5DB ,
变短了一点点。实际上上面这个字符串号可以进一步的压缩,等会再谈。
 
为了区别代表串的值和原来的单个的数 据值,需要使它们的数值域不重合,比如说原来的数我们是以8位为单位来处理的(就算实际上不是8位的,我们也可以看作是8位的,反正是一个0101的数据 流),那么就认为原始的数的范围是0~255,压缩程序生成的标号的范围就不能为0~255,可以从256开始,但是这样一来就超过了8位的表示范围了, 所以 LZW 算法必须要扩展数据的位数,至少要扩一位,这样看起来不是反而是增加了数据流的体积了吗?不过如果能用一个数据代表一个原始数据串,那么还是划得来的。从这个原理也可以看出LZW算法的适用范围, 那就是原始数据串最好是有大量的子串多次重复出现,重复的越多,压缩效果越好。反之则越差,可能真的不减反增了
LZW算法在处理数据时,随着新的串的不断发现,标号的值也就不断增加,增加到一定的程度,出与查找效率和标号集(也就是字典)所需存储空间的考虑,就不能再让它增加了,那么怎么办呢?干脆就从头开始,在这里做一个标记( 清除标志 CLEAR),表示从这里我 重新开始构造字典字典了,以前的所有标记作废,开始使用新的标记。这个标号集的大小多少比较合适呢?据说理论上是越大压缩率越高(我个人感觉太大了也不见得就好),不过处理的开销也呈指数增长,  一般都是根据处理速度和内存空间选定一个大小,GIF规范规定的是12位,超过12位的表达范围就推倒重来,并且GIF为了提高压缩率,采用的是变长的字 长。比如说原始数据是8位,那么一开始,先加上一位再说,开始的字长就成了9位,然后开始加标号,当标号加到512时,也就是超过9为所能表达的最大数据 时,也就意味着后面的标号要用10位字长才能表示了,那么从这里开始,后面的字长就是10位了。依此类推,到了2^12也就是4096时,在这里插一个清 除标志,从后面开始,从9位再来。
GIF规定的清除标志 CLEAR 的数值是原始数据字长表示的最大值加 1,如果原始数据字长是8清除标志就是256,如果原始数据字长为4那么就是16。另外GIF还规定了一个 结束标志 END ,它的值是清除标志 CLEAR 再加 1。由于GIF规定的位数有1位(单色图),4位(16色)和8位(256色),而1位的情况下如果只扩展1位,只能表示4种状态,那么加上一个清除标志和结束标志就用完了,所以1位的情况下就必须扩充到3位。其它两种情况初始的字长就为5位和9位。
 
好了,现在开始谈谈LZW的具体算法。前面已经说了,LZW是基于字典的压缩方法,那么这个字典是怎么来的呢?难道先编一本“大百科字典”,随压缩包免费奉送?这显然是不可能的。LZW算法的优点就是可以 动态生成字典,并且这个字典的信息已经包含在压缩后的数据流中了,不必再另外储存字典信息了。下面以一个压缩过程为例来说明一下。这个例子是Bob Montgomery给出的,非常的经典,我这里充当一个翻译的工作,并稍微加一点我的解释。
比如有一个字符串,是由A、B、C、D四个字符构成的,那么就可以用0 1 2 3 来表示,两位就够了。
A B A B A B A B B B A B A B A A C D A C D A D C A B A A A B A B .....
首先要扩充一位,变成3位,定义 Clear=4,End=5。那么以后的标号就从6开始。
第一步,取第一个字符,是A,A已经在我们的定义中了,也就是说,我们已经(认识)他了,就不做处理了。
下一步,取第二个字符,现在的取到的字符串为 A B ,注意,这里引入一个 前缀( prefix 后缀( suffix 的概念,一个字符串可以用一个前缀加一个后缀来表示即 (前缀,后缀)前缀是一个标号,可以是原始的字符,也可以是一个代表字符串的标号,后缀则是一个字符。那么这里取到了( A A ),以前没见过,不认识,好,现在 6 代表( A B ), 下次就认识了。因为有不认识的了,所以把前缀  A  放入到输出流中,只保留后缀  B  ,让它变成前缀。
第三步,取下一个字符,是  A ,现在的字符串是( B A ),还是不认得,用一个新标号来表示他, 7 =( B A )。把前缀  B  放入到输出流, 变成前缀。
第四步,取下一个字符,是  B  ,那么取道的是( A B ),哈,这次认得了,不就是老 6 么。好,把字符串规约到  6 ,以 6 来作为前缀。
第五步,取下一个,是 ,即为( 6 A ),又不认得了,就令 8 =( 6 A ),把前缀  6 放到 输出流。现在的前缀变成  A 了。 注意 ,到这里标号已经超过 3 位能够表示的最大范围了,所以接下来必须要 扩展数据位 ,那么接下来的数据就是以 4 位字长来表示了。
接下来的流程不一一讲述了,如图所示
A B A B A B A B B B A B A B A A C D A C D A D C A B A A A B A B ....输入流.
\/\/---/-----/\/---/-------/\/ \/ \ /\/---/---/\ /\/-----/---/---/
6 7   8     9 10 11      12 13 14 15 16 17 18 19 20   21 22 23     标号
    6    8      10    9                14 16        8    13 7       前缀
 
 
 
 
 
 
Color    Code      Prefix    Suffix    String    Output 位数
 A        0                             -
 B        1                             -
 C        2                             -
 D        3                             -
Clear     4                             -
End       5                             -
 A \                A         A                   First color is a special case.
 B / \   6         A         B         AB        A                3
 A | /   7         B         A         BA        B                3
 B |
 A / |   8         6         A         ABA       6                3
 B    |
 A    |
 B \ /   9         8         B         ABAB      8                4
 B / |   10        B         B         BB        B                4
 B    |
 A | /   11        10        A         BBA       10              
 B |
 A |
 B |
 A / \   12        9         A         ABABA     9
 A \ /   13        A         A         AA        A
 C / \   14        A         C         AC        A
 D \ /   15        C         D         CD        C
 A / |   16        D         A         DA        D                4
 C    |
 D | /   17        14        D         ACD       14              5
 A |
 D / \   18        16        D         DAD       16
 C \ /   19        D         C         DC        D
 A / |   20        C         A         CA        C
 B    |
 A    |
 A | /   21        8         A         ABAA      8
 A |
 B / |   22        13        B         AAB       13
 A    |
 B    /   23        7         B         BAB       7
输出数据流为: A B 6 8 B 10 9 A A C D 14 16 D C 8 ....

*****************************************************************************************************/
以下为笔者自述:

LZW解码:

lzw解码过程中的要点:

    因为编码过程是动态的,编码长度慢慢变成,所以解码过程中,在读取编码流的时候要注意读取的位数.例如GIF色深是4,那么每次 要从GIF中读取5位开始解码,等到编码表写到32项的时候,每次要从GIF读6位开始解码,依次类推直到读到12位,编码最大长度.

lzw解码过程:

    clear(清除标志):假设B=原始码位数,N=2^B,那么初始化码表(字典)从0-N-1项,清除标志位数N,遇到清除标志,则整个码表要被复位,下一个解码要读B+1位.

    块结束标志:N+1,表示读到一副图像编码的末尾,本帧图像解码结束.


解码流程(接上例A B 6 8 B 10 9 A A C D 14 16 D C 8 13 7):

    1,色深位2,则每次读3位码流,读码字A,则本次前缀prefix=A,因为有输出,必然会添加字典,字典从6开始添加,则,本次添加字典6(A,?),读下一码字B,注意每次输出为前缀,即为上一次的后缀,所以,字典6(A,B),此次prefix=B,添加字典7(B,?);

    2,下一个读出为6(字典7出现,开始读4位),prefix=(AB),suffix=?,添加字典8(AB,?),显然(AB)不能作为上次编码后缀(后缀只能位单个字符),所以上一次编码无输出,上次编码suffix=B,则prefix=A,A只有一位,则可作为上上次编码后缀,suffix=A,补齐字典7(B,A);

    3,下一个读出为8,8为(AB,?),AB不能作为后缀,所以上次编码无输出,(AB)为字符串,suffix=B,prefix=A,A可以作为后缀,所以上上次string为8(AB,A),下次string(A,?),此次9=string(ABA,?);

    4,下一个读出为B,string(B,?),上次string(?,B),由过程3知道上次string(A,B);

    5,......................

如果不理解上述所讲,则看如下表格:

解码过程
字典67891011121314151617181920212223
读码流位数334444444455555555
读出码值AB68B109AACD1416DC8137
上次字符串(-,B)(A,B)(A,B)(AB,?)(8,B)(10)(9)(9,A)(A,A)(A,C)(C,D)(14)(16)(16,D)(D,C)(8)(13)(7)
本次字符串(A,?)(B,?)(AB,?)(AB?,?)(B,?)(10,?)(9,?)(A,?)(A,?)(C,?)(D,?)(14,?)(16,?)(D,?)(C,?)(8,?)(13,?)(7,?)
得出字典值 6=AB7=BA8=ABA9=ABAB10=BB11=BBA12=ABABA13=AA14=AC15=CD16=DA17=ACD18=DAD19=DC20=CA21=ABAA22=AAB

    提示:此解码表要一列一列看,每一列有且只能得出上一个字典值,后缀只能位单个字符;根据字典编号读相应的位数;如不理解此表,请对照编码流程自行思考.


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
一个可以实现GIF编码的动态链接库DLL文件GIFINFO.dll。 引用GIFINFO.dll文件后。 在程序中定义GIFINFO类实例,如GIFEncode,然后调用实例的GIFCode方法,就可以实现图像信息的GIF编码。 它的格式如下: Function GIFCode(PicInfo() As Long,FileName As String) 参数PicInfo是长整型数组,它存放你的图片像素颜色信息,每个数组元素都对应一个RGB()类型长整型值。 如6*6的图像, RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) FileName是你要保存的图像文件名信息,要求是以.GIF结尾的,如App.Path & "abc.gif" 这个函数执行之后,你就可以在你指定的位置找到你压缩的GIF图像了。 当然你可以要求压缩带透明区域的GIF图像,方法是将透明区域的数组元素值设置为-1。 如6*6大小的图像用的PicInfo()数组中的一些值是-1 -1 -1 -1 RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) -1 -1 -1 RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) -1 -1 -1 RGB(0,255,0) RGB(0,255,0) RGB(0,255,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0) RGB(255,0,0)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值