學習LZ77與LZSS算法的有關資料

第五節   詞典編碼

有許多場合,開始時不知道要編碼數據的統計特性,也不一定允許我們事先知道它們的統計特性。因此,人們提出了許許多多的數據壓縮方法,企圖用來對這些數據進行壓縮編碼,在實際編碼過程中以儘可能獲得最大的壓縮比。這些技術統稱爲通用編碼技術。詞典編碼(Dictionary Encoding)技術就是屬於這一類,這種技術屬於無損壓縮技術。

一.詞典編碼分類

詞典編碼的根據是數據本身包含有重複代碼序列這個特性。例如文本文件(碼詞表示字符)和光柵圖像(碼詞表示像素)就具有這種特性。

詞典編碼法的種類很多,歸納起來分爲兩類。

第一類詞典法的想法是企圖查找正在壓縮的字符序列是否在前面的輸入數據中出現過,如果是,則用指向早期出現過的字符串的“指針”替代重複的字符串。這種編碼思想如圖03-05-1所示。

 

   圖03-05-1 第一類詞典法編碼概念

這裏的“詞典”是隱含的,指用以前處理過的數據。這類編碼中的所有算法都是以Abraham Lempel和Jakob Ziv在1977年開發和發表的算法(稱爲LZ77算法)爲基礎。此算法的一個改進算法是由Storer和Szymanski在1982年開發的,稱爲LZSS算法。

第二類算法的想法是企圖從輸入的數據中創建一個“短語詞典(dictionary of the phrases)”。編碼數據過程中當遇到已經在詞典中出現的“短語”時,編碼器就輸出這個詞典中的短語的“索引號”,而不是短語本身。這個概念如圖03-05-2所示。

 

     圖03-05-2 第二類詞典法編碼概念

A.Lempel和J.Ziv在1978年首次發表了介紹這種編碼方法的文章,稱爲LZ78。在他們的研究基礎上,Terry A.Welch在1984年發表對這種編碼算法進行了改進的文章,並首先在高速硬盤控制器上應用了這種算法。因此後來把這種編碼方法稱爲LZW(Lempel-Ziv Walch)壓縮編碼。

 

二.LZ77算法

1977年,Jacob Ziv和Abraham Lempel描述了一種基於滑動窗口緩存的技術,該緩存用於保存最近剛剛處理的文本(J. Ziv and A. Lempel, “A Universal Algorithm for Sequential Data Compression”, IEEE Transaction on Information Theory, May 1977)。這個算法一般稱爲IZ77。

LZ77和它的變體發現,在正文流中詞彙和短語(GIF中的圖像模式)很可能會出現重複。當出現一個重複時,重複的序列可以用一個短的編碼來代替。壓縮程序掃描這樣的重複,同時生成編碼來代替重複序列。隨着時間的過去,編碼可以重用來捕獲新的序列。算法必須設計成解壓程序能夠在編碼和原始數據序列推導出當前的映射。

在研究LZ77的細節之前,先看一個簡單的例子(J. Weiss and D. Schremp, “Putting Data on a Diet”, IEEE Spectrum, August 1993)。考慮這樣一句話:

  the brown fox jumped over the brown foxy jumping frog

這個短語的長度總共是53個八位組 = 424 bit。算法從左向右處理這個文本。初始時,每個字符被映射成9 bit的編碼,二進制的1跟着該字符的8 bit ASCII碼。在處理進行時,算法查找重複的序列。當碰到一個重複時,算法繼續掃描直到該重複序列終止。換句話說,每次出現一個重複時,算法包括儘可能多的字符。碰到的第一個這樣的序列是the brown fox。這個序列被替換成指向前一個序列的指針和序列的長度。在這種情況下,前一個序列的the brown fox出現在26個字符之前,序列的長度是13個字符。對於這個例子,假定存在兩種編碼選項:8 bit的指針和4 bit的長度,或者12 bit的指針和6 bit的長度。使用2 bit的首部來指示選擇了哪種選項,00表示第一種選項,01表示第二種選項。因此,the brown fox的第二次出現被編碼爲 <00b><26d><13 d >,或者00 00011010 1101。

