爲了減少殘差係數使用的編碼比特,x264中使用了一種方法--decimation。原話是這樣子的:
Writing the 16 CBFs in an i16x16 block is quite costly, so decimation can save many bits. More useful with CAVLC, but still useful with CABAC. 中心思想是如果一個16x16塊的非零殘差係數個數很少且最大絕對值不超過1(用一個score來衡量),直接將其所有4x4塊的殘差係數置爲0,這樣子就不要爲該16x16塊發送16個cbf;
對於亮度,如果是8x8塊,則score<4時,將殘差係數全置爲0;
如果是16x16塊,則score<6時,將殘差係數全置爲0;
對於色度,則score<7時,將殘差係數全置爲0;
該技術一般用於幀間宏塊,總共在下面幾個函數應用:
x264_mb_encode_i16x16;
x264_macroblock_encode_p8x8_internal;
x264_macroblock_probe_skip_internal;
x264_macroblock_encode_internal;
x264_mb_encode_chroma_internal;
以x264_mb_encode_i16x16爲例來看看:
static void x264_mb_encode_i16x16( x264_t *h, int p, int i_qp )
{
pixel *p_src = h->mb.pic.p_fenc[p];
pixel *p_dst = h->mb.pic.p_fdec[p];
ALIGNED_ARRAY_N( dctcoef, dct4x4,[16],[16] );
ALIGNED_ARRAY_N( dctcoef, dct_dc4x4,[16] );
int nz, block_cbp = 0;
int decimate_score = h->mb.b_dct_decimate ? 0 : 9;//I幀設置爲9,p幀設置爲0;
int i_quant_cat = p ? CQM_4IC : CQM_4IY;
int i_mode = h->mb.i_intra16x16_pred_mode;
if( h->mb.b_lossless )
x264_predict_lossless_16x16( h, p, i_mode );
else
h->predict_16x16[i_mode]( h->mb.pic.p_fdec[p] );
if( h->mb.b_lossless )
{
for( int i = 0; i < 16; i++ )
{
int oe = block_idx_xy_fenc[i];
int od = block_idx_xy_fdec[i];
nz = h->zigzagf.sub_4x4ac( h->dct.luma4x4[16*p+i], p_src+oe, p_dst+od, &dct_dc4x4[block_idx_yx_1d[i]] );
h->mb.cache.non_zero_count[x264_scan8[16*p+i]] = nz;
block_cbp |= nz;
}
h->mb.i_cbp_luma |= block_cbp * 0xf;
h->mb.cache.non_zero_count[x264_scan8[LUMA_DC+p]] = array_non_zero( dct_dc4x4, 16 );
h->zigzagf.scan_4x4( h->dct.luma16x16_dc[p], dct_dc4x4 );
return;
}
CLEAR_16x16_NNZ( p );
h->dctf.sub16x16_dct( dct4x4, p_src, p_dst );//對所有16x16殘差係數進行dct變換;
if( h->mb.b_noise_reduction )
for( int idx = 0; idx < 16; idx++ )
h->quantf.denoise_dct( dct4x4[idx], h->nr_residual_sum[0], h->nr_offset[0], 16 );
for( int idx = 0; idx < 16; idx++ )//將每個4x4小塊的dc係數置0;
{
dct_dc4x4[block_idx_xy_1d[idx]] = dct4x4[idx][0];
dct4x4[idx][0] = 0;
}
if( h->mb.b_trellis )
{
for( int idx = 0; idx < 16; idx++ )
if( x264_quant_4x4_trellis( h, dct4x4[idx], i_quant_cat, i_qp, ctx_cat_plane[DCT_LUMA_AC][p], 1, !!p, idx ) )
{
block_cbp = 0xf;
h->zigzagf.scan_4x4( h->dct.luma4x4[16*p+idx], dct4x4[idx] );
h->quantf.dequant_4x4( dct4x4[idx], h->dequant4_mf[i_quant_cat], i_qp );
if( decimate_score < 6 ) decimate_score += h->quantf.decimate_score15( h->dct.luma4x4[16*p+idx] );
h->mb.cache.non_zero_count[x264_scan8[16*p+idx]] = 1;
}
}
else
{
for( int i8x8 = 0; i8x8 < 4; i8x8++ )//遍歷4個8x8塊;
{
nz = h->quantf.quant_4x4x4( &dct4x4[i8x8*4], h->quant4_mf[i_quant_cat][i_qp], h->quant4_bias[i_quant_cat][i_qp] );//量化,並且返回每個4x4小塊是否有非零係數;
if( nz )
{
block_cbp = 0xf;
FOREACH_BIT( idx, i8x8*4, nz )//遍歷每個有非零係數的4x4小塊;
{
h->zigzagf.scan_4x4( h->dct.luma4x4[16*p+idx], dct4x4[idx] );//掃描;
h->quantf.dequant_4x4( dct4x4[idx], h->dequant4_mf[i_quant_cat], i_qp );//反量化;
if( decimate_score < 6 ) decimate_score += h->quantf.decimate_score15( h->dct.luma4x4[16*p+idx] );//對掃描後的殘差係數計算score;
h->mb.cache.non_zero_count[x264_scan8[16*p+idx]] = 1;
}
}
}
}
/* Writing the 16 CBFs in an i16x16 block is quite costly, so decimation can save many bits. */
/* More useful with CAVLC, but still useful with CABAC. */
if( decimate_score < 6 )//如果score<6,則將該16x16塊的殘差係數這是爲全0;
{
CLEAR_16x16_NNZ( p );
block_cbp = 0;
}
else
h->mb.i_cbp_luma |= block_cbp;
h->dctf.dct4x4dc( dct_dc4x4 );//對dc係數做dct變換;
if( h->mb.b_trellis )
nz = x264_quant_luma_dc_trellis( h, dct_dc4x4, i_quant_cat, i_qp, ctx_cat_plane[DCT_LUMA_DC][p], 1, LUMA_DC+p );
else
nz = h->quantf.quant_4x4_dc( dct_dc4x4, h->quant4_mf[i_quant_cat][i_qp][0]>>1, h->quant4_bias[i_quant_cat][i_qp][0]<<1 );//對dc係數做量化;
h->mb.cache.non_zero_count[x264_scan8[LUMA_DC+p]] = nz;
if( nz )//如果dc殘差係數中有非零係數
{
h->zigzagf.scan_4x4( h->dct.luma16x16_dc[p], dct_dc4x4 );//掃描
/* output samples to fdec */
h->dctf.idct4x4dc( dct_dc4x4 );//反變換;
h->quantf.dequant_4x4_dc( dct_dc4x4, h->dequant4_mf[i_quant_cat], i_qp ); /* XXX not inversed *///反量化
if( block_cbp )//存在非零係數,則把dc係數賦值回去,否則dc係數就是0;
for( int i = 0; i < 16; i++ )
dct4x4[i][0] = dct_dc4x4[block_idx_xy_1d[i]];
}
/* put pixels to fdec */
if( block_cbp )//如果存在非零係數,則對殘差係數反變換然後加到預測值上得到重建值;(如果score<6,則永遠不會滿足該條件)
h->dctf.add16x16_idct( p_dst, dct4x4 );
else if( nz ) //如果dc係數中存在非零係數,則將dc係數反變換然後加到預值上得到重建值;
h->dctf.add16x16_idct_dc( p_dst, dct_dc4x4 );
//否則的話重建值直接等於預測值;
}
其他函數應該類似;