一、霍夫曼编码
1.文件源
原始图像分块为(8*8),在RLE编码之后做霍夫曼编码。其中RLE码字格式如下。
struct RLECODE
{
uchar runLen; //码字个数
uchar sizeLen; //码字长度
short codeWord; //RLE码字
};
2.构建霍夫曼表
本文中使用的为JPEG标准AC_Y霍夫曼表。构建一个256维大小的码表huffTable,其霍夫曼码字结构如下。
struct HUFFCODE
{
unsigned short code; // huffman 码字
uchar length; // 编码长度
unsigned short val; // 码字对应的值
};
3.关键帧霍夫曼编码
(8*8*3)的3通道图像块编码为rleBuf[rleLength],
for(int i = 0; i < rleLength; i++)
{
RLECODE tempRle = rleBuf[i];
//如果码字长度为0,表示码字为0,
if(tempRle.sizeLen == 0)
{
//RLE码字为0处理方式
ProcessZero();
}
else
{
//RLE码字非0处理方式
ProcessNonZero();
}
}
RLE码字为0时,以霍夫曼表中索引为0处的码字表示0,既写入huffTable[0]的相应码字,每达到15个0则写入一个huffTable [0xF0]码字表示15个0,解码与之对应。其表示如下:
ProcessZero()
{
HUFFCODE tempHuff;
int runLen = tempRle. runLen /15;
for (int j = 0; j < runLen; j++)
{
tempHuff = m_huffCode[0xF0];
//写入huffTable [0xF0]码字的二进制流,既表示15个0
WriteBitsStream(tempHuff.code,tempHuff.length);
}
//写入剩余0的码字
for (int j = 0; j < (tempRle. runLen %15); j ++)
{
tempHuff = m_huffCode[0];
WriteBitsStream(tempHuff.code,tempHuff.length);
}
}
RLE码字为0时的第二种处理方式:写入huffTable[0]的相应码字代表该元素是0,没有对0进行编码,所有其编码长度为0.
ProcessZero()
{
writeBits(acHuffTab[0x00], fp);
将连0的个数tempRle. runLen写入另一文件中;
}
RLE码字为非0时,写入对应索引处码字的二进制流,以及该码字的相应编码,其索引计算方式如下。
//数据二进制编码格式
struct BINCODE
{
short binCode; //二进制码字
uchar codeLen; //码字长度
};
ProcessNonZero()
{
HUFFCODE tempHuff;
//RLE中runLen范围为1-16,因此需要减1,解码进行相应处理
int huffTableIndex = (tempRle. runLen-1)*16 + tempRle. sizeLen;
tempHuff = huffTable [huffTableIndex];
//写入相应霍夫曼码字的二进制流
WriteBitsStream(tempHuff.code, tempHuff.length);
//RLE码字的二进制编码
BINCODE temp Bin= BuildSym2(tempRle.amplitude);
//写入RLE码字二进制编码的二进制流
WriteBitsStream(tempBin. binCode, tempBin.codeLen);
}
二进制编码如下表所示,为其数值以及对应的二进制值,以及长度。给定一个值value,其处理过程如下
BINCODE BuildSym2(value)
{
BINCODE temp Bin;
tempBin.codeLen = ComputeVLI(value); //计算编码长度
if (value >= 0)
{
Symbol. binCode = value;
}
else
{
//计算反码
Symbol.amplitude = short(pow(2.0, tempBin.codeLen)-1) + value;
}
return tempBin;
}
//返回符号的长度
uchar ComputeVLI(short val)
{
uchar binStrLen = 0;
val = abs(val);
//获取二进制码长度
if( val == 1)
{
binStrLen = 1;
}
else if(val >= 2 && val <= 3)
{
binStrLen = 2;
}
.
.
.
return binStrLen;
}
对于元素的编码长度根据下面的标准确定:(由元素的绝对值确定码长)
元素值范围 | 编码长度 | 元素值范围 | 编码长度 |
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 | — | — |
4.参考帧霍夫曼编码
将图像参考帧标记为不同的块,存入相应的差值,short类型,并进行RLE编码,然后进行霍夫曼编码,方法与3中的关键帧霍夫曼编码相同,此处不再赘述。
5.依次循环完成所有图像霍夫曼编码。
二、霍夫曼解码
源文件存储的是二进制码流。解码过程如下:
1)取得一个在huffTable中索引为index的Huffman码(使用Huffman表的JPEG标准AC_Y霍夫曼表);
2) 取得index的高四位是该数值的个数sameCount,取得低四位表示该数值的二进制位数codeLen;
3)根据低四位从二进制码流中取得相应位数的数据value;
4)根据value以及codeLen求得实际数值。
if(value的codeLen-1位为0)
{
value -= 2codeLen-1;
}
else
{
value不变
}
5)根据sameCount将(sameCount+1)个value保存在一个192大小的buffer中。
重复步骤1)-5),直到保存192个数据为止。
1. 关键帧解码
根据以上步骤,解得所有分块的192个数据。将分块数据合并为图像数据,完成了关键帧图像的解码。
2. 参考帧解码
根据以上步骤,解得所有差值块的192个差值数据。根据前一帧数据和解得的差值数据计算出当前参考帧的图像数据,完成了参考帧图像的解码。