001-RLE算法
一、定义
RLE全称(run-length encoding),翻译为游程编码,又译行程长度编码,又称变动长度编码法(run coding),在控制论中对于二值图像而言是一种编码方法,对连续的黑、白像素数(游程)以不同的码字进行编码。游程编码是一种简单的非破坏性资料压缩法,其好处是加压缩和解压缩都非常快。其方法是计算连续出现的资料长度压缩之。
是一个针对无损压缩的非常简单的算法。它用重复字节和重复的次数来简单描述来代替重复的字节。尽管简单并且对于通常的压缩非常低效,但它有的时候却非常有用(例如, JPEG 就使用它)。
二、特点
一种压缩过的位图文件格式,RLE压缩方案是一种极其成熟的压缩方案,特点是无损失压缩,既节省了磁盘空间又不损失任何图像数据。
游程编码是一种统计编码,该编码属于无损压缩编码。对于二值图有效。其在对图像数据进行编码时,沿一定方向排列的具有相同灰度值的像素可看成是连续符号,用字串代替这些连续符号,可大幅度减少数据量。
行程编码是连续精确的编码,在传输过程中,如果其中一位符号发生错误,即可影响整个编码序列,使行程编码无法还原回原始数据。
游程编码所能获得的压缩比有多大,主要取决于图像本身的特点。如果图像中具有相同颜色的图像块越大,图像块数目越少,获得的压缩比就越高。反之,压缩比就越小。
三、缺点
在打开这种压缩文件时,要花费更多时间,此外,一些兼容性不太好的应用程序可能会打不开。
不过RLE还有一个缺点,那要是内容像ABCABCABC的话使用这种算法文件会增大,就是1A1B1C1A1B1C1A1B1C了,更长,就达不到压缩的效果了。简单来说,游程编码就是用一个符号值或串长代替具有相同值的连续符号(连续符号构成了一段连续的“行程”。行程编码因此而得名),使符号长度少于原始数据的长度。
四、原理
显示了一个如何使用 RLE 算法来对一个数据流编码的例子,其中出现六次的符号‘ 93 ’已经用 3 个字节来代替:一个标记字节(‘ 0 ’在本例中)重复的次数(‘ 6 ’)和符号本身(‘ 93 ’)。
RLE 解码器遇到符号‘ 0 ’ 的时候,它表明后面的两个字节决定了需要输出哪个符号以及输出多少次。
RLE算法的基本思路是把数据按照线性序列分成两种情况:一种是连续的重复数据块,另一种是连续的不重复数据块。对于第一种情况,对连续的重复数据块进行压缩,压缩方法就是用一个表示块数的属性加上一个数据块代表原来连续的若干块数据。对于第二种情况,RLE算法有两种处理方法,一种处理方法是用和第一种情况一样的方法处理连续的不重复数据块,仅仅是表示块数的属性总是1;另一种处理方法是不对数据进行任何处理,直接将原始数据作为压缩后的数据。
五、实现
RLE 可以使用很多不同的方法。基本压缩库中详细实现的方式是非常有效的一个。一个特殊的标记字节用来指示重复节的开始,而不是对于重复非重复节都 coding run 。
因此非重复节可以有任意长度而不被控制字节打断,除非指定的标记字节出现在非重复节(顶多以两个字节来编码)的稀有情况下。为了最优化效率,标记字节应该是输入流中最少出现的符号(或许就不存在)。
重复 runs 能够在 32768 字节的时候运转。少于 129 字节的要求 3 个字节编码(标记 + 次数 + 符号),而大雨 128 字节要求四个字节(标记 + 次数的高 4 位 |0x80+ 次数的低 4 位)。这是通常所有采用的压缩的做法,并且也是相比较三个字节固定编码(允许使用 3 个字节来编码 256 个字节)而言非常少见的有损压缩率的方法。
在这种模式下,最坏的压缩结果是:输出大小 =257/256* 输入大小 +1
java代码实现:地址
import java.util.regex.Matcher; import java.util.regex.Pattern; public class RunLengthEncoding { public static String encode(String source) { StringBuffer dest = new StringBuffer(); for (int i = 0; i < source.length(); i++) { int runLength = 1; while (i+1 < source.length() && source.charAt(i) == source.charAt(i+1)) { runLength++; i++; } dest.append(runLength); dest.append(source.charAt(i)); } return dest.toString(); } public static String decode(String source) { StringBuffer dest = new StringBuffer(); Pattern pattern = Pattern.compile("[0-9]+|[a-zA-Z]"); Matcher matcher = pattern.matcher(source); while (matcher.find()) { int number = Integer.parseInt(matcher.group()); matcher.find(); while (number-- != 0) { dest.append(matcher.group()); } } return dest.toString(); } public static void main(String[] args) { String example = "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW"; System.out.println(encode(example)); System.out.println(decode("1W1B1W1B1W1B1W1B1W1B1W1B1W1B")); } }