`

gif图片介绍和LZW算法说明

 
阅读更多

                                     GIF图片介绍和LZW算法说明

1. GIF格式

 

GIF图像的文件格式如下:


 

 

重点字段介绍:

逻辑屏幕标志符:包含了逻辑屏幕的长宽,是否包含全局颜色列表,背景色等。

全局颜色列表:包含了图片中使用的所有颜色,而图片的数据使用全局颜色列表中的索引代替。

图像块:gif的数据是由数据块组成,每个数据块的长度最大为255

 

2. LZW算法

全称为“串表压缩算法”,LZW代表了研发该算法的三个人(坑爹啊,还以为是英文缩写)。属于字典编码的一种,最大的优点在于压缩后的数据不需要额外携带字典就可以解压。GIF采用LZW算法进行编码的。

 

2.1 首先介绍几个相关术语:

数据流:需要进行编码的数据串,GIF中指像素的颜色值。LZW编码时的输入

编码流:数据流编码的结果。LZW编码时的输出

编译表:编码时候的字典表,是动态建立的。

字符:数据流中最基本的单位,GIF中一个像素的颜色在颜色列表中的序号。

字符串:由连续字符组成的,前缀和后缀一起代表了字符串

前缀:也是字符串,但可以为空。具体解释见下文

后缀:也是字符串,一个字符就是后缀,可以为空。具体解释见下文

 

2.2 编码流程

 

首先举一个简单的例子,有一个数据流为:abcbabbac

       我们为了和数据流的字符进行区别,我们采用1,2,3这样的数字进行编码。数据流中可能出现的字符为,a~z,所以我们初始化编译表,用1~26,代表a~z单个字符。所以对于其它的字符串只能从27进行编码。初始化前缀为空,后缀也为空,编码流为空,编译表为1~26和a~z的对应。

       1.2.1 从数据流取字符a作为后缀,前缀为空,字符串为a,在编译表中存在,为1,字符串变前缀。前缀:a;后缀:空;字符串:a;编码流:空;编译表:单个字符的对应(后面不再显示)

       1.2.2 取字符b作为后缀,字符串为:ab,编译表中不存在,定义ab为27,前缀输出,后缀变前缀。前缀:b;后缀:空;字符串:b;编码流:1;编译表:27=ab

       1.2.3 取字符c作为后缀,字符串为:bc,编译表不存在,定义bc为28,前缀输出,后缀变前缀。前缀:c;后缀:空;字符串:c;编码流:1-2;编译表:27=ab   28=bc

       1.2.4 取字符b作为后缀,字符串为:cb,编译表不存在,定义cb为29,前缀输出,后缀变前缀。前缀:b;后缀:空;字符串:b;编码流:1-2-3;编译表:27=ab   28=bc 29=cb

       1.2.5 取字符a作为后缀,字符串为:ba,编译表不存在,定义ba为30,前缀输出,后缀变前缀。前缀:a;后缀:空;字符串:a;编码流:1-2-3-2;编译表:27=ab   28=bc 29=cb 30=ba

       1.2.6 取字符b作为后缀,字符串为:ab,编译表存在,为27,字符串变前缀。前缀:ab;后缀:空;字符串:ab;编码流:1-2-3-2;编译表:27=ab   28=bc 29=cb 30=ba

       1.2.7 取字符b作为后缀,字符串为:abb,编译表不存在,定义abb为31,前缀输出,后缀变前缀。前缀:b;后缀:空;字符串:b;编码流:1-2-3-2-27;编译表:27=ab   28=bc 29=cb 30=ba 31=abb

       1.2.8 取字符a作为后缀,字符串为:ba,编译表存在,为30,字符串变前缀。前缀:ba;后缀:空;字符串:ba;编码流:1-2-3-2-27;编译表:27=ab   28=bc 29=cb 30=ba 31=abb

       1.2.9 取字符c作为后缀,字符串为:bac,编译表不存在,定义bac为32,前缀输出,后缀变前缀。前缀:c;后缀:空;字符串:c;编码流:1-2-3-2-27-30;编译表:27=ab   28=bc 29=cb 30=ba 31=abb 32=bac

       1.2.10 输入流没有字符,将前缀c输出,最终编码流为:1-2-3-2-27-30-3

能够看到原来的9个字符被压缩成7个字符。

 

GIF是基于颜色列表的图片结构,GIF的数据对应的是每个点的颜色在颜色列表的索引,LZW编码是对该索引进行的编码。在GIF格式的图片中,对LZW做了一些改变:

       1. 首先定义编码长度,gif中最大编码长度为12位,能表示的最大数字为4096,因此,编译表中序号不能超过4096.

        2. gif最大支持256色,因此gif编码长度为9~12位。采用变长编码,到1023,再编码就改为10位,以此类推。

        3. gif定义了clear code,清除标记为原始数据长度+1,比如gif最大值为255,那么清除标记为256. 当一个编译的时候,如果编译表达到了最大值,比如4096,就要插入一个clear code,即可以清空编译表,重新开始编译。

        4. gif定义了结束标记end,end为清除标记+1, gif中为257,当gif的一张图片编码数据结束后插入end,表示图片结束。

 

GIF图片编码举例:

输入流:10 21 22 26 10 21 22 10 26 

编译表:1~255,256(clear code),257(end)

 

输入 前缀 后缀 字符串 编译表 输出
10   10 ( ,10)    
21 10 21 (10, 21) 258=(10, 21) 10
22 21 22 (21, 22) 259=(21, 22) 21
26 22 26 (22, 26) 260=(22, 26) 22
10 26 10 (26, 10) 261=(26, 10) 26
21 10 21 (10, 21)    
22 258 22 (258, 22) 262=(258, 22) 258
10 22 10 (22. 10) 263=(22, 10) 22
26 10 26 (10, 26) 264=(10, 26) 10

 

最后的输出流为:10 21 22 26 258 22 10 26

具体流程图为:



 

1.2 解码流程

 

对于1.1中第一个例子,压缩后的编码为1-2-3-2-27-30-3。初始化后的编码表为1~26对应a~z。

        1.2.1 读入值1,编译表中存在,输出a,输入做前缀,字符串为a,也在编译表中,前缀为a

        1.2.2 读入值2,编译表中存在,输出b,,将b作为后缀,字符串为ab,不在编译表中,定义27=ab,输入做前缀,前缀为b

        1.2.3 读入值3,编译表中存在,输出c,将c作为后缀,字符串为bc,不在编译表中,定义28=bc,输入做前缀,前缀为c

        1.2.4 读入值2,编译表中存在,输出b,将b作为后缀,字符串为cb,不在编译表中,定义29=cb,输入做前缀,前缀为b

        1.2.5 读入值27,编译表中存在,输出ab,将a作为后缀,字符串为ba,不在编译表中,定义30=ba,输入做前缀,前缀为ab

        1.2.6 读入值30,编译表中存在,输出ba,将b作为后缀,字符串为abb,在编译表中,输入做前缀为ba

        1.2.7 读入值3,编译表中存在,输出c,将c作为后缀,字符串为bac,不在编译表中,定义32=bac,前缀为c

 

解码后的输出为:abcbabbac

 

上述第二个例子同理,不再赘述。

 

如果读入值30后,编译表中不存在,则将上次输入的值+该值的第一个字符输出,将该输出定义为30

 

 流程图为:



 

3. GraphicsMagic

 

在使用中发现GM对于有些gif文件无法解析

 

3.1 bug分析

 

GM解析gif图片的部分代码:

 

for (x=0; x < (long) image->columns; )
    {
      if (top_stack == pixel_stack)
        {
          if (bits < code_size)
            {
              /*
                Load bytes until there is enough bits for a code.
              */
              if (count == 0)
                {
                  /*
                    Read a new data block.
                  */
                  count=ReadBlobBlock(image,packet);
                  if (count == 0)
                    break;
                  c=packet;
                }
              datum+=(unsigned long) (*c) << bits;
              bits+=8;
              c++;
              count--;
              continue;
            }
          /*
            Get the next code.
          */
          code=(long) (datum & code_mask);
          datum>>=code_size;
          bits-=code_size;
          /*
            Interpret the code
          */
          if (code > available)
            {
              status=MagickFail;
              break;
            }
          if (code == end_of_information)
            break;
          if (code == clear)
            {
              /*
                Reset decoder.
              */
              code_size=data_size+1;
              code_mask=(1 << code_size)-1;
              available=clear+2;
              old_code=NullCode;
              continue;
            }
          if (old_code == NullCode)
            {
              *top_stack++=suffix[code];
              old_code=code;
              first=(unsigned char) code;
              continue;
            }
          in_code=code;
          if (code >= available)
            {
              *top_stack++=first;
              code=old_code;
            }
          while (code >= clear)
          {
            if ((top_stack-pixel_stack) >= MaxStackSize)
              {
                status=MagickFail;
                break;
              }
            *top_stack++=suffix[code];
            code=prefix[code];
          }
          if (status == MagickFail)
            break;
          first=suffix[code];
          /*
            Add a new string to the string table,
          */
          if (available >= MaxStackSize)
            {
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                                    "Excessive LZW string data "
                                    "(string table overflow)");
              status=MagickFail;
              break;
            }

            *top_stack++=first;		
            prefix[available]=(short) old_code;
            suffix[available]=first;
            available++;
          
          if (((available & code_mask) == 0) && (available < MaxStackSize))
            {
              code_size++;
              code_mask+=available;
            }
          old_code=in_code;
        }
      /*
        Pop a pixel off the pixel stack.
      */
      top_stack--;
      index=(*top_stack);
      VerifyColormapIndex(image,index);
      indexes[x]=index;
      *q=image->colormap[index];
      q->opacity=(Quantum)
        (index == opacity ? TransparentOpacity : OpaqueOpacity);
      x++;
      q++;
    }

 

结果发现对于有些gif文件,使用gm进行解析会出错,通过debug发现错误信息为:

 

Excessive LZW string data (string table overflow)

 可知当available>4096时会出现该错误。原因是对于有些gif文件没有严格遵守gif格式,导致解码表超过4096。

 

3.2 解决方案

将上述红色的代码改为:

if (available < MaxStackSize) 
{
		  		
		prefix[available]=(short) old_code;
                suffix[available]=first;
                available++;
		if (available >= code_mask + 1 && code_size < 12U)
		{
			code_size++;
		}
}

 将蓝色的代码去掉。

这样对于超出解码表的像素不再进行解析。

 

  • 大小: 20.4 KB
  • 大小: 5.2 KB
  • 大小: 25.7 KB
分享到:
评论

相关推荐

    (利用GIF_LZW算法实现的)网络图片传输.zip_gif lzw_huffman jpeg_jpeg_vb 图片_图片传输

    (利用GIF_LZW算法实现的)网络图片传输 技术特点 ~~~~~~~~ 一、使用TCP协议传输数据。理由:稳定性好。 二、使用字节数据流进行传输,理由: A:VB的String存在自动Unicode转换问题,影响速度; B:可以直接发送8位...

    VB利用GIF_LZW算法实现网络图片传输.rar

    VB利用GIF_LZW算法实现网络图片传输,技术特点  一、使用TCP议传输数据。理由:稳定性好。  二、使用字节数据流进行传输,理由:  A:VB的String存在自动Unicode转换问题,影响速度;  B:可以直接发送8位字节数据...

    gif图片压缩(纯java实现,不依赖第三方类库)

    总结来说,不依赖第三方库的GIF图片压缩用Java实现是一项挑战性的任务,涉及颜色量化和LZW编码两大技术。虽然这种方法可能比使用专门的图像处理库更加复杂,但它提供了更多的控制权和学习机会,对于提升个人技能和...

    VB GIF_LZW图像压缩算法实现桌面传输

    内容索引:VB源码,网络相关,文件传输 VB利用了GIF_LZW算法实现网络图片传输,也就是Windows XP系统自带的远程桌面登录后的功能,程序仍然采用稳定性好的TCP协议及字节数据流进行传输,减少了编码时间,另外使用GIF-...

    简单实用的gif图片解码C语言实现

    本篇文章将深入探讨“简单实用的gif图片解码C语言实现”这一主题,帮助你理解和掌握如何用C语言解析GIF图像文件。 GIF解码过程通常包括以下几个步骤: 1. **文件头解析**:GIF文件以一个固定的6字节文件头开始,...

    gif图片文件编解码

    gif用的变长lzw压缩算法: 没弄懂原理,只知道过程。压缩取一个字符作为后缀,看看字符串是否存在。存在,用对应的编码作为前缀继续取;不存在把字符串添加到字典,前缀添加到输出流,后缀作为前缀继续取。解压取一...

    GIF图片压缩

    - GIF图片通常采用LZW(Lempel-Ziv-Welch)压缩算法,这是一种字典编码方法,通过查找重复的模式并用更短的代码表示来实现压缩。这种方法可以保持图片质量不变,但压缩率有限。 2. **有损压缩**: - 对于静态GIF...

    LZW压缩实例

    LZW算法在文件压缩(如GIF图像格式)、数据传输等领域有广泛应用。然而,由于专利问题,LZW在某些场合已被其他无专利限制的压缩算法(如DEFLATE,用于ZIP和PNG格式)所取代。尽管如此,LZW仍然是理解和学习数据压缩...

    C++实现播放GIF图片

    在C++编程中,实现显示GIF图片的功能是一项挑战,因为GIF是一种支持动画和透明度的复杂图像格式。为了实现这一目标,开发者通常需要利用第三方库或者直接处理GIF的二进制数据。以下是一些关键的知识点,涵盖了如何在...

    Gif图片分解 源码

    通过这个"Gif图片分解 源码",开发者不仅可以学习到GIF图像格式的底层知识,还能提升他们在二进制文件处理、图像处理和软件工程方面的技能。这不仅有助于开发自定义的GIF工具,也有助于理解其他图像处理和动画相关的...

    C#制作和提取gif图片

    GIF格式采用LZW压缩算法,支持256种颜色,这使得它在传输和存储方面具有较高的效率。同时,GIF支持透明度和循环播放,非常适合制作简单的动画。 2. **C#与GIF处理库** 在C#中处理GIF,我们通常会利用第三方库,如...

    gif图片压缩软件gif图片压缩软件

    在选择GIF图片压缩软件时,除了考虑压缩效率和质量,还需要关注软件的安全性和兼容性。确保软件来自可信赖的来源,避免携带病毒或恶意软件。同时,软件应能兼容各种操作系统,并能处理不同版本的GIF文件。 总的来说...

    加载中gif图片-透明loading.rar

    5. **自定义和集成**:开发者和设计师可以根据项目需求选择合适的加载中gif图片,也可以通过代码实现自定义动画,如使用CSS3的`@keyframes`规则或JavaScript库如Animate.css、GSAP等。 6. **资源管理**:将所有加载...

    BlackBerry gif图片 显示包装类

    本篇文章将深入探讨如何在BlackBerry平台上创建一个GIF图片显示包装类,并结合BlackBerry线程应用知识,确保程序的性能和流畅性。 首先,理解GIF图片格式是至关重要的。GIF是一种支持动画的位图格式,它通过在一个...

    GIF图片合成制作

    - **格式介绍**:GIF格式使用LZW压缩算法,可以存储多帧图像,形成连续播放的动画效果。 - **颜色限制**:GIF采用8位色彩,最多支持256种颜色,这使得它在色彩丰富的图像上可能不如JPEG或PNG表现好。 - **透明度*...

    GIF图片解码

    这个过程涉及一系列复杂的算法和技术,下面将详细阐述GIF图片解码的相关知识点。 1. **GIF文件结构**:GIF文件由头部、逻辑屏幕描述符、全局颜色表、图像描述符、局部颜色表、压缩数据和结束标记等部分组成。解码...

    GIF图片大小修改器

    1. **压缩算法**:GIF使用的是LZW(Lempel-Ziv-Welch)无损压缩算法,该算法通过查找和替换重复的字节模式来减少文件大小。这种压缩方法保留了图像的原始质量,但可能无法像有损压缩的JPEG那样实现大幅度的压缩。 2...

    as3 LZW数据压缩代码

    压缩图片时,LZW算法通常用于GIF格式,因为GIF支持LZW压缩。对于其他类型的图片,如JPEG和PNG,它们使用的是不同的压缩算法。然而,AS3实现的LZW代码可以用来压缩任何类型的数据,包括非图像数据。 总的来说,AS3中...

    Blackberry 上显示动态 GIF 图片

    总的来说,要在Blackberry设备上显示动态GIF图片,开发者需要对GIF文件格式有深入理解,并能够编写代码来处理和播放GIF动画。"DecodGIF"可能是实现这一功能的关键组件,它封装了GIF解析和动画播放的核心逻辑。通过...

    gif图片素材.rar

    标题中的“gif图片素材.rar”表明这是一个包含gif图像文件的压缩包。GIF,全称Graphics Interchange Format,是一种广泛用于互联网的图像格式,尤其适用于动画。它支持透明度、循环播放以及多帧动画,因此在网站设计...

Global site tag (gtag.js) - Google Analytics