當前位置:成語大全網 - 書法字典 - GIF圖像的LZW壓縮算法流程

GIF圖像的LZW壓縮算法流程

LZW數據壓縮算法原理分析

希望通過本文的介紹,能給目前對lzw算法及其在gif圖像中的應用不太了解,但又渴望了解的人壹些啟發和幫助。只是為了引玉,希望園裏的兄弟們提出寶貴意見。

1的全稱是什麽?LZW?

倫佩爾-齊夫-韋爾奇(LZW)。

2.LZW的引入和壓縮原理是什麽?

LZW壓縮算法是壹種新穎的壓縮方法,由萊姆普爾-齊夫-韋爾奇創立,並以他們的名字命名。它采用了高級的字符串表壓縮,將每個第壹個字符串放在壹個字符串表中,並使用壹個數字來表示該字符串。壓縮後的文件只存儲數字,不存儲字符串,大大提高了圖像文件的壓縮效率。神奇的是,這個字符串表在壓縮或解壓縮時都能正確建立,壓縮或解壓縮後又被丟棄。

在LZW算法中,首先建立壹個字符串表,將每個第壹次出現的字符串放入字符串表中,用壹個數字表示,這個數字與字符串在字符串表中的位置有關,這個數字存儲在壹個壓縮文件中。如果該字符串再次出現,它可以被代表它的數字替換,這個數字存儲在文件中。壓縮後丟棄字符串表。例如,如果“print”字符串在壓縮時用266表示,那麽每當它再次出現時就用266表示,“print”字符串將存儲在字符串表中。當圖像解碼過程中遇到數字266時,可以從字符串表中找出用266表示的字符串“print”,解壓時可以根據壓縮數據重新生成字符串表。

3.在詳細介紹算法之前,先列出壹些與算法相關的概念和詞匯。

1)'字符':壹個字符,壹個基本的數據元素,在壹個普通的文本文件中占據1個單獨的字節,但是在壹個圖像中,它是壹個表示給定像素的顏色的索引值。

2)“CharStream”:數據文件中的字符流。

3)“前綴”:前綴。和這個詞的意思壹樣,代表壹個字符之前最直接的字符。前綴字符的長度可以是0,前綴和字符可以組成壹個字符串。

4)‘後綴’:後綴,是壹個字符。壹個字符串可以由(A,B)組成。a是前綴,b是後綴。當A的長度為0時,它代表根,Root。

5)‘Code:代碼,用來表示壹個字符串的位置編碼。

6)“條目”,壹個代碼和它所代表的字符串。

4.壓縮算法的簡單例子並不是要完全實現lzw算法,而是從最直觀的角度來看LZW算法的思想。

LZW壓縮的原始數據ABCCAABCDDAACCDB。

原始數據中只包含四個字符(A,B,C,D),這四個字符可以用壹個2-2位數來表示,0-A,1-B,2-C,3-D,從最直觀的角度來看,原始字符串中有重復的字符:ABCCAABCDDAACCDB,4表示AB,4。

5.5的適用範圍。LZW算法

為了區分代表壹個字符串的值(代碼)和原來的單個數據值(字符串),需要使它們的數值域不重合,用0-3來代表A-D,所以AB必須用大於3的數值來代替。再比如,原來的數值範圍可以用8位表示,所以認為原來的數值範圍是0 ~ 255,壓縮程序生成的標簽範圍不能是0 ~。我們只能從256開始,但這樣會超出8位表示的範圍,所以我們必須至少擴展壹位的數據位數,但這不是增加了1個字符所占用的空間嗎?但是,壹個字符可以代表幾個字符。比如255以前是8位,現在用256來表示254和255這兩個數字是值得的。從這個原理可以看出,LZW算法的適用範圍是原始數據串要有大量重復多次的子串,重復次數越多,壓縮效果越好。相反,越是糟糕,實際上可能越是增加而不是減少。

6.6中的特殊標記。LZW算法

