急求GIF图像的LZW压缩算法过程

2C 00 00 00 00 0A 00 08 00 00 08 32 11 1F 08 1C ,...............
48 F0 C1 3D 7B 07 EF D5 4B F8 C0 9E C3 87 0F 0D ................
22 A4 56 B0 A1 3D 8B 10 0D D6 6B F8 80 22 45 8B ................
0D F1 3D 10 49 8D DA C1 07 EF 3A AA 0C 08 00 3B ...............;

这是一幅256色的GIF图片的图像数据,已知其宽为10px(h0A)高为8px(h08),如何解压缩其图像数据块?如何重新用lzw算法对其压缩?

请用VB

另:一张GIF动画是由多张静态图片组成,也就是多个帧组成,请问帧与帧之间的分隔是什么代码?

LZW数据压缩算法的原理分析
我希望通过本文的介绍,能给那些目前不太了解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,4代表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 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位。此处参照了http://blog.csdn.net/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 第几步 前缀 后缀 Entry 认识(Y/N) 输出 标号
1 255 (,255)
2 255 24 (255,24) N 255 258
3 24 54 (24,54) N 24 259
4 54 255 (54,255) N 54 260
5 255 24 (255,24) Y
6 258 255 (258,255) N 258 261
7 255 255 (255,255) N 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;
初始标号集因该为
0 1 2 3 4 5
A B C D Clear End

而压缩过程为:
第几步 前缀 后缀 Entry 认识(Y/N) 输出 标号
1 A (,A)
2 A B (A,B) N A 6
3 B A (B,A) N B 7
4 A B (A,B) Y
5 6 A (6,A) N 6 8
6 A B (A,B) Y
7 6 A (6,A) Y
8 8 B (8,B) N 8 9
9 B B (B,B) N B 10
10 B B (B,B) Y
11 10 A (10,A) N 10 11
12 A B (A,B) Y

.....
当进行到第12步的时候,标号集应该为
0 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算法的伪代码实现
1STRING = get input character
2WHILE there are still input characters DO
3 CHARACTER = get input character
4 IF STRING+CHARACTER is in the string table then
5 STRING = STRING+character
6 ELSE
7 output the code for STRING
8 add STRING+CHARACTER to the string table
9 STRING = CHARACTER
10 END of IF
11END of WHILE
12output the code for STRING
13

9.LZW算法的流程图
没有安visio,画了一个,比较难看,

----------------------------------------------------------------------------------------

using System;
using System.IO;

