1. 程式人生 > >LZW資料壓縮演算法的原理分析

LZW資料壓縮演算法的原理分析

   我希望通過本文的介紹,能給那些目前不太瞭解lzw演算法和該演算法在gif影象中應用,但渴望瞭解它的人一些啟發和幫助拋磚引玉而已,更希望園子裡面兄弟提出寶貴的意見
1.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 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 .....
上面這個示例有些不能完整體現,另外一個例子是
原輸入資料為: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 .....
用兩bit即可表述,根據lzw演算法,首先擴充套件一位變為3為,Clear=2的2次方+1=4; End=4+1=5;
初始標號集因該為

而壓縮過程為:

.....
當進行到第12步的時候,標號集應該為

8.LZW演算法的虛擬碼實現