【编解码】霍夫曼编解码

一、霍夫曼编码

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个差值数据。根据前一帧数据和解得的差值数据计算出当前参考帧的图像数据,完成了参考帧图像的解码。

    原文作者:游程编码问题
    原文地址: https://blog.csdn.net/heiheiya/article/details/81226904
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