隨著新字符串的發現,標簽將繼續增長。如果原始數據過大,生成的標簽表會越來越大。這時候操作這壹套會造成效率問題。如何避免這個問題?Gif使用lzw算法的方式是,當標簽集足夠大時,它顯然不會增加,所以只需從頭開始。在這個位置,插入了壹個標簽,意味著我重新開始構造字典,之前的標簽全部無效,開始使用新的。

這時,另壹個問題出現了。它有多大?這個標簽集的合適大小是多少?理論上,標簽集越大,壓縮比越高,但開銷也越高。壹般根據處理速度和內存空間來選擇。GIF規範規定12位,如果表達式範圍超過12位,就要推倒重來。為了提高壓縮比,GIF使用了可變字長。比如原來的數據是8位,那麽開始的時候先加1位,初始字長變成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比特就會用完。在另外兩種情況下,初始字長分別為5位和9位。此處引用了壓力例程。

//

//基於“壓縮”的倫佩爾-齊夫壓縮。GIF修改者

//大衛·羅利·(mgardi@watdcsu.waterloo.edu)

//常規定義

靜態只讀int位= 12;

靜態只讀int HSIZE = 5003// 80%的入住率

// GIF圖像壓縮-修改的“壓縮”

//

//基於:compress.c文件壓縮ala IEEE計算機,6月1984。

//

//作者:斯潘塞·w·托馬斯(decvax!哈波。猶他-cs!猶他-gr!托馬斯)

//吉姆·麥基(decvax!麥克瓦克斯!吉姆)

//斯蒂夫·戴維斯(decvax!vax135!petsd!佩奧拉。srd)

// Ken Turkowski (decvax!decwrl!turtlevax!肯)

//詹姆斯·A·伍茲(decvax!ihnp4!艾米斯。頜)

// Joe Orost (decvax!vax135!petsd!喬)

int n _ bits//位數/代碼數

int maxbits = BITS//用戶可設置的最大位數/碼

int maxcode//給定n_bits的最大代碼

int maxmax code = 1 & lt;& lt比特;//永遠不要生成此代碼

int[]htab = new int[HSIZE];//這是壹個用於哈希的包。妳可以很快在裏面找到1把鑰匙。

int[]codetab = new int[HSIZE];

int hsize = HSIZE//用於動態調整表格大小

int free _ ent = 0;//第壹個未使用的條目

//塊壓縮參數——所有代碼用完之後,

//並且壓縮率改變,重新開始。

bool clear _ flg = false

//算法:在

//前綴代碼/下壹個字符組合。我們做了壹個Knuth的變體

//算法D(第3卷,秒。6.4)連同g .克諾特的相對-總理

//輔助探測器。在這裏,模塊化的第壹個探索是讓路

//到更快的異或操作。也使用進行塊壓縮

//自適應復位,從而在壓縮時清除代碼表

//比率減小,但是在表填充之後。可變長度輸出

//此時會重新調整代碼的大小,並生成壹個特殊的清除代碼

//用於解壓縮器。後期添加:根據以下內容構建表格

//文件大小可以顯著提高小文件的速度。請指導

//關於這個實現的問題交給ames!下巴。

int g _ init _ bits

int ClearCode

int EOFCode

//輸出

//

//輸出給定的代碼。

//輸入:

// code:壹個n_bits位的整數。如果== -1,那麽EOF。這假設

//那n _ bits = & ltwordsize - 1。

//輸出:

//將代碼輸出到文件。

//假設:

//字符長度為8位。

//算法:

//維護壹個二進制字符長的緩沖區(這樣8個代碼將

//恰好適合它)。使用VAX insv指令插入每個

//依次編碼。當緩沖區填滿時,清空它並重新開始。

int cur _ accum = 0;

int cur _ bits = 0;

int []掩碼=

{

0x0000,

0x0001,

0x0003,

0x0007,

0x000F,

0x001F,

0x003F,

0x007F,

0x00FF,

0x01FF,

0x03FF,

0x07FF,

0x0FFF,

0x1FFF,

0x3FFF,

0x7FFF,

0x ffff };

//到目前為止這個“數據包”中的字符數

int a _ count

