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 为止,然后设置不重复数据标志,将新位置记为下次搜索的起始位置,最后将长度属性字节写入压缩数据并将原始数据逐字节复制到压缩数据。然后从上一步标记的新的搜索起始位开始,一直重复上面的过程,直到原始数据结束。

  1. int Rle_Encode(unsigned char *inbuf, int inSize, unsigned char *outbuf, int onuBufSize)
  2. {
  3. unsigned char *src = inbuf;
  4. int i;
  5. int encSize = 0;
  6. int srcLeft = inSize;
  7. while(srcLeft > 0)
  8. {
  9. int count = 0;
  10. if(IsRepetitionStart(src, srcLeft)) /*是否连续三个字节数据相同? */
  11. {
  12. if ((encSize + 2) > onuBufSize) /* 输出缓冲区空间不够了 */
  13. {
  14. return -1;
  15. }
  16. count = GetRepetitionCount(src, srcLeft);
  17. outbuf[encSize++] = count | 0x80;
  18. outbuf[encSize++] = *src;
  19. src += count;
  20. srcLeft -= count;
  21. }
  22. else
  23. {
  24. count = GetNonRepetitionCount(src, srcLeft);
  25. if ((encSize + count + 1) > onuBufSize) /* 输出缓冲区空间不够了 */
  26. {
  27. return -1;
  28. }
  29. outbuf[encSize++] = count;
  30. for(i = 0; i < count; i++) /*逐个复制这些数据*/
  31. {
  32. outbuf[encSize++] = *src++;;
  33. }
  34. srcLeft -= count;
  35. }
  36. }
  37. return encSize;
  38. }

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 位就是数据长度属性。
  1. int Rle_Decode(unsigned char *inbuf, int inSize, unsigned char *outbuf, int onuBufSize) {
  2. unsigned char *src = inbuf;
  3. int i;
  4. int decSize = 0;
  5. int count = 0;
  6. while(src < (inbuf + inSize))
  7. {
  8. unsigned char sign = *src++;
  9. int count = sign & 0x3F;
  10. if ((decSize + count) > onuBufSize) /* 输出缓冲区空间不够了 */
  11. {
  12. return -1;
  13. }
  14. if ((sign & 0x80) == 0x80) /* 连续重复数据标志 */
  15. {
  16. for(i = 0; i < count; i++)
  17. {
  18. outbuf[decSize++] = *src;
  19. }
  20. src++;
  21. }
  22. else
  23. {
  24. for(i = 0; i < count; i++)
  25. {
  26. outbuf[decSize++] = *src++;
  27. }
  28. }
  29. }
  30. return decSize;
  31. }

Rle_Decode() 函数是解压缩算法的实现代码,每组数据的第一字节是长度标识字节,其最高位是标识位,低 7 位是数据长度属性,根据标识位分别进行处理即可。

RLE压缩算法详解的更多相关文章

  1. Hive存储格式之RCFile详解,RCFile的过去现在和未来

    我在整理Hive的存储格式和压缩格式,本来打算一篇发出来,结果其中一小节就有很多内容,于是打算写成Hive存储格式和压缩格式系列. 本节主要讲一下Hive存储格式最早的典型的列式存储格式RCFile. ...

  2. JPEG图像压缩算法流程详解

    JPEG图像压缩算法流程详解 JPEG代表Joint Photographic Experts Group(联合图像专家小组).此团队创立于1986年,1992年发布了JPEG的标准而在1994年获得 ...

  3. PNG,JPEG,BMP,JIF图片格式详解及其对比

    原文地址:http://blog.csdn.net/u012611878/article/details/52215985 图片格式详解 不知道大家有没有注意过网页里,手机里,平板里的图片,事实上,图 ...

  4. 【转】jpeg文件格式详解

    JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写.它由国际电话与电报咨询委员会CCITT(The International Telegraph ...

  5. 【转】jpg文件格式详解

    JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写.它由国际电话与电报咨询委员会CCITT(The International Telegraph ...

  6. BMP格式详解

    BMP格式详解 BMP文件格式详解(BMP file format) BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),是Win ...

  7. JPEG文件编/解码详解

    JPEG文件编/解码详解(1) JPEG(Joint Photographic Experts Group)是联合图像专家小组的英文缩写.它由国际电话与电报咨询委员会CCITT(The Interna ...

  8. H264编码原理以及I帧、B和P帧详解, H264码流结构分析

    H264码流结构分析 http://blog.csdn.net/chenchong_219/article/details/37990541 1.码流总体结构: h264的功能分为两层,视频编码层(V ...

  9. 关于IPB帧与恒定比特率、动态比特率的详解

    之所以写这篇文章是因为有朋友对IPB帧的设置比较感兴趣,回复中说得比较简单,因此在这里详细的写一下,虽然说一般情况下我们很少去设置这个IPB帧,不过,如果真的学好了,并且清楚的了解了这个IPB帧的概念 ...

随机推荐

  1. [P3935] Calculating - 整除分块

    容易发现题目要求的 \(f(x)\) 就是 \(x\) 的不同因子个数 现在考虑如何求 \(\sum_{i=1}^n f(i)\),可以考虑去算每个数作为因子出现了多少次,很容易发现是 \([n/i] ...

  2. Nginx绑定IP,解决session共享

    1.Nginx通过负载均衡IP地址固定绑定,解决Session共享           upstream note.java.itcast.cn{    ip_hash;        server ...

  3. update(十)

    Vue 的 _update 是实例的一个私有方法,它被调用的时机有 2 个,一个是首次渲染,一个是数据更新的时候:由于我们这一章节只分析首次渲染部分,数据更新部分会在之后分析响应式原理的时候涉及._u ...

  4. C++常用字符串操作和UTF-8和GBK之间的转换以及判定(转)

    编码转换原文地址:https://www.cnblogs.com/Toney-01-22/p/9935297.html C++字符串常用操作:C++ 中字符串查找.字符串截取.字符串替换

  5. git上传代码技巧

    1.一定要先在git上面创建项目然后把文件拉到本地先 1.1克隆到本地(orgin=>'你的远程仓库地址') git clone orgin 1.2初始化项目文件夹 git init 2.操作之 ...

  6. Ant风格表达式

    1. ?:匹配任意一个字符 *   :匹配0或者任意数量的字符 **  :匹配0或更多的目录

  7. 继 “多闪”后“飞聊”再被diss?其实社交还能这么玩

    近日头条低调上线了新的社交APP——飞聊,目前在AppStore社交排行榜第7位.但很多人使用了之后都觉得新产品的各个功能都让人想起其他的产品.兴趣小组让人想到豆瓣的兴趣小组,生活动态让人想到微博动态 ...

  8. 编码 - 调整 gitbash 文本字符集

    概述 gitbash 设置 文本字符集 背景 最近被 编码 的事情搞得乱七八糟 有点没头绪, 所以碰到 编码相关 的东西, 都想看上一看 环境 os win10.1903 git 2.20.1 1. ...

  9. 【资源分享】Gmod日志记录脚本

    *----------------------------------------------[下载区]----------------------------------------------* ...

  10. C# 后台调用http,post访问url,获取数据

    1.封装post方法发送 using System; using System.Collections.Generic; using System.IO; using System.Linq; usi ...