一、游程编码
游程编码属于无损压缩编码,对二指图像非常有效,编解码都很简单。
游程编码的原理: 用一个符号值或串长代替具有相同值的连续符号,使符号长度少于原始数据长度。
例如:5555557777733322221111111
经过游程编码后的序列为:(5,6)(7,5)(3,3)(2,4)(1,7)。
1、处理DIFF文件数据
在做差分编码时,为了尽量压缩数据,做了如下处理:
差值diff在[-127,127]之间则diff +=128转换为uchar类型存储在相应的uchar块结构中,若diff < -127或者diff > 127则将其存储在后面的short块结构中,同 时uchar结构块的相应位置标记为0。
在做游程编码时,为统一数据类型,需将diff文件中的差值全部恢复成short类型的数据,而关键帧的数据依然保持uchar类型。
2、编码
定义结构体
struct RLEstru
{
uchar runLength;//行程,代表相同元素的个数
uchar codeLen; //该元素的编码长度
short RLEValue; //元素值
};
对于元素的编码长度根据下面的标准确定:(由元素的绝对值确定码长)
元素值范围 | 编码长度 | 元素值范围 | 编码长度 |
1 | 1 | [256,511] | 9 |
[2,3] | 2 | [512,1023] | 10 |
[4,7] | 3 | [1024,2047] | 11 |
[8,15] | 4 | [2048,4095] | 12 |
[16,31] | 5 | [4096,8191] | 13 |
[32,63] | 6 | [8192,16383] | 14 |
[64,127] | 7 | [16384,32767] | 15 |
[128,255] | 8 | — | — |
每次从diff文件中读取一个像素块RGB三通道值192个元素进行编码。编码相同元素个数不得超过16个,若多于16个,则将当前元素值及游程16保存为一个编码项struct RLEstru,然后重新计数行程。
下面是游程编码的伪代码:
const uchar MAXRUNLENGTH = 16; //最大允许行程
uint runLength = 1; //行程长度
for (int i=0; i<数据量-1; i++)
{
//相邻元素相同
if (前一元素 == 下一元素 && runLength <= MAXRUNLENGTH)
{
runLength++;
}
//相邻元素不同
else
{
struct RLEstru[RLECount].RLEValue =前一元素;
struct RLEstru[RLECount].runLength = runLength;
struct RLEstru[RLECount].codeLen =前一元素的编码长度;
RLECount++; //该块数据编码后的struct RLEstru个数
runLength = 1;
}
}
//边界处理
struct RLEstru[RLECount].RLEValue = 最后一个元素;
struct RLEstru[RLECount].runLength = runLength;
struct RLEstru[RLECount].codeLen =最后一个元素的编码长度;
RLECount++;
3、写入文件
一块数据编码完成后,首先将该块数据编码后的struct RLEstru个数RLECount写入文件,然后将编码后的数据写入文件。重复步骤2直到所有数据处理完。
二、游程解码
游程解码与差分解码对文件的读取流程基本一致,这里省略,只描述游程解码的具体步骤。
1. 读取struct RLEstru个数RLECount。
2. 读取RLECount * sizeof(struct RLEstru)字节数据。
3. 解码:
int blockDataCount = 0; //解码数据个数计数
for (int i=0; i<RLECount; i++)
{
for (int j =0; j<编码数据[i].runLength; j++) //当前元素的行程
{
解码数据[blockDataCount] = 编码数据[i].RLEValue;
blockDataCount ++;
}
}
重复步骤1、2、3直到所有数据处理完。