H.266/VVC VTM 10-符号化構造CodingStructureを読む


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は、色度が区別されない場合に用いられる.
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;
  }
}