namespace Gif.Components
{
public class LZWEncoder
{

private static readonly int EOF = -1;

private int imgW, imgH;
private byte[] pixAry;
private int initCodeSize;
private int remaining;
private int curPixel;

// GIFCOMPR.C - GIF Image compression routines
//
// Lempel-Ziv compression based on 'compress'. GIF modifications by
// David Rowley (mgardi@watdcsu.waterloo.edu)

// General DEFINEs

static readonly int BITS = 12;

static readonly int HSIZE = 5003; // 80% occupancy

// GIF Image compression - modified 'compress'
//
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
//
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
// Jim McKie (decvax!mcvax!jim)
// Steve Davies (decvax!vax135!petsd!peora!srd)
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
// James A. Woods (decvax!ihnp4!ames!jaw)
// Joe Orost (decvax!vax135!petsd!joe)

int n_bits; // number of bits/code
int maxbits = BITS; // user settable max # bits/code
int maxcode; // maximum code, given n_bits
int maxmaxcode = 1 << BITS; // should NEVER generate this code

int[] htab = new int[HSIZE];//这个是放hash的筒子,在这里面可以很快的找到1个key
int[] codetab = new int[HSIZE];

int hsize = HSIZE; // for dynamic table sizing

int free_ent = 0; // first unused entry

// block compression parameters -- after all codes are used up,
// and compression rate changes, start over.
bool clear_flg = false;

// Algorithm: use open addressing double hashing (no chaining) on the
// prefix code / next character combination. We do a variant of Knuth's
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
// secondary probe. Here, the modular division first probe is gives way
// to a faster exclusive-or manipulation. Also do block compression with
// an adaptive reset, whereby the code table is cleared when the compression
// ratio decreases, but after the table fills. The variable-length output
// codes are re-sized at this point, and a special CLEAR code is generated
// for the decompressor. Late addition: construct the table according to
// file size for noticeable speed improvement on small files. Please direct
// questions about this implementation to ames!jaw.

int g_init_bits;

int ClearCode;
int EOFCode;

// output
//
// Output the given code.
// Inputs:
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
// that n_bits =< wordsize - 1.
// Outputs:
// Outputs code to the file.
// Assumptions:
// Chars are 8 bits long.
// Algorithm:
// Maintain a BITS character long buffer (so that 8 codes will
// fit in it exactly). Use the VAX insv instruction to insert each
// code in turn. When the buffer fills up empty it and start over.

int cur_accum = 0;
int cur_bits = 0;

int [] masks =
{
0x0000,
0x0001,
0x0003,
0x0007,
0x000F,
0x001F,
0x003F,
0x007F,
0x00FF,
0x01FF,
0x03FF,
0x07FF,
0x0FFF,
0x1FFF,
0x3FFF,
0x7FFF,
0xFFFF };

// Number of characters so far in this 'packet'
int a_count;

// Define the storage for the packet accumulator
byte[] accum = new byte[256];

//----------------------------------------------------------------------------
public LZWEncoder(int width, int height, byte[] pixels, int color_depth)
{
imgW = width;
imgH = height;
pixAry = pixels;
initCodeSize = Math.Max(2, color_depth);
}

// Add a character to the end of the current packet, and if it is 254
// characters, flush the packet to disk.
void Add(byte c, Stream outs)
{
accum[a_count++] = c;
if (a_count >= 254)
Flush(outs);
}

// Clear out the hash table

// table clear for block compress
void ClearTable(Stream outs)
{
ResetCodeTable(hsize);
free_ent = ClearCode + 2;
clear_flg = true;

Output(ClearCode, outs);
}

// reset code table
// 全部初始化为-1
void ResetCodeTable(int hsize)
{
for (int i = 0; i < hsize; ++i)
htab = -1;
}

void Compress(int init_bits, Stream outs)
{
int fcode;
int i /* = 0 */;
int c;
int ent;
int disp;
int hsize_reg;
int hshift;

// Set up the globals: g_init_bits - initial number of bits
//原始数据的字长,在gif文件中,原始数据的字长可以为1(单色图),4(16色),和8(256色)
//开始的时候先加上1
//但是当原始数据长度为1的时候,开始为3
//因此原始长度1->3,4->5,8->9

//?为何原始数据字长为1的时候,开始长度为3呢??
//如果+1=2,只能表示四种状态,加上clearcode和endcode就用完了。所以必须扩展到3
g_init_bits = init_bits;

// Set up the necessary values
//是否需要加清除标志
//GIF为了提高压缩率,采用的是变长的字长(VCL)。比如说原始数据是8位,那么开始先加上1位(8+1=9)
//当标号到2^9=512的时候,超过了当前长度9所能表现的最大值,此时后面的标号就必须用10位来表示
//以此类推,当标号到2^12的时候,因为最大为12,不能继续扩展了,需要在2^12=4096的位置上插入一个ClearCode,表示从这往后,从9位重新再来了
clear_flg = false;
n_bits = g_init_bits;
//获得n位数能表述的最大值(gif图像中开始一般为3,5,9,故maxcode一般为7,31,511)
maxcode = MaxCode(n_bits);
//表示从这里我重新开始构造字典字典了,以前的所有标记作废,
//开始使用新的标记。这个标号集的大小多少比较合适呢?据说理论上是越大压缩率越高(我个人感觉太大了也不见得就好),
//不过处理的开销也呈指数增长
//gif规定,clearcode的值为原始数据最大字长所能表达的数值+1;比如原始数据长度为8,则clearcode=1<<(9-1)=256
ClearCode = 1 << (init_bits - 1);
//结束标志为clearcode+1
EOFCode = ClearCode + 1;
//这个是解除结束的
free_ent = ClearCode + 2;
//清楚数量
a_count = 0; // clear packet
//从图像中获得下一个像素
ent = NextPixel();

hshift = 0;
for (fcode = hsize; fcode < 65536; fcode *= 2)
++hshift;
//设置hash码范围
hshift = 8 - hshift; // set hash code range bound

hsize_reg = hsize;
//清除固定大小的hash表,用于存储标记,这个相当于字典
ResetCodeTable(hsize_reg); // clear hash table

Output(ClearCode, outs);

outer_loop : while ((c = NextPixel()) != EOF)
{
fcode = (c << maxbits) + ent;
i = (c << hshift) ^ ent; // xor hashing
//嘿嘿,小样,又来了,我认识你
if (htab == fcode)
{
ent = codetab;
continue;
}
//这小子,新来的
else if (htab >= 0) // non-empty slot
{
disp = hsize_reg - i; // secondary hash (after G. Knott)
if (i == 0)
disp = 1;
do
{
if ((i -= disp) < 0)
i += hsize_reg;

if (htab == fcode)
{
ent = codetab;
goto outer_loop;
}
} while (htab >= 0);
}
Output(ent, outs);
//从这里可以看出,ent就是前缀(prefix),而当前正在处理的字符标志就是后缀(suffix)
ent = c;
//判断终止结束符是否超过当前位数所能表述的范围
if (free_ent < maxmaxcode)
{
//如果没有超
codetab = free_ent++; // code -> hashtable
//hash表里面建立相应索引
htab = fcode;
}
else
//说明超过了当前所能表述的范围,清空字典,重新再来
ClearTable(outs);
}
// Put out the final code.
Output(ent, outs);
Output(EOFCode, outs);
}

//----------------------------------------------------------------------------
public void Encode( Stream os)
{
os.WriteByte( Convert.ToByte( initCodeSize) ); // write "initial code size" byte
//这个图像包含多少个像素
remaining = imgW * imgH; // reset navigation variables
//当前处理的像素索引
curPixel = 0;

Compress(initCodeSize + 1, os); // compress and write the pixel data

os.WriteByte(0); // write block terminator
}

// Flush the packet to disk, and reset the accumulator
void Flush(Stream outs)
{
if (a_count > 0)
{
outs.WriteByte( Convert.ToByte( a_count ));
outs.Write(accum, 0, a_count);
a_count = 0;
}
}

/// <summary>
/// 获得n位数所能表达的最大数值
/// </summary>
/// <param name="n_bits">位数,一般情况下n_bits = 9</param>
/// <returns>最大值,例如n_bits=8,则返回值就为2^8-1=255</returns>
int MaxCode(int n_bits)
{
return (1 << n_bits) - 1;
}

//----------------------------------------------------------------------------
// Return the next pixel from the image
//----------------------------------------------------------------------------
/// <summary>
/// 从图像中获得下一个像素
/// </summary>
/// <returns></returns>
private int NextPixel()
{
//还剩多少个像素没有处理
//如果没有了,返回结束标志
if (remaining == 0)
return EOF;
//否则处理下一个,并将未处理像素数目-1
--remaining;
//当前处理的像素
int temp = curPixel + 1;
//如果当前处理像素在像素范围之内
if ( temp < pixAry.GetUpperBound( 0 ))
{
//下一个像素
byte pix = pixAry[curPixel++];
return pix & 0xff;
}
return 0xff;
}
/// <summary>
/// 输出字到输出流
/// </summary>
/// <param name="code">要输出的字</param>
/// <param name="outs">输出流</param>
void Output(int code, Stream outs)
{
//得到当前标志位所能表示的最大标志值
cur_accum &= masks[cur_bits];

if (cur_bits > 0)
cur_accum |= (code << cur_bits);
else
//如果标志位为0,就将当前标号为输入流
cur_accum = code;
//当前能标志的最大字长度(9-10-11-12-9-10。。。。。。。)
cur_bits += n_bits;
//如果当前最大长度大于8
while (cur_bits >= 8)
{
//向流中输出一个字节
Add((byte) (cur_accum & 0xff), outs);
//将当前标号右移8位
cur_accum >>= 8;
cur_bits -= 8;
}

// If the next entry is going to be too big for the code size,
// then increase it, if possible.
if (free_ent > maxcode || clear_flg)
{
if (clear_flg)
{
maxcode = MaxCode(n_bits = g_init_bits);
clear_flg = false;
}
else
{
++n_bits;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MaxCode(n_bits);
}
}

if (code == EOFCode)
{
// At EOF, write the rest of the buffer.
while (cur_bits > 0)
{
Add((byte) (cur_accum & 0xff), outs);
cur_accum >>= 8;
cur_bits -= 8;
}

Flush(outs);
}
}
}
}

