(转载)很久没看264了,前几天突发奇想,想把264的熵编码方式改成全精细渐进模式。虽然我知道有分级编码中也有了精细分级方式,但是我还是想试试。
当年硕士的时候,我就剩下CABAC、码流格式没看懂,当时碰到那一堆函数就气馁。后来转到图像压缩,又不可避免的涉及到算术编码,后来花了大力气终于清楚了所以然。较常规的CABAC方式,精细分级方式肯定会出现一定的性能下降,所以需要先看看CABAC利用了残差数据的那些统计特性,也正好补一下以前的空缺。
打开x264,搜索x264_macroblock_write_cabac函数,进入残差编码block_residual_write_cabac函数,首先是
if( i_count != 64 )
{
/* coded block flag */
x264_cabac_encode_decision( cb, 85 + x264_cabac_mb_cbf_ctxidxinc( h, i_ctxBlockCat, i_idx ), i_coeff != 0 );
if( i_coeff == 0 )
return;
}
这是判断残差块是否全为0,如果全为0则返回。x264_cabac_encode_decision函数的第二个变量是算术编码的模板,第三个变量是所要算术编码的值。
for( i = 0; i < i_sigmap_size; i++ )
{
int i_sig_ctxIdxInc;
int i_last_ctxIdxInc;
if( i_ctxBlockCat == DCT_LUMA_8x8 )
{
i_sig_ctxIdxInc = significant_coeff_flag_offset_8x8[i];
i_last_ctxIdxInc = last_coeff_flag_offset_8x8[i];
}
else
i_sig_ctxIdxInc = i_last_ctxIdxInc = i;
x264_cabac_encode_decision( cb, i_ctx_sig + i_sig_ctxIdxInc, l[i] != 0 );
if( l[i] != 0 )
x264_cabac_encode_decision( cb, i_ctx_last + i_last_ctxIdxInc, i == i_last );
}
这是依次判断非零系数的位置,如果是非零系数,还要判断该数是否是最后一个非零系数。
for( i = i_coeff - 1; i >= 0; i-- )
{
/* write coeff_abs - 1 */
const int i_prefix = X264_MIN( i_coeff_abs_m1[i], 14 );
const int i_ctxIdxInc = (i_abslevelgt1 ? 0 : X264_MIN( 4, i_abslevel1 + 1 )) + i_ctx_level; // i_abslevel1是已编码的幅值为1的个数
x264_cabac_encode_decision( cb, i_ctxIdxInc, i_prefix != 0 ); //是否正负1
if( i_prefix != 0 )
{
const int i_ctxIdxInc = 5 + X264_MIN( 4, i_abslevelgt1 ) + i_ctx_level; // i_abslevelgt1是已编码的幅值大于1的个数
int j;
for( j = 0; j < i_prefix - 1; j++ )
x264_cabac_encode_decision( cb, i_ctxIdxInc, 1 );
if( i_prefix < 14 )
x264_cabac_encode_decision( cb, i_ctxIdxInc, 0 );
else /* suffix */
x264_cabac_encode_ue_bypass( cb, 0, i_coeff_abs_m1[i] - 14 );//哥伦布编码的算术形式
i_abslevelgt1++;
}
else
i_abslevel1++;
/* write sign */
x264_cabac_encode_bypass( cb, i_coeff_sign[i] ); //编码非零系数的符号
}
这里就是编码幅值和正负号了。倒序编码,幅值编码要复杂点,先编码是否是“1”,模板由已编码“1”的个数确定。如果大于1,就依次判断是否是“2”、是否是“3”….一直到“14”,模板由已编码大于1的个数确定。如果大于14,就用哥伦布编码剩下的差值。
总的来看,CABAC在非零系数的位置和符号编码上很简单,最复杂、压缩效率最高的就是幅值编码。一般而言,高频的幅值小于低频系数,幅值从高频到低频编码,根据高频的幅值大小,编码后续低频系数时选择不同的算术编码模板,从而达到高效的目的。