之前的博客中我详细的讲解了关于视频编码中熵编码部分的具体的技术细节
H.266/VVC代码学习笔记5:VTM4.0中的熵编码CABAC部分的详细解析
今天就来具体的讲一下在帧内色度预测模式中的熵编码具体是怎么编码的。
1.编码端的Rdcost:
按照下表的顺序对这6种模式进行Rdcost检测;
modeList[ 0 ] = PLANAR_IDX;
modeList[ 1 ] = VER_IDX;
modeList[ 2 ] = HOR_IDX;
modeList[ 3 ] = DC_IDX;
modeList[4] = LM_CHROMA_IDX;
modeList[5] = DM_CHROMA_IDX;
DM模式检测的是对应亮度块的帧内模式;
当DM模式对应的帧内模式与前4种模式相同时,将前面的该模式置为角度模式66去进行Rdcost检测
编码的第一位是判断是否用DM模式(0有效)
编码的第二位是判断是否用LM模式(0有效)
第一位用上下文模型1,第二位用上下文模型2,其余位用旁路编码器去编码。
1.编码端的Rdcost:
(1)在Rdcost之前增加提前的SATD检测:
对VER,HOR,LM_L,LM_T,DM(角度模式和MDLM)这5种模式提前进行残差的(预测值与原始值之间)SATD检测,根据SATD的结果从小到大排序,将SATD最大的两种模式去掉(即这两种模式不进行Rdcost检测);
对PLANAR,DC,LM(非角度模式和LM)不进行提前的SATD检测,直接进行Rdcost检测;
(2)将剩余的6种模式按照右表的顺序进行Rdcost检测,选择最优模式;
(3)DM模式检测的是对应亮度块的帧内模式;当DM模式对应的帧内模式与前4种模式相同时,将前面的该模式置为角度模式66去进行Rdcost检测
modeList[ 0 ] = PLANAR_IDX;
modeList[ 1 ] = VER_IDX;
modeList[ 2 ] = HOR_IDX;
modeList[ 3 ] = DC_IDX;
modeList[4] = LM_CHROMA_IDX;
modeList[5] = MDLM_L_IDX;
modeList[6] = MDLM_T_IDX;
modeList[7] = DM_CHROMA_IDX;
注:VTM3.0中为MDLM设计了一个初始值固定的ctx3
第一位是否用DM(0有效)
第二位是否用LM(67)(0有效)
第三位是否用LM_L或者LM_T (1有效)
若第三位为1,则第四位标志MDLM的方向,是L还是T
若第三位为0,则第四位和第五位按顺序标志四种传统角度模式
具体到代码中如下:
这是帧内色度的8种预测模式的熵编码的流程具体细节
void CABACWriter::intra_chroma_pred_mode( const PredictionUnit& pu )
{
const unsigned intraDir = pu.intraDir[1];
const bool isDerivedMode = intraDir == DM_CHROMA_IDX;//首先判断是否为DM模式
m_BinEncoder.encodeBin(isDerivedMode ? 0 : 1, Ctx::IntraChromaPredMode(0));//如果是DM模式,只将第一位编为0,用ctx1.
if (isDerivedMode)
{
return;
}
// LM chroma mode
if( pu.cs->sps->getUseLMChroma() )//如果是lm模式,将第一位编为1,用ctx1.
{
intra_chroma_lmc_mode( pu );//LM模式的三种编码方式
if ( PU::isLMCMode( intraDir ) )
{
return;
}
}
// chroma candidate index
unsigned chromaCandModes[ NUM_CHROMA_MODE ];
PU::getIntraChromaCandModes( pu, chromaCandModes );
int candId = 0;
//如果经过前面的判断都不是DM或者LM模式,则进入下面的四种常规模式的编码
for ( ; candId < NUM_CHROMA_MODE; candId++ )
{
if( intraDir == chromaCandModes[ candId ] )
{
break;
}
}
CHECK( candId >= NUM_CHROMA_MODE, "Chroma prediction mode index out of bounds" );
CHECK( chromaCandModes[ candId ] == DM_CHROMA_IDX, "The intra dir cannot be DM_CHROMA for this path" );
{
m_BinEncoder.encodeBinsEP( candId, 2 );//这里选用常规编码器,编码最后的两位
}
}
这是为三种LM模式单独准备的熵编码的函数
void CABACWriter::intra_chroma_lmc_mode( const PredictionUnit& pu )
{
const unsigned intraDir = pu.intraDir[1];
int lmModeList[10];
int maxSymbol = PU::getLMSymbolList( pu, lmModeList );//maxSymbol=3.
int symbol = -1;
for ( int k = 0; k < LM_SYMBOL_NUM; k++ )
{
if ( lmModeList[k] == intraDir || ( lmModeList[k] == -1 && intraDir < LM_CHROMA_IDX ) )
{
symbol = k;
break;
}
//如果选中LM模式,symbol=0
//如果选中其他模式,symbol=1
//如果选中L模式,symbol=2
//如果选中T模式,symbol=3
}
CHECK( symbol < 0, "invalid symbol found" );
//这里对LM选用合适的上下文模型进行具体的编码
unary_max_symbol(symbol, Ctx::IntraChromaPredMode(1), Ctx::IntraChromaPredMode(2), maxSymbol - 1);
}
这里是三种LM模式具体的编码方式
void CABACWriter::unary_max_symbol( unsigned symbol, unsigned ctxId0, unsigned ctxIdN, unsigned maxSymbol )
{
CHECK( symbol > maxSymbol, "symbol > maxSymbol" );
const unsigned totalBinsToWrite = std::min( symbol + 1, maxSymbol );
//如果是LM模式,totalBinsToWrite=1,symbol=0
//如果是其他模式,totalBinsToWrite=2,symbol=1
//如果是L模式,totalBinsToWrite=3,symbol=2
//如果是T模式,totalBinsToWrite=3,symbol=3
for( unsigned binsWritten = 0; binsWritten < totalBinsToWrite; ++binsWritten )
{
const unsigned nextBin = symbol > binsWritten;
//如果是LM模式,nextBin=1;第二位编0
//如果是其他模式,nextBin=1;第二位编1,nextBin++,第三位编0
//如果是L模式,nextBin=1;第二位编1,nextBin++,第三位编1,nextBin++,第四位编0
//如果是T模式,nextBin=1;第二位编1,nextBin++,第三位编1,nextBin++,第四位编1
m_BinEncoder.encodeBin( nextBin, binsWritten == 0 ? ctxId0 : ctxIdN );
}
}