参考资料:http://www.laiva-laiva.com/blogs/thief/archive/2006/11/06/LZW_70656E638B53297F977BD56C84769F53067406529067_.aspx

温馨提示:内容为网友见解,仅供参考
无其他回答

无损压缩算法LZW介绍
在实际应用中,LZW算法的编码过程相对简单,只需依次读取输入内容并构建表,读取一个字符后,查找该字符与下一个字符构成的字符串在编码表中的索引,如索引存在则继续读取下个字符直至无法构成新字符串后,在编码表中添加新字符串并输出当前最长字符串的索引。在选择实现时,推荐关注LZW压缩与解压的两个实...

LZW算法LZW算法
LZW解压算法,具体解压步骤如下:① 读取第一个字符,输出它,并将其赋给prefix_character。② 读取下一个字符,判断是否文件已结束,未结束则赋给current_character。③ 若current_character大于N(基本字符元素数量),检查是否在code_table中。④ 若不在code_table中,输出prefix_character与prefix_charac...

急求GIF图像的LZW压缩算法过程
LZW算法中,首先建立一个字符串表,把每一个第一次出现的字符串放入串表中,并用一个数字来表示,这个数字与此字符串在串表中的位置有关,并将这个数字存入压缩文件中,如果这个字符串再次出现时,即可用表示它的数字来代替,并将这个数字存入文件中。压缩完成后将串表丢弃。如"print" 字符串,如果在压缩时用266表示,...

