引——色度预测:H.266/VVC代码学习7:VTM4.0帧内色度预测代码(estIntraPredChromaQT)
先放结论,VTM4.0中编码方式如下:
如果需要进一步研究请继续。
一、编码:CABACWriter
关于encodeBinsEP和encodeBin函数,可阅读:H.266/VVC代码学习24:常见的熵编码函数(encodeBin、encodeBinEP、encodeBinsEP)
调用的函数及结构如下
CABACWriter::
intra_chroma_pred_mode
—intra_chroma_lmc_mode
——unary_max_symbol
—getIntraChromaCandModes
1 入口函数
void CABACWriter::intra_chroma_pred_modes( const CodingUnit& cu )
{
if( cu.chromaFormat == CHROMA_400 || ( CS::isDualITree( *cu.cs ) && cu.chType == CHANNEL_TYPE_LUMA ) )
{
return;//如果没有色度或没独立划分,直接返回
}
const PredictionUnit* pu = cu.firstPU;
intra_chroma_pred_mode( *pu );//对单个pu进行色度预测
}
2 处理方法
判断顺序:先看是否为DM,如是,编0返回。再看是否为CCLM模式,如是,编0返回。再看是否为MDLM模式,如是,编1。最后判断传统模式。请看代码:
void CABACWriter::intra_chroma_pred_mode( const PredictionUnit& pu )
{
const unsigned intraDir = pu.intraDir[1];//获取色度预测模式
/************ DM的处理 ***********/
const bool isDerivedMode = intraDir == DM_CHROMA_IDX;//是否为DM模式
m_BinEncoder.encodeBin(isDerivedMode ? 0 : 1, Ctx::IntraChromaPredMode(0));//用常规编码器,是DM编0,不是编1
if (isDerivedMode)//是DM就直接返回,即只编了一个0返回,否则编1继续进行下面。
{
return;
}
/************ LM的处理 ***********/
if( pu.cs->sps->getUseLMChroma() )//如果使用了LM模式
{
intra_chroma_lmc_mode( pu );//是LM,进行LM的处理(内部函数会看到用常规编码器的两个码字来编这三种LM模式)
if ( PU::isLMCMode( intraDir ) )//是LM的情况(模式67,68,69),直接返回,否则继续进行下面
{
return;
}
}
/******** 其他模式的处理 *******/
unsigned chromaCandModes[ NUM_CHROMA_MODE ];
PU::getIntraChromaCandModes( pu, chromaCandModes );//创建并获取色度模式列表
int candId = 0;//列表的序号
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 );//用旁路编码器,两个码字编列表中的序号。注意:编的是序号candId!
}
}
3 LM模式的额外操作
LM模式中有很多预留,列表中有10个,而VTM4.0中用了三个。最后编码的是LM的序号symbol。
void CABACWriter::intra_chroma_lmc_mode( const PredictionUnit& pu )
{
const unsigned intraDir = pu.intraDir[1];//看是67,68还是69
int lmModeList[10];//设LM有10种(很多是预留的,并没有用上)
int maxSymbol = PU::getLMSymbolList( pu, lmModeList );
int symbol = -1;
for ( int k = 0; k < LM_SYMBOL_NUM; k++ )//找到symbol的序号
{
if ( lmModeList[k] == intraDir || ( lmModeList[k] == -1 && intraDir < LM_CHROMA_IDX ) )
{
symbol = k;
break;
}
}
CHECK( symbol < 0, "invalid symbol found" );
unary_max_symbol(symbol, Ctx::IntraChromaPredMode(1), Ctx::IntraChromaPredMode(2), maxSymbol - 1);//编这个序号
}
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 );
for( unsigned binsWritten = 0; binsWritten < totalBinsToWrite; ++binsWritten )
{
const unsigned nextBin = symbol > binsWritten;
m_BinEncoder.encodeBin( nextBin, binsWritten == 0 ? ctxId0 : ctxIdN );
}
}
二、解码:CABACReader
调用的函数及结构如下
CABACReader::
intra_chroma_pred_mode
—intra_chroma_lmc_mode
——unary_max_symbol
—getIntraChromaCandModes
整体来看就是编码的逆过程:
1 入口函数
void CABACReader::intra_chroma_pred_modes( CodingUnit& cu )
{
if( cu.chromaFormat == CHROMA_400 || ( CS::isDualITree( *cu.cs ) && cu.chType == CHANNEL_TYPE_LUMA ) )
{
return;
}
PredictionUnit *pu = cu.firstPU;
{
CHECK( pu->cu != &cu, "Inkonsistent PU-CU mapping" );
intra_chroma_pred_mode( *pu );
}
}
2 处理方法
void CABACReader::intra_chroma_pred_mode( PredictionUnit& pu )
{
RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__INTRA_DIR_ANG, pu.cu->blocks[pu.chType].lumaSize(), CHANNEL_TYPE_CHROMA );
if (m_BinDecoder.decodeBin(Ctx::IntraChromaPredMode(0)) == 0)//第一位为0,直接解码为66
{
pu.intraDir[1] = DM_CHROMA_IDX;
return;
}
// LM chroma mode
if( pu.cs->sps->getUseLMChroma() )//第一位为1,则拿出第二三位,判断是哪种LM模式,解码出来
{
if( intra_chroma_lmc_mode( pu ) )
{
return;
}
}
unsigned candId = m_BinDecoder.decodeBinsEP( 2 );//前面都不是,则看第四五位,得到一个序号
unsigned chromaCandModes[ NUM_CHROMA_MODE ];
PU::getIntraChromaCandModes( pu, chromaCandModes );
CHECK( candId >= NUM_CHROMA_MODE, "Chroma prediction mode index out of bounds" );
CHECK( PU::isLMCMode( chromaCandModes[ candId ] ), "The intra dir cannot be LM_CHROMA for this path" );
CHECK( chromaCandModes[ candId ] == DM_CHROMA_IDX, "The intra dir cannot be DM_CHROMA for this path" );
pu.intraDir[1] = chromaCandModes[ candId ];//获取列表,解码得出列表中对应的序号的模式
}
3 LM模式的额外操作
这里我还没仔细阅读,先放代码,以后填坑:
bool CABACReader::intra_chroma_lmc_mode( PredictionUnit& pu )
{
int lmModeList[10];
int maxSymbol = PU::getLMSymbolList(pu, lmModeList);
int symbol = unary_max_symbol(Ctx::IntraChromaPredMode(1), Ctx::IntraChromaPredMode(2), maxSymbol - 1);
if (lmModeList[symbol] != -1)
{
pu.intraDir[1] = lmModeList[symbol];
return true;
}
return false;
}
unsigned CABACReader::unary_max_symbol( unsigned ctxId0, unsigned ctxIdN, unsigned maxSymbol )
{
unsigned onesRead = 0;
while( onesRead < maxSymbol && m_BinDecoder.decodeBin( onesRead == 0 ? ctxId0 : ctxIdN ) == 1 )
{
++onesRead;
}
return onesRead;
}