from:http://data.biancheng.net/view/152.html
RLE壓縮算法(下簡稱RLE算法)的基本思路是把數據按照線性序列分成兩種情況:一種是連續的重復數據塊,另一種是連續的不重復數據塊。
RLE算法的原理就是用一個表示塊數的屬性加上一個數據塊代表原來連續的若干塊數據,從而達到節省存儲空間的目的。一般RLE算法都選擇數據塊的長度為 1 字節,表示塊數的誠性也用1字節表示,對於顏色數小於 256 色的圖像文件或文本文件,塊長度選擇 1 字節是比較合適的。
連續重復數據的處理
RLE 算法有很多優化和改進的變種算法,這些算法對連續重復數據的處理方式基本上都是一樣的。對於連續重復出現的數據,RLE算法一般用兩字節表示原來連續的多字節重復數據。我們用一個例子更直觀地說明 RLE 算法對這種情況的處理,假如原始數據有 5 字節的連續數據:
[data] [data] [data] [data] [data]
則壓縮后的數據就包含塊數和 [data] 兩字節,其中 [data] 只存儲了一次,節省了存儲空間:
[5] [data]
需要注意的是,一般 RLE 算法都采用插入一個長度屬性字節存儲連續數據的重復次數,因此能夠表達的扱大值就是 255 字節,如果連續的相同數據超過 255 字節時,就從第 255 字節處斷開,將第 256 字節以及 256 字節后面的數裾當成新的數椐處理。
隨着 RLE 算法采用的優化方式不同,這個長度屬性字節所表達的意義也不同,對於本節給出的這種優化算法,長度屬性字節的最高位被用來做一個標志位,只有 7 位用來表示長度。
連續非重復數據的處理
對於連續的非重復數據,RLE 算法有兩種處理方法:
- 一種處理方法是將每個不重復的數據當作只重復一次的連續重復數據處理,在算法實現上就和處理連續重復數據一樣;
- 另一種處理方法是不對數據進行任何處理,直接將原始數據作為壓縮后的數據存儲。
假如有以下 5 字節的連續非重復數據:
[datal] [data2] [data3] [data4] [data5]
按照第一種處理方法,最后的壓縮數據就如下所示:
[1][datal] [1][data2] [1][data3] [1][data4] [1][data5]
如果按照第二種處理方法,最后的數據和原始數據一樣:
[data1] [data2] [data3] [data4] [data5]
如果采用第一種方式處理連續非重復數據,則存在一個致命的問題,對連續出現的不重復數據,會因為插入太多塊數屬性字節而膨脹一倍,如果原始數據主要是隨機的非重復數據,則采用這種方式不僅不能起到壓縮數據的目的,反而起到惡化的作用。多數經過優化的 RLE 算法都會選擇使用第二種方式處理連續非重復數據,但是這就引入了新問題,在 RLE 算法解碼的時候,如何區分連續重復和非重復數據?
前面己經提到,如果把非重復數據當作獨立的單次重復數據處理,反而會造成數據膨脹,但是如果把連續非重復數據也當成一組數據整理考慮呢?這是一個優化的思路,首先,給連續重復數據和連續非重復數據都附加一個表示長度的屬性字節,並利用這個長度屬性字 節的最高位來區分兩種情況。
長度屬性字節的最高位如果是 1,則表示后面緊跟的是個重復數據,需要重復的次數由長度屬性字節的低 7 位(最大值是 127)表示。長度屬性字節的最高位如果是 0,則表示后面緊跟的是非重復數據,長度也由長度屬性字節的低 7 位表示。
采用這種優化方式,壓縮后的數據非常有規律,兩種類型的數據都從長度屬性字節開始,除了標志位的不同,后跟的數據也不同。第一種情況后跟一個字節的重復數據,第二種情況后跟的是若干個字節的連續非重復數據。
算法實現
首先介紹一下數據壓縮的編碼過程如何實現。釆用前面給出的優化方式,編碼算法不僅要能夠識別連續重復數據和連續非重復數據兩種情況,還要能夠統計出兩種情況下數據塊的長度。
編碼算法從數據的起始位置開始向后搜索,如果發現后面是重復數據且重復次數超過 2,則設置連續重復數據的標志並繼續向后查找,直到找到第一個與之不相同的數據為止,將這個位置記為下次搜索的起始位置,根據位置差計算重復次數,最后長度屬性字節以及一個字節的原始重復數據一起寫入壓縮數據;如果后面數據不是連續重復數據,則繼續向后搜索查找連續重復數據,直到發現連續重復的數據且重復次數大於 2 為止,然后設置不重復數據標志,將新位置記為下次搜索的起始位置,最后將長度屬性字節寫入壓縮數據並將原始數據逐字節復制到壓縮數據。然后從上一步標記的新的搜索起始位開始,一直重復上面的過程,直到原始數據結束。
- int Rle_Encode(unsigned char *inbuf, int inSize, unsigned char *outbuf, int onuBufSize)
- {
- unsigned char *src = inbuf;
- int i;
- int encSize = 0;
- int srcLeft = inSize;
- while(srcLeft > 0)
- {
- int count = 0;
- if(IsRepetitionStart(src, srcLeft)) /*是否連續三個字節數據相同? */
- {
- if ((encSize + 2) > onuBufSize) /* 輸出緩沖區空間不夠了 */
- {
- return -1;
- }
- count = GetRepetitionCount(src, srcLeft);
- outbuf[encSize++] = count | 0x80;
- outbuf[encSize++] = *src;
- src += count;
- srcLeft -= count;
- }
- else
- {
- count = GetNonRepetitionCount(src, srcLeft);
- if ((encSize + count + 1) > onuBufSize) /* 輸出緩沖區空間不夠了 */
- {
- return -1;
- }
- outbuf[encSize++] = count;
- for(i = 0; i < count; i++) /*逐個復制這些數據*/
- {
- outbuf[encSize++] = *src++;;
- }
- srcLeft -= count;
- }
- }
- return encSize;
- }
Rle_Encode() 函數是 RLE 算法的實現,它通過調用 IsRepetitionStart() 函數判斷從 src 開始的數據是否是連續重復數據:
- 如果是連續重復數據,則調用 GetRepetitionCount() 函數計算出連續重復數據的長度,將長度屬性字節的最高位罝 1 並向輸出緩沖區寫入一個字節的重復數據。
- 如果不是連續重復數據,則調用 GetNonRepetitionCount() 函數計算連續非重復數據的長度,將長度屬性字節的極高位罝 0 並向輸出緩沖區復制連續的多個非重兌數據。
GetRepetitionCount() 函數和 GetNonRepetitionCount() 函數都比較簡單,此處就不列出代碼了。
根據算法要求,只有數裾重復出現兩次以上才算作連續重復數據,因此 IsRepetitionStart() 函數檢査連續的3字節是否是相同的數據,如果是則判定為出現連續重復數據。之所以要求至少要 3 字節的重復數據才判定為連續重復數據,是為了盡量優化對短重復數據間隔出現時的壓縮效率。
舉個例子,對於這樣的數據“AABCCD”,如果不采用這個策略,最終的壓縮數據應該是 [0x82][A][0x01][B][0x82][C][0x01][D],壓縮后數據長度是 8 字節。如果采用這個策略,則上述數據就被認定為連續非重復數據局,最終被壓縮為 [0x06][A][A][B][C][C][D],壓縮后數據長度是 7 字節,這樣的數據越長,效果越明顯。
解壓縮算法相對比較簡單,因為兩種情況下的壓縮數據首部都是 1 字節的長度屬性標識,只要根據這個標識判斷如何處理就可以了。首先從壓縮數據中取出 1 字節的長度屬性標識,然后判斷是連續重復數據的標識還是連續非重復數據的標識:
- 如果是連續重復數據,則將標識字節后面的數據重復復制 n 份寫入輸出緩沖區;
- 如果是連續非重復數據,則將標識字節后面的 n 個數據復制到輸出緩沖區。n 的值是標識字節與 0x3F 做與操作后得到,因為標識字節低 7 位就是數據長度屬性。
- int Rle_Decode(unsigned char *inbuf, int inSize, unsigned char *outbuf, int onuBufSize) {
- unsigned char *src = inbuf;
- int i;
- int decSize = 0;
- int count = 0;
- while(src < (inbuf + inSize))
- {
- unsigned char sign = *src++;
- int count = sign & 0x3F;
- if ((decSize + count) > onuBufSize) /* 輸出緩沖區空間不夠了 */
- {
- return -1;
- }
- if ((sign & 0x80) == 0x80) /* 連續重復數據標志 */
- {
- for(i = 0; i < count; i++)
- {
- outbuf[decSize++] = *src;
- }
- src++;
- }
- else
- {
- for(i = 0; i < count; i++)
- {
- outbuf[decSize++] = *src++;
- }
- }
- }
- return decSize;
- }
Rle_Decode() 函數是解壓縮算法的實現代碼,每組數據的第一字節是長度標識字節,其最高位是標識位,低 7 位是數據長度屬性,根據標識位分別進行處理即可。