H.266/VVC VTM 10-符号化構造CodingStructureを読む
40741 ワード
VTMにおけるcs(CodingStructure)は、領域が属する位置(フレーム、シート、座標、サイズ)、量子化パラメータ、各パラメータセット、符号化端探索のためのモード情報など、1つの領域内の各種符号化情報を含む一般的なクラスであり、またcsは、領域内のCU、PU、TUをそれぞれ格納する3つのリストを確立している.CSは、1つのCUの符号化情報を記述してもよいし、1フレーム内のすべてのCUの符号化情報を、CS記述領域のサイズに応じて記述してもよい.VTMがCUを符号化するとtempCSとbestCSが確立され、最適モードを決定するために使用されます.この場合、cuリストには1つのcuしか含まれていないはずです.またpictureヘッダ情報にはcs変数がフレーム全体の符号化情報を格納し、cuリストからフレーム全体のcuを取得することもできる.符号化cuにおけるいくつかのcsの応用を以下に記録する.
1、分割モードの決定
CUはさらに様々なサイズのCUに分割できるため、EncCuは作成時にいくつかのCSリストを確立して様々なサイズのCUの符号化構造を格納する.m_pTempCS 2とm_pBestCS 2は、色度が区別されない場合に用いられる.
符号化サブCUの前に対応するサブCSを取得し、符号化サブCUでは最適サブCSを得、符号化サブCUの後に最適サブCSのcostを現在のCSに記録し、すべてのサブCSのcostに分割情報を加えたcostを現在モードのcostとする.
また、イントラサーチはCUをさらに分割することができるので、イントラサーチにおけるCSにも同様の用法がある.
2、cs情報に基づいてcuを確立する
3、最適なcsを更新する
関数でuseModeResult関数が呼び出されます.useModeResult関数の注意は2つの部分に分けられ,まず現在のモードに基づいてCUのコンテキストを更新し,次に現在のrd costが最適なrd costより小さいかどうかを判断する.
1、分割モードの決定
CUはさらに様々なサイズのCUに分割できるため、EncCuは作成時にいくつかのCSリストを確立して様々なサイズのCUの符号化構造を格納する.m_pTempCS 2とm_pBestCS 2は、色度が区別されない場合に用いられる.
void EncCu::create( EncCfg* encCfg )
{
...
unsigned numWidths = gp_sizeIdxInfo->numWidths();
unsigned numHeights = gp_sizeIdxInfo->numHeights();
m_pTempCS = new CodingStructure** [numWidths];
m_pBestCS = new CodingStructure** [numWidths];
m_pTempCS2 = new CodingStructure** [numWidths];
m_pBestCS2 = new CodingStructure** [numWidths];
for( unsigned w = 0; w < numWidths; w++ )
{
m_pTempCS[w] = new CodingStructure* [numHeights];
m_pBestCS[w] = new CodingStructure* [numHeights];
m_pTempCS2[w] = new CodingStructure* [numHeights];
m_pBestCS2[w] = new CodingStructure* [numHeights];
for( unsigned h = 0; h < numHeights; h++ )
{
unsigned width = gp_sizeIdxInfo->sizeFrom( w );
unsigned height = gp_sizeIdxInfo->sizeFrom( h );
if( gp_sizeIdxInfo->isCuSize( width ) && gp_sizeIdxInfo->isCuSize( height ) )
{
m_pTempCS[w][h] = new CodingStructure( m_unitCache.cuCache, m_unitCache.puCache, m_unitCache.tuCache );
m_pBestCS[w][h] = new CodingStructure( m_unitCache.cuCache, m_unitCache.puCache, m_unitCache.tuCache );
m_pTempCS[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
m_pBestCS[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
m_pTempCS2[w][h] = new CodingStructure( m_unitCache.cuCache, m_unitCache.puCache, m_unitCache.tuCache );
m_pBestCS2[w][h] = new CodingStructure( m_unitCache.cuCache, m_unitCache.puCache, m_unitCache.tuCache );
m_pTempCS2[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
m_pBestCS2[w][h]->create(chromaFormat, Area(0, 0, width, height), false, (bool)encCfg->getPLTMode());
}
else
{
m_pTempCS[w][h] = nullptr;
m_pBestCS[w][h] = nullptr;
m_pTempCS2[w][h] = nullptr;
m_pBestCS2[w][h] = nullptr;
}
}
}
...
}
符号化サブCUの前に対応するサブCSを取得し、符号化サブCUでは最適サブCSを得、符号化サブCUの後に最適サブCSのcostを現在のCSに記録し、すべてのサブCSのcostに分割情報を加えたcostを現在モードのcostとする.
void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, const ModeType modeTypeParent, bool &skipInterPass )
{
...
do
{
const auto &subCUArea = partitioner.currArea();
if( tempCS->picture->Y().contains( subCUArea.lumaPos() ) )
{
const unsigned wIdx = gp_sizeIdxInfo->idxFrom( subCUArea.lwidth () );
const unsigned hIdx = gp_sizeIdxInfo->idxFrom( subCUArea.lheight() );
CodingStructure *tempSubCS = m_pTempCS[wIdx][hIdx];
CodingStructure *bestSubCS = m_pBestCS[wIdx][hIdx];
tempCS->initSubStructure( *tempSubCS, partitioner.chType, subCUArea, false );
tempCS->initSubStructure( *bestSubCS, partitioner.chType, subCUArea, false );
...
bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;
tempCS->useSubStructure( *bestSubCS, partitioner.chType, CS::getArea( *tempCS, subCUArea, partitioner.chType ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi );
...
}
} while( partitioner.nextPart( *tempCS ) );
partitioner.exitCurrSplit();
...
}
また、イントラサーチはCUをさらに分割することができるので、イントラサーチにおけるCSにも同様の用法がある.
2、cs情報に基づいてcuを確立する
void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
...
for( int bcwLoopIdx = 0; bcwLoopIdx < bcwLoopNum; bcwLoopIdx++ )
{
...
CodingUnit &cu = tempCS->addCU( tempCS->area, partitioner.chType );
partitioner.setCUData( cu );
cu.slice = tempCS->slice;
cu.tileIdx = tempCS->pps->getTileIdx( tempCS->area.lumaPos() );
cu.skip = false;
cu.mmvdSkip = false;
//cu.affine
cu.predMode = MODE_INTER;
cu.chromaQpAdj = m_cuChromaQpOffsetIdxPlus1;
cu.qp = encTestMode.qp;
CU::addPUs( cu );
cu.BcwIdx = g_BcwSearchOrder[bcwLoopIdx];
uint8_t bcwIdx = cu.BcwIdx;
bool testBcw = (bcwIdx != BCW_DEFAULT);
m_pcInterSearch->predInterSearch( cu, partitioner );
...
}
...
}
3、最適なcsを更新する
bool EncCu::xCheckBestMode( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode )
{
bool bestCSUpdated = false;
if( !tempCS->cus.empty() )
{
if( tempCS->cus.size() == 1 )
{
const CodingUnit& cu = *tempCS->cus.front();
CHECK( cu.skip && !cu.firstPU->mergeFlag, "Skip flag without a merge flag is not allowed!" );
}
#if WCG_EXT
DTRACE_BEST_MODE( tempCS, bestCS, m_pcRdCost->getLambda( true ) );
#else
DTRACE_BEST_MODE( tempCS, bestCS, m_pcRdCost->getLambda() );
#endif
if( m_modeCtrl->useModeResult( encTestMode, tempCS, partitioner ) )
{
std::swap( tempCS, bestCS );
// store temp best CI for next CU coding
m_CurrCtx->best = m_CABACEstimator->getCtx();
m_bestModeUpdated = true;
bestCSUpdated = true;
}
}
// reset context states
m_CABACEstimator->getCtx() = m_CurrCtx->start;
return bestCSUpdated;
}
関数でuseModeResult関数が呼び出されます.useModeResult関数の注意は2つの部分に分けられ,まず現在のモードに基づいてCUのコンテキストを更新し,次に現在のrd costが最適なrd costより小さいかどうかを判断する.
bool EncModeCtrlMTnoRQT::useModeResult( const EncTestMode& encTestmode, CodingStructure*& tempCS, Partitioner& partitioner )
{
xExtractFeatures( encTestmode, *tempCS );
ComprCUCtx& cuECtx = m_ComprCUCtxList.back();
if( encTestmode.type == ETM_SPLIT_BT_H )
{
cuECtx.set( BEST_HORZ_SPLIT_COST, tempCS->cost );
}
else if( encTestmode.type == ETM_SPLIT_BT_V )
{
cuECtx.set( BEST_VERT_SPLIT_COST, tempCS->cost );
}
else if( encTestmode.type == ETM_SPLIT_TT_H )
{
...
}
...
// for now just a simple decision based on RD-cost or choose tempCS if bestCS is not yet coded
if( tempCS->features[ENC_FT_RD_COST] != MAX_DOUBLE && ( !cuECtx.bestCS || ( ( tempCS->features[ENC_FT_RD_COST] + ( tempCS->useDbCost ? tempCS->costDbOffset : 0 ) ) < ( cuECtx.bestCS->features[ENC_FT_RD_COST] + ( tempCS->useDbCost ? cuECtx.bestCS->costDbOffset : 0 ) ) ) ) )
{
cuECtx.bestCS = tempCS;
cuECtx.bestCU = tempCS->cus[0];
cuECtx.bestTU = cuECtx.bestCU->firstTU;
if( isModeInter( encTestmode ) )
{
//Here we take the best cost of both inter modes. We are assuming only the inter modes (and all of them) have come before the intra modes!!!
cuECtx.bestInterCost = cuECtx.bestCS->cost;
}
return true;
}
else
{
return false;
}
}