壓縮報文的剩餘部分是字母y;序列<00b><27d><5 d >替換了由一個空格跟着jump組成的序列,以及字符序列ing frog。

圖03-05-3演示了壓縮映射的過程。壓縮過的報文由35個9 bit字符和兩個編碼組成,總長度爲35 x 9 + 2 x 14 = 343比特。和原來未壓縮的長度爲424比特的報文相比,壓縮比爲1.24。

 

圖03-05-3 LZ77模式例

 (一)壓縮算法說明

LZ77(及其變體)的壓縮算法使用了兩個緩存。滑動歷史緩存包含了前面處理過的N個源字符,前向緩存包含了將要處理的下面L個字符(圖03-05-4(a))。算法嘗試將前向緩存開始的兩個或多個字符與滑動歷史緩存中的字符串相匹配。如果沒有發現匹配,前向緩存的第一個字符作爲9 bit的字符輸出並且移入滑動窗口,滑動窗口中最久的字符被移出。如果找到匹配,算法繼續掃描以找出最長的匹配。然後匹配字符串作爲三元組輸出(指示標記、指針和長度)。對於K個字符的字符串,滑動窗口中最久的K個字符被移出,並且被編碼的K個字符被移入窗口。

圖03-05-4(b)顯示了這種模式對於我們的例子的運行情況。這裏假定了39個字符的滑動窗口和13個字符的前向緩存。在這個例子的上半部分,已經處理了前面的40個字符,滑動窗口中是未壓縮的最近的39個字符。剩下的源字符串在前向窗口中。壓縮算法確定了下一個匹配,從前向窗口將5個字符移入到滑動窗口中,並且輸出了這個匹配字符串的編碼。經過這些操作的緩存的狀態顯示在這個例子的下半部分。

 

(a)通用結構

 

(b)例子

    圖03-05-4  LZ77模式 

儘管LZ77是有效的,對於當前的輸入情況也是合適的,但是存在一些不足。算法使用了有限的窗口在以前的文本中查找匹配,對於相對於窗口大小來說非常長的文本塊,很多可能的匹配就會被丟掉。窗口大小可以增加,但這會帶來兩個損失:(1)算法的處理時間會增加,因爲它必須爲滑動窗口的每個位置進行一次與前向緩存的字符串匹配的工作;(2)<指針>字段必須更長,以允許更長的跳轉。

(二)壓縮算法描述

爲了更好地說明LZ77算法的原理,首先介紹算法中用到的幾個術語:

輸入數據流(input stream):待壓縮處理的字符序列。

 

字符(character):輸入數據流中的基本單元。

 

編碼位置(coding position):輸入數據流中當前要編碼的字符位置,指前向緩衝器中的開始字符。

 

前向緩衝器(lookahead buffer):存放從編碼位置到輸入數據流結束的字符序列的存儲器。

 

窗口(Window):指包含W個字符的窗口,字符是從編碼位置開始向後數也就是最後處理的字符數。

 

指針(Pointer):指向窗口中的匹配串且含長度的指針。

LZ77編碼算法的核心是查找從前向緩衝器開始的最長的匹配串。算法的具體執行步驟如下:

 

把編碼位置設置到輸入數據流的開始位置。

 

查找窗口中最長的匹配串。

 

輸出(Pointer, Length) Characters,其中Pointer是指向窗口中匹配串的指針,Length表示匹配字符的長度,Characters是前向緩衝器中的第1個不匹配的字符。

 

如果前向緩衝器不是空的,則把編碼位置和窗口向前移Length+1個字符,然後返回到步驟2。

例:待編碼的數據流如表03-05-1所示,編碼過程如表03-05-2所示。現作如下說明:

 

“步驟”欄表示編碼步驟。

 

“位置”欄表示編碼位置,輸入數據流中的第1個字符爲編碼位置1。

 

“匹配”欄表示窗口中找到的最長的匹配串。

 

“字符”欄表示匹配之後在前向緩衝存儲器中的第1個字符。

 

