LZW算法的PYTHON实现

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
我希望通过本文的介绍,能给那些目前不太了解 lzw 算法和该算法在 gif 图像中应用,但渴望了 解它的人一些启发和帮助。抛砖引玉而已,更希望园子里面兄弟提出宝贵的意见。 1.LZW 的全称是什么? Lempel-Ziv-Welch (LZW). 2. LZW 的简介和压缩原理是什么? LZW 压缩算法是一种新颖的压缩方法,由 Lemple-Ziv-Welch 三人共同创造,用他们的名字命 名。它采用了一种先进的串表压缩,将每个第一次出现的串放在一个串表中,用一个数字来表示 串,压缩文件只存贮数字,则不存贮 串,从而使图象文件的压缩效率得到较大的提高。奇妙的 是,不管是在压缩还是在解压缩的过程中都能正确的建立这个串表,压缩或解压缩完成后,这个 串表又被丢 弃。 LZW 算法中,首先建立一个字符串表,把每一个第一次出现的字符串放入串表中,并用一个 数字来表示,这个数字与此字符串在串表中的位置有关,并将这个数字 存入压缩文件中,如果 这个字符串再次出现时,即可用表示它的数字来代替,并将这个数字存入文件中。压缩完成后将 串表丢弃。如"print" 字符串,如果在压缩时用 266 表示,只要再次出现,均用 266 表示,并将 "print"字符串存入串表中,在图象解码时遇到数字 266,即可从串表中查出 266 所代表的字符 串"print",在解压缩时,串表可以根据压缩数据重新生成。 3.在详细介绍算法之前,先列出一些与该算法相关的概念和词汇 1)'Character': 字符,一种基础数据元素,在普通文本文件中,它占用 1 个单独的 byte,而在 图像中,它却是 一种代表给定像素颜色的索引值。 2)'CharStream':数据文件中的字符流。 3)'Prefix':前缀。如这个单词的含义一样,代表着在一个字符最直接的前一个字符。一个前缀 字符长度可以为 0,一个 prefix 和一个 character 可以组成一个字符串(string), 4)'Suffix': 后缀,是一个字符,一个字符串可以由(A,B)来组成,A 是前缀,B 是后缀,当 A 长度 为 0 的时候,代表 Root,根 5)'Code:码,用于代表一个字符串的位置编码 6)'Entry',一个 Code 和它所代表的字符串(string) 4.压缩算法的简单示例,不是完全实现 LZW 算法,只是从最直观的角度看 lzw 算法的思想 对原始数据 ABCCAABCDDAACCDB 进行 LZW 压缩 原始数据中,只包括 4 个字符(Character),A,B,C,D,四个字符可以用一个 2bit 的数表示, 0-A,1-B,2-C,3-D,从最直观的角度看,原始字符串存在重复字符:ABCCAABCDDAACCDB,用 4 代表 AB,5 代表 CC, 上面的字符串可以替代表示为:45A4CDDAA5DB,这样是不是就比原数据短了 一些呢! 5.LZW 算法的适用范围 为了区别代表串的值(Code)和原来的单个的数据值(String),需要使它们的数值域不重合,上 面用 0-3 来代表 A-D,那么 AB 就必须用大于 3 的 数值来代替,再举另外一个例子,原来的数值 范围可以用 8bit 来表示,那么就认为原始的数的范围是 0~255,压缩程序生成的标号的范围就 不能为 0~255(如果是 0-255,就重复了)。只能从 256 开始,但是这样一来就超过了 8 位的 表示范围了,所以必须要扩展数据的位数,至少扩展一位,但是这样不是增加了 1 个字符占用 的空间了么?但是却可以用一个字符代表几个字符,比如原来 255 是 8bit,但是现在用 256 来表 示 254,255 两个数,还是划得来的。从这个原理可以看出 LZW 算法的适用范围是原始数据串 最好是有大量的子串多次重复出现,重复的越多,压缩效果越好。反之则越差,可能真的不减反 增了。 6.LZW 算法中特殊标记 随着新的串(string)不断被发现, 标号也会不断地增长, 如果原数据过大, 生成的标号集 (string
..... 当进行到第 12 步的时候,标号集应该为
1 2 3 4 5 6 7 8 9 10 11
A B C D Clear End AB BA 6A 8B BB 10A
8.LZW 算法的伪代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 python 代码:
# -*- coding: gbk -*def LZW ( inStr,narrow=False,bits=12): '''使用 LZW 压缩算法压缩。 narrow 为 True 时输出字节流位紧缩 if isinstance(inStr,str)ቤተ መጻሕፍቲ ባይዱ inStr=list(inStr) for i in range(len(inStr)): inStr[i]=ord(inStr[i]) sOutStr=[256] mTagMap={} iBitsLen=9 #先放一个 开始&清除 标记 #字典 默认最大位宽 14 位,允许范围 12~16 位'''
table)会越来越大, 这时候操作这个集合就会产生效率问题。 如何避免这个问题呢?Gif 在采用 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 位。此处参照了 /whycadi/ 7.用 lzw 算法压缩原始数据的示例分析 输入流,也就是原始的数据 为:255,24,54,255,24,255,255,24,5,123,45,255,24,5,24,54.................. 这个正好可以看到是 gif 文件中像素数组的一部分,如何对它进行压缩 因为原始数据可以用 8bit 来表示,故清除标志 Clear=255+1 =256,结束标志为 End=256+1=257,目前标号集为 0 1 2 3 .................................................................................255 CLEAR END 第一步,读取第一个字符为 255,在标记表里面查找,255 已经存在,我们已经认识 255 了,不 做处理 第二步,取第二个字符,此时前缀为 A,形成当前的 Entry 为(255,24),在标记集合不存在,我们 并不认识 255,24 好,这次你小子来了,我就记住你,把它在标记集合中标记为 258,然后输出前 缀 A,保留后缀 24,并作为下一次的前缀(后缀变前缀) 第三步,取第三个字符为 54,当前 Entry(24,54),不认识,记录(24,54)为标号 259,并输出 24, 后缀变前缀 第四部:取第四个字符 255,Entry=(54,255),不认识,记录(54,255)为标号 260,输出 54,后缀变 前缀 第五步 取第 5 个字符 24,entry=(255,24),啊,认识你,这不是老 258 么,于是把字符串规 约为 258,并作为前缀 第六步 取第六个字符 255,entry=(258,255),不认识,记录(258,255)为 261,输出 258,后缀 变前缀 ....... 一直处理到最后一个字符, 用一个表记录处理过程 CLEAR=256,END=257
for ii in range(1,len(inStr)): cChar=inStr[ii] cTemp=(iTag<<8)+cChar if cTemp in mTagMap: #(前缀 后缀) #该(前缀 后缀)已存在
第几步 前缀 后缀 Entry 1 255 (,255) 认识(Y/N) 输出 标号
2 3 4 5 6 7
255 24 24 54 54 255
(255,24) (24,54) (54,255) (255,24) (258,255) (255,255)
N N N Y N N
255 258 24 54 259 260
STRING = STRING + character
iTagCurrent=258 #当前的标记总数 0~255 标记 256 为 begin & clear , 257 为 end
iTag=inStr[0] ii=0 cTemp=0
#当前标记
if bits>16 or bits<12: return None iMaxLen=(1<<bits)-1 #最多允许的标记数,由位宽决定
255 24 258 255 255 255
258 261 255 262
..... 上面这个示例有些不能完整体现,另外一个例子是 原输入数据为: 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 ..... 采用 LZW 算法对其进行压缩,压缩过程用一个表来表述为: 注意原数据中只包含 4 个 character,A,B,C,D 用两 bit 即可表述, 根据 lzw 算法, 首先扩展一位变为 3 为,Clear=2 的 2 次方+1=4; End=4+1=5; 初始标号集因该为
1 2 3 4 5
A B C D Clear End
而压缩过程为:
第几步 前缀 后缀 Entry 1 2 3 4 5 6 7 8 9 10 11 12 A B A 6 A 6 8 B B 10 A A B A B A B A B B B A B (,A) (A,B) (B,A) (A,B) (6,A) (A,B) (6,A) (8,B) (B,B) (B,B) (10,A) (A,B) N N Y N Y Y N N Y N Y 10 11 8 B 9 10 6 8 A B 6 7 认识(Y/N) 输出 标号
STRING =
get input character get input character in the string table then
WHILE there are still input characters DO CHARACTER = IF STRING + CHARACTER is ELSE output the code for STRING add STRING + CHARACTER to the string table STRING = CHARACTER END of IF END of WHILE output the code for STRING
相关文档
最新文档