熵编码是无损压缩编码方法,它生成的码流可以经熵解码无失真地恢复出数据。
对应熵编码方案,在H.264标准中也有3种熵解码方案: 指数哥伦布解码,CAVLC解码和CABAC解码。在标准中通过描述子(Descriptor)的形式来说明一个语法元素熵解码的方法。对于片层以上的语法元素有定长解码和变长解码方案;对于片层以下的语法元素,当entropy_coding_mode_flag为0时使用指数哥伦布和CAVLC解码,为1时使用CABAC解码。
//coeffLevel存放着解析后的系数
residual_block_cavlc(int coeffLevel, int maxNumCoeff )
{
for( i = 0; i < maxNumCoeff; i++ )
coeffLevel[ i ] = 0;
int coeff_token = ce(v); //根据(标准表9-5)计算出非零系数数目(TotalCoeff)、拖尾系数数目(TrailingOnes)
if( TotalCoeff( coeff_token ) > 0 ) //非零系数的数目 > 0
{
int suffixLength; //后缀长度
if( TotalCoeff( coeff_token ) > 10 && TrailingOnes( coeff_token ) < 3 )
suffixLength = 1;
else
suffixLength = 0;
for( i = 0; i < TotalCoeff( coeff_token ); i++ )
{
if( i < TrailingOnes( coeff_token ) ) //解码拖尾系数的符号
{
int trailing_ones_sign_flag = u(1);
level[ i ] = 1 – 2 * trailing_ones_sign_flag;
}
else //解码拖尾系数之外的非零系数的幅值(Levels)
{
int level_prefix = ce(v); //从当前位置不断读取0,直至读到1为止,则0的个数就是level_prefix的值(标准表9-6)
int levelCode = ( Min( 15,level_prefix ) << suffixLength );
if( suffixLength > 0 || level_prefix >= 14 )
{
int level_suffix = u(v); //读入v位并解释成无符号整数
levelCode += level_suffix;
}
if( level_prefix >= 15 && suffixLength == 0 )
levelCode += 15;
if( level_prefix > = 16 )
levelCode += ( 1 << ( level_prefix – 3 ) ) – 4096;
if( i == TrailingOnes( coeff_token ) && TrailingOnes( coeff_token ) < 3 )
levelCode += 2;
//将无符号的levelCode转换成有符号的level
if( levelCode % 2 == 0 )
level[ i ] = ( levelCode + 2 ) >> 1;
else
level[ i ] = ( -1*levelCode – 1 ) >> 1;
//更新suffixLength,隐含了上下文自适应过程
if( suffixLength == 0 )
suffixLength = 1;
if( Abs( level[ i ] ) > ( 3 << ( suffixLength – 1 ) ) && suffixLength < 6 )
suffixLength++;
}
int zerosLeft;
if( TotalCoeff( coeff_token ) < maxNumCoeff )
{
int total_zeros = ce(v); //系数中0的总个数(标准表9-7,9-8,9-9)
zerosLeft = total_zeros;
}
else
zerosLeft = 0;
for( i = 0; i < TotalCoeff( coeff_token ) – 1; i++ )
{
if( zerosLeft > 0 )
{
int run_before = ce(v); //解码非零系数前零的个数(标准表9-10)
run[ i ] = run_before;
} else
run[ i ] = 0;
zerosLeft = zerosLeft – run[ i ];
}
run[ TotalCoeff( coeff_token ) – 1 ] = zerosLeft; //run[i]记录了第i个非零系数前0的个数
int coeffNum = -1;
for( i = TotalCoeff( coeff_token ) – 1; i >= 0; i--)
{
coeffNum += run[ i ] + 1
coeffLevel[ coeffNum ] = level[ i ];
}
}
}
}
在H.264中,使用CABAC需要进行二值化处理,而指数哥伦布编码就是CABAC的一种二值化处理的方法。k阶指数哥伦布编解码具体过程如下:
A、编码过程:假设待编码数字为CodeNum(必须非负整数)
指数哥伦布编码后的形式为[MZeors][1][Info],MZero表示M个0。
1、将CodeNum以二进制形式表示(若不足k位,前面补0),去掉后面k位(若刚好是k位,去掉k位后得0),将结果(数值)加1,得到二进制数T1;
2、M为二进制数T1的二进制位数减一;
3、然后将第一步中舍去的k位接到T1结尾,就得到[1][Info]。
设[Info]的二进制位数为I,编码过程也可以如下描述:
[1 Info] 是CodeNum+2^k的二进制表示,MZeros中0的个数M = I - k。
于是就有总的编码长度CodeLen = M + 1 + I =2M+k+1。
B、解码过程:
1、读入连续0,连续0的个数就是M;
2、计算CodeLen = 2M+k+1,得到[1 Info]的位数是 I=CodeLen - M =M+K+1;
3、读入I位二进制码字,转换成10进制,假设为W。由W = CodeNum + 2^k,得CodeNum = W-2^K。
C、示例:
对于 k =0时:CodeNum=3。
编码如下:
二进制表示为11,去掉k=0位后加1得100;
所以M=2;
所以编码后结果为[MZeros][1][Info] = [MZeros][1 Info] = 00100
解码如下:
读入连续2个0,所以M=2;CodeLen=2M+1+k=5;所以需要再读入3个码流100,[1 Info]就是100,转成十进制结果W为4,所以CodeNum = W-2^K=4-1=3;
同样对于k=0,CodeNum=6时,编码为:00111;
同样对于k=3,CodeNum=3时,编码为:1011;
同样对于k=3,CodeNum=6时,编码为:1110;