//定義包累加器的存儲

byte[] accum =新字節[256];

// -

public LZWEncoder(int width,int height,byte[] pixels,int color_depth)

{

imgW =寬度;

imgH =身高;

pixAry =像素;

initCodeSize = Math。Max(2,color _ depth);

}

//在當前數據包的末尾添加壹個字符,如果是254

//字符,將數據包刷新到磁盤。

void Add(字節c,流輸出)

{

accum[a _ count++]= c;

if(a _ count & gt;= 254)

沖洗(流出);

}

//清除哈希表

//清除塊壓縮的表

void ClearTable(流輸出)

{

ResetCodeTable(hsize);

free _ ent = clear code+2;

clear _ flg = true

輸出(清除代碼、輸出);

}

//重置代碼表

//全部初始化為-1

void ResetCodeTable(int hsize)

{

for(int I = 0;我& lthsize++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

//設置全局變量:g_init_bits -初始位數

//原始數據的字長。在gif文件中,原始數據的字長可以是1(單色圖像)、4(16色)、8(256色)。

//開頭加1。

//但是當原始數據長度為1時,從3開始。

//所以原來的長度是1->;3,4->5,8->九

//?原始數據的字長是1,為什麽起始長度是3??

//如果+1=2,只能表示四種狀態,clearcode和endcode會用完。所以必須擴展到3。

g _ init _ bits = init _ bits

//設置必要的值

//需要加壹個明確的標記嗎?

//GIF為了提高壓縮比,采用了可變字長(VCL)。比如原始數據是8位,那麽從1位開始(8+1=9)。

//當標簽達到2 ^ 9 = 512,超過當前長度9所能表示的最大值時,那麽後面的標簽就必須用10位來表示。

//以此類推,當標簽達到2 12時,就不能再擴展了,因為最大值是12。妳需要在2 12 = 4096的位置插入壹個明碼,也就是說從現在開始,妳要從9位數重新開始。

clear _ flg = false

n _ bits = g _ init _ bits

//得到可以用n位數表示的最大值(在壹張gif圖片中,壹般是3,5,9開頭,所以maxcode壹般是7,365,438+0,565,438+065,438+0)。

max code = max code(n _ bits);

//表示我已經從這裏開始構造字典了,前面的標記都無效。

//開始使用新的標記。這個標簽集的合適大小是多少?據說理論上壓縮比越大壓縮比越高(個人感覺太大不壹定好)。

//但是,處理成本也成倍增長。

//gif規定clearcode的值是原始數據最大字長可以表示的值+1;例如,如果原始數據長度為8,那麽clear code = 1 < & lt;(9-1)=256

clear code = 1 & lt;& lt(init _ bits-1);

//結束標誌是clearcode+1。

eof code = clear code+1;

//取消到此結束。

free _ ent = clear code+2;

//清除數量

a _ count = 0;//清除數據包

//從圖像中獲取下壹個像素

ent = next pixel();

h shift = 0;

for(fcode = hsize;fcode & lt65536;fcode *= 2)

++ h shift;

//設置哈希代碼範圍

h shift = 8-h shift;//設置哈希代碼範圍界限

hsize _ reg = hsize

//清除存儲標簽的固定大小哈希表,相當於壹個字典。

ResetCodeTable(hsize _ reg);//清除哈希表

輸出(清除代碼、輸出);

outer_loop : while ((c = NextPixel())!= EOF)

{

fcode =(c & lt;& ltmaxbits)+ent;

I =(c & lt;& lt^耳鼻喉科;// xor哈希

//嘿嘿,小樣,又來了,我認識妳。

if (htab == fcode)

{

ent = codetab

繼續;

}

//這小子,初來乍到。

else if(htab & gt;= 0) //非空槽

{

disp = hsize _ reg-I;//二級哈希(在g .克諾特之後)

如果(i == 0)

disp = 1;

{

if((I-= disp)& lt;0)

I+= hsize _ reg;

if (htab == fcode)

{

ent = codetab

goto outer _ loop

}

} while(htab & gt;= 0);

}

輸出(ent,outs);

//由此可以看出,ent是前綴,當前正在處理的字符符號是後綴。

ent = c;

//判斷終止符是否超出當前位數所能表示的範圍。

if(free _ ent & lt;maxmaxcode)

{

//如果沒有超級

codetab = free _ ent++;//code-& gt;散列表

//在//哈希表中建立相應的索引。

htab = fcode

}

其他

//描述超出了目前可以表達的範圍。清空字典,重新開始。

可清除(出局);

}

//輸出最終代碼。

輸出(ent,outs);

輸出(EOFCode,outs);

}

// -

公共空編碼(流操作系統)

{

os。WriteByte(轉換。ToByte(init codesize));//寫入“初始代碼大小”字節

//這個圖像包含多少像素?

剩余= imgW * imgH//重置導航變量

//當前處理的像素索引

curPixel = 0;

Compress(initCodeSize + 1,OS);//壓縮並寫入像素數據

os。write byte(0);//寫塊終止符

}

//將數據包刷新到磁盤,並重置累加器

無效沖洗(流出)

{

if(a _ count & gt;0)

{

出局。WriteByte(轉換。ToByte(a _ count));

出局。Write(accum,0,a _ count);

a _ count = 0;

}

}

///& lt;總結& gt

///得到可以用n位數表示的最大值。

///& lt;/summary & gt;

///& lt;param name = " n _ bits " & gt位數,壹般n _ bits = 9

///& lt;returns & gt最大值,例如n_bits=8,則返回值為2 ^ 8-1 = 255

int MaxCode(int n_bits)

{

return(1 & lt;& ltn _ bits)-1;

}

// -

//返回圖像的下壹個像素

// -

///& lt;總結& gt

///從圖像中獲取下壹個像素

///& lt;/summary & gt;

///& lt;returns & gt& lt/returns & gt;

private int NextPixel()

{

//還有多少像素沒有處理?

//如果不是,返回結束標誌。

if(剩余== 0)

返回EOF

//否則,處理下壹個,並將未處理的像素數設置為-1。

-剩余;

//當前正在處理的像素

int temp = cur pixel+1;

//如果當前處理的像素在像素範圍內

if(temp & lt;pixAry。GetUpperBound( 0))

{

//下壹個像素

byte pix = pixAry[cur pixel++];

返回像素& amp0xff

}

返回0xff

}

///& lt;總結& gt

///將字輸出到輸出流

///& lt;/summary & gt;

///& lt;param name = " code " & gt要輸出的單詞

///& lt;param name = " outs " & gt輸出流

void輸出(int代碼,流輸出)

{

//獲取當前標誌位可以表示的最大標誌值。

當前累計& amp= masks[cur _ bits];

if(cur _ bits & gt;0)

cur _ accum | =(code & lt;& ltcur _ bits);

其他

//如果標誌位為0,則當前標簽為輸入流。

cur _ accum = code

//當前能量標誌的最大漢字長度(9-10-11-12-9-10。。。。。。。)

cur _ bits+= n _ bit;

//如果當前最大長度大於8

while(cur _ bits & gt;= 8)

{

//向流中輸出壹個字節。

Add((字節)(cur _ accum & amp0xff)、outs

//將當前標簽向右移動8位。

cur _ accum & gt& gt= 8;

cur _ bits-= 8;

}

//如果下壹個條目對於代碼大小來說太大,

//然後增加,如果可能的話。

if(free _ ent & gt;maxcode || clear_flg)

{

if (clear_flg)

{

max code = max code(n _ bits = g _ init _ bits);

clear _ flg = false

}

其他

{

++ n _ bits;

if (n_bits == maxbits)

maxcode = maxmaxcode

其他

max code = max code(n _ bits);

}

}

if (code == EOFCode)

{

//在EOF時,寫入緩沖區的其余部分。

while(cur _ bits & gt;0)

{

Add((字節)(cur _ accum & amp0xff)、outs

cur _ accum & gt& gt= 8;

cur _ bits-= 8;

}

沖洗(流出);

}

}

}

}