LZW压缩算法?
LZW算法的核心在于建立一个自适应的字符串编码表,通过将长字符串替换为较短的编码,实现数据的高效压缩。这一算法源于两位科学家Ziv和Lempel在1977和1978年的开创性工作,而Terry Welch在1984年的改进使其广为人知,因此得名LZW。在实际应用中,LZW尤其在GIF图像压缩中大放异彩。编码过程开始于初始化一...

LZW是什么意思
转换表是在压缩或解压缩过程中动态生成的表,该表只在进行压缩或解压缩过程中需要,一旦压缩和解压缩结束,该表将不再起任何作用。2.LZW算法 LZW算法基于转换串表(字典)T,将输入字符串映射成定长(通常为12位)的码字。在12位4096种可能的代码中,256个代表单字符,剩下3840给出现的字符串。LZW...

如何用python实现LZW压缩算法的解码?
LZW(Lempel-Ziv-Welch)算法是一种使用字典进行压缩的无损数据压缩算法。在Python中实现LZW算法的解码部分,我们需要一个构建好的字典,并且需要一个编码后的数据流。下面是如何实现LZW解码的步骤:‌初始化字典‌:字典中包含所有可能的字符序列,其中索引表示编码,值表示对应的字符串。‌...

GIF格式的动态图片怎么压缩小啊
调整图角大小窗口。如下图,确认勾选了“保持原样比例”;原来GIF图片是320×280像素,那这里就可以改小“画布尺寸”,比如200×175像素,你可以随意;“品质”也可以选择的,默认是Good。然后点击“确定”保存设置并返回主界面。这样就可以实现压缩的效果了。然后选择“文件”菜单-“另存为”-“GIF文件...

图像压缩编码的发展过程,可以分为哪三个阶段?
图像压缩编码的发展过程介绍如下:图像压缩技术的发展历程经历了多个阶段,以下是其大致的发展历程:无损压缩阶段:早期的图像压缩技术主要集中在无损压缩算法上,即压缩图像文件大小而不损失图像质量。其中最著名的算法是无损预测编码算法,如LZW(Lempel-Ziv-Welch)算法和Huffman编码算法。有损压缩阶段:随着...

LZW算法LZW压缩的特点
3. LZW压缩技术不仅用于图像数据处理,还适用于文本、程序等多种数据压缩需求。4. LZW压缩技术有多种变体,如ARC、RKARC、PKZIP等高效压缩程序,具有广泛适用性。5. 无论图像的任意宽度和像素位长度,LZW压缩过程稳定,压缩和解压缩速度快。6. 对于LZW压缩,硬件需求不高,能在Intel 80386级别的计算机...

gif是无损压缩吗
GIF的原义为图像互换格式,是一种基于LZW算法的无损压缩格式,压缩率一般在50%。就单从格式来说,GIF是无损的。不过,在现实软件的应用中,GIF文件通常都会被进行有损压缩。无损压缩的基本原理是相同的颜色信息只需保存一次。压缩图像的软件首先会确定图像中哪些区域是相同的,哪些是不同的。包括了重复...

相似回答