“輸出”欄以(Back_chars, Chars_length) Explicit_character格式輸出。其中(Back_chars,Chars_length)是指指向匹配串的指針,告訴譯碼器“在這個窗口中向後退Back_chars個字符然後拷貝Chars_length個字符到輸出”,Explicit_character是真實字符。例如,表3-13中的輸出“(5,2) C”告訴譯碼器回退5個字符,然後拷貝2個字符“AB”

表03-05-1 待編碼的數據流

位置

1

2

3

4

5

6

7

8

9

字符

A

A

B

C

B

B

A

B

C

表03-05-2 編碼過程

步驟

位置

匹配串

字符

輸出

1

1

--

A

(0,0) A

2

2

A

B

(1,1) B

3

4

--

C

(0,0) C

4

5

B

B

(2,1) B

5

7

A B

C

(5,2) C

(三)解壓算法

對於LZ77壓縮文本的解壓很簡單。解壓算法必須保存解壓輸出的最後N個字符。當碰到編碼字符串時,解壓算法使用<指針>,和<長度>,字段將編碼替換成實際的正文字符串。

三.LZSS算法

LZ77通過輸出真實字符解決了在窗口中出現沒有匹配串的問題,但這個解決方案包含有冗餘信息。冗餘信息表現在兩個方面,一是空指針,二是編碼器可能輸出額外的字符,這種字符可能包含在下一個匹配串中。LZSS算法以比較有效的方法解決這個問題,它的思想是如果匹配串的長度比指針本身的長度長就輸出指針,否則就輸出真實字符。由於輸出的壓縮數據流中包含有指針和字符本身,爲了區分它們就需要有額外的標誌位,即ID位。

LZSS編碼算法的具體執行步驟如下:

把編碼位置置於輸入數據流的開始位置。

 

在前向緩衝器中查找窗口中最長的匹配串
  ①Pointer :=匹配串指針。
  ②Length :=匹配串長度。

 

判斷匹配串長度Length是否大於等於最小匹配串長度(MIN_LENGTH) ,
  如果“是”:輸出指針,然後把編碼位置向前移動Length個字符。
  如果“否”:輸出前向緩衝存儲器中的第1個字符,然後把編碼位置向前移動一個字符。

 

如果前向緩衝器不是空的,就返回到步驟2。

例:編碼字符串如表03-05-3所示,編碼過程如表03-05-4所示。現說明如下:

 

“步驟”欄表示編碼步驟。

 

“位置”欄表示編碼位置,輸入數據流中的第1個字符爲編碼位置1。

 

“匹配”欄表示窗口中找到的最長的匹配串。

 

“字符”欄表示匹配之後在前向緩衝存儲器中的第1個字符。

 

“輸出”欄的輸出爲:
    ①如果匹配串本身的長度Length >= MIN_LENGTH,輸出指向匹配串的指針,格式爲(Back_chars,Chars_length)。該指針告訴譯碼器“在這個窗口中向後退Back_chars個字符然後拷貝Chars_length個字符到輸出”。
    ②如果匹配串本身的長度Length >= MIN_LENGTH,則輸出真實的匹配串。

表03-05-3 輸入數據流

位置

1

2

3

4

5

6

7

8

9

10

11

字符 

A

A

B

B

C

B

B

A

A

B

C

表03-05-4 編碼過程(MIN_LENGTH = 2)

步驟

位置 

匹配串

輸出

1

1

--

A

2

2

A

A

3

3

--

B

4

4

B

B

5

5

--

C

6

6

B B

(3,2)

7

8

A A B

(7,3)

8

11

C

C

在相同的計算環境下,LZSS算法可獲得比LZ77更高的壓縮比,而譯碼同樣簡單。這也就是爲什麼這種算法成爲開發新算法的基礎。許多後來開發的文檔壓縮程序都使用了LZSS的思想,例如PKZip,ARJ,LHArc和ZOO等等,其差別僅僅是指針的長短、窗口的大小等有所不同。

LZSS同樣可以和熵編碼聯合使用,例如ARJ就與霍夫曼編碼聯用,而PKZip則與Shannon-Fano聯用,它的後續版本也採用霍夫曼編碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章