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