资讯详情

VVC代码阅读 xCheckModeSplit()函数 (中间CABAC还没看)最后代码还没看完

  ///当前测试qp   const int qp                = encTestMode.qp;   //当前slice   const Slice &slice          = *tempCS->slice;   // 之前的QP   const int oldPrevQp         = tempCS->prevQP[partitioner.chType];   // 之前的LUT   const auto oldMotionLut     = tempCS->motionLut; #if ENABLE_QPA_SUB_CTU   const PPS &pps              = *tempCS->pps;   const uint32_t currDepth    = partitioner.currDepth; #endif   // 之前的PLT   const auto oldPLT           = tempCS->prevPLT;   // 获取当前测试的划分模式   const PartSplit split = getPartSplit( encTestMode );   // 子结点的模式类型   const ModeType modeTypeChild = partitioner.modeType;    CHECK( split == CU_DONT_SPLIT, "No proper split provided!" );    tempCS->initStructData( qp );   // 获取上下文   m_CABACEstimator->getCtx() = m_CurrCtx->start;   // 定位上下文各部分的起始位置    const TempCtx ctxStartSP( m_CtxCache, SubCtx( Ctx::SplitFlag,   m_CABACEstimator->getCtx() ) );   const TempCtx ctxStartQt( m_CtxCache, SubCtx( Ctx::SplitQtFlag, m_CABACEstimator->getCtx() ) );   const TempCtx ctxStartHv( m_CtxCache, SubCtx( Ctx::SplitHvFlag, m_CABACEstimator->getCtx() ) );   const TempCtx ctxStart12( m_CtxCache, SubCtx( Ctx::Split12Flag, m_CABACEstimator->getCtx() ) );   const TempCtx ctxStartMC( m_CtxCache, SubCtx( Ctx::ModeConsFlag, m_CABACEstimator->getCtx() ) );   // 重置上下文比特   m_CABACEstimator->resetBits();   // 设置划分 CU 的模式   m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );   // 设置模式限制   m_CABACEstimator->mode_constraint( split, *tempCS, partitioner, modeTypeChild );

initStructData():初始化 CS(dist清零、cost取正无限等)

 // 计算预测 cost 当前的因素 QP > 30 时为 1.1,否则为 1.075  const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );   tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();   if (!tempCS->useDbCost)     CHECK(bestCS->costDbOffset != 0, "error"); // 根据上下文预测的计算 cost   const double cost   = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits()   ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) )   bestCS->costDbOffset / factor; // 定位上下文各部分   m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitFlag,   ctxStartSP );   m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitQtFlag, ctxStartQt );   m_CABACEstimator->getCtx() = SubCtx( Ctx::SplitHvFlag, ctxStartHv );   m_CABACEstimator->getCtx() = SubCtx( Ctx::Split12Flag, ctxStart12 );   m_CABACEstimator->getCtx() = SubCtx( Ctx::ModeConsFlag, ctxStartMC ); // 若上下文预测 cost > bestCS cost,重置上下文状态并确认 bestCS   if (cost > bestCS->cost   bestCS->costDbOffset #if ENABLE_QPA_SUB_CTU     || (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP() && (slice.getCuQpDeltaSubdiv() > 0) && (split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT) &&         (currDepth == 0)) // force quad-split or no split at CTU level #endif     )   {     xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );     return;   }

getUseEncDbOpt():是否启用db cost. 默认为false

encoder-side deblocking optimization. When it is enabled, de-blocking fifilter is applied during mode decision.
const bool chromaNotSplit = modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTRA ? true : false;   if( partitioner.treeType != TREE_D )   {     tempCS->treeType = TREE_L;   }   else   {     if( chromaNotSplit )     {       CHECK( partitioner.chType != CHANNEL_TYPE_LUMA, "chType must be luma" );       tempCS->treeType = partitioner.treeType = TREE_L;     }     else     {       tempCS->treeType = partitioner.treeType = TREE_D;     }   }

chromaNotSplit:如果所有类型的父节点都可以,子节点是MODE_TYPE_INTRA,则chromaNotSplit为true

if语句:如果目前的树类不是TREE_D,将数类型设置为TREE_L,否则若chromaNotSplit为true,则当前 tempCS 分树类型设置为 TREE_L,如果要划分色度,则设置为TREE_D

  partitioner.splitCurrArea( split, *tempCS );   bool qgEnableChildren = partitioner.currQgEnable(); // QG possible at children level   bool qgChromaEnableChildren = partitioner.currQgChromaEnable(); // Chroma QG possible at children level    m_CurrCtx  ;   ///重建和预测像素值   tempCS->getRecoBuf().fill( 0 );    tempCS->getPredBuf().fill(0);   AffineMVInfo tmpMVInfo;   bool isAffMVInfoSaved; #if GDR_ENABLED   AffineMVInfoSolid tmpMVInfoSolid;   m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, tmpMVInfoSolid, isAffMVInfoSaved); #else   m_pcInterSearch->savePrevAffMVInfo(0, tmpMVInfo, isAffMVInfoSaved); #endif   BlkUniMvInfo tmpUniMvInfo;   bool         isUniMvInfoSaved = false;

根据划分模式确划分当前区域:这里currDepth++;currSubdiv++

图像划分过程中的splitCurrArea()函数以及其中的getCUSubPartitions()函数_青椒鸡汤的博客-CSDN博客

qgEnableChildren:确认子level的QG是否开启

  do
  {
    const auto &subCUArea  = partitioner.currArea();//等于for(const auto&x:range),读取当前区域数据

    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 );
      // 将子 CU 的 tempCS、bestCS 的 best Parent 均设置为 bestCS
      tempSubCS->bestParent = bestSubCS->bestParent = bestCS;
      double newMaxCostAllowed = isLuma(partitioner.chType) ? std::min(encTestMode.maxCostAllowed, bestCS->cost - m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist)) : MAX_DOUBLE;
      newMaxCostAllowed = std::max(0.0, newMaxCostAllowed);
      xCompressCU(tempSubCS, bestSubCS, partitioner, newMaxCostAllowed);
      

do while结构:

c++中的auto、const auto&_群山QunShine的博客-CSDN博客_auto const

subCUArea :获取划分后的区域。比如一个128 X 128的CTU,在四叉树划分后,变成了64X64

lwidth():获得当前已划分区域的宽

gp_sizeIdxInfo->idxFrom():从当前已划分区域的宽得到这个宽度的索引号,这个索引号好像是2的n次方的n。如宽64,就为6

tempSubCS:从当前已划分CU的wIdx和hIdx推出新的tempCs

initSubStructure():将父CU的CS的信息传入子CU的CS中。这里一开始是将CTU级别的信息传输到子CU节点里。调用了两次,分别传输给了

tempCS->initSubStructure:为子CU的CS结构

https://blog.csdn.net/dfhg54/article/details/124304502

newMaxCostAllowed :设置子CU允许的最大COST

然后再调用xcompressCU()函数,此时再将tempSubCS和bestSubCS传输进去,在递归调用的xcompressCU()中,这两个即为

// 子 CU 的 bestParent 设置为空
tempSubCS->bestParent = bestSubCS->bestParent = nullptr;
      // 若子 CU 的最优 cost 为正无穷
      if( bestSubCS->cost == MAX_DOUBLE )
      {
        CHECK( split == CU_QUAD_SPLIT, "Split decision reusing cannot skip quad split" );
        // 将当前 cost 设置为正无穷
        tempCS->cost = MAX_DOUBLE;
        tempCS->costDbOffset = 0;
        tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
        // 退回上下文
        m_CurrCtx--;
        // 退出当前划分
        partitioner.exitCurrSplit();
        // 检查当前模式是否为最佳模式,若为最佳模式则与 bestCS 交换
        xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
        // 若当前通道为 LUMA
        // 则还原 LUT
        if( partitioner.chType == CHANNEL_TYPE_LUMA )
        {
          tempCS->motionLut = oldMotionLut;
        }
        // 结束划分尝试
        return;
      }
      // 是否需要保存残差信号
      bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;
      // 将子cu的最优模式信息copy到tempCS中
      tempCS->useSubStructure( *bestSubCS, partitioner.chType, CS::getArea( *tempCS, subCUArea, partitioner.chType ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, keepResi, true );

      // 若当前 QG 可用
      // 则用子 CU prevQP 更新当前 tempCS 的 prevQP
      if( partitioner.currQgEnable() )
      {
        tempCS->prevQP[partitioner.chType] = bestSubCS->prevQP[partitioner.chType];
      }
      // 若当前限制尝试 Inter
      if( partitioner.isConsInter() )
      {
        // 循环遍历所有子 CU,检查预测模式是否均为 INTER
        for( int i = 0; i < bestSubCS->cus.size(); i++ )
        {
          CHECK( bestSubCS->cus[i]->predMode != MODE_INTER, "all CUs must be inter mode in an Inter coding region (SCIPU)" );
        }
      }
      // 若当前限制尝试 INTRA
      else if( partitioner.isConsIntra() )
      {
        // 循环遍历所有子 CU,检查预测模式是否均为 INTRA
        for( int i = 0; i < bestSubCS->cus.size(); i++ )
        {
          CHECK( bestSubCS->cus[i]->predMode == MODE_INTER, "all CUs must not be inter mode in an Intra coding region (SCIPU)" );
        }
      }

      // tempSubCS和bestSubCS的清空初始化
      tempSubCS->releaseIntermediateData();
      bestSubCS->releaseIntermediateData();
      // 若当前帧不是 I 帧,且当前限制只尝试 INTRA
      if( !tempCS->slice->isIntra() && partitioner.isConsIntra() )
      {
        // 计算 tempCS cost
        tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );
        // 若当前 cost > 最佳 cost
        if( tempCS->cost > bestCS->cost )
        {
          tempCS->cost = MAX_DOUBLE;
          tempCS->costDbOffset = 0;
          tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
          m_CurrCtx--;
          // 划分的子cu区域,在partitioner中出栈
          partitioner.exitCurrSplit();
          // 若当前通道为 LUMA
          // 则还原 LUT
          if( partitioner.chType == CHANNEL_TYPE_LUMA )
          {
            tempCS->motionLut = oldMotionLut;
          }
          return;
        }
      }
    }
  } while( partitioner.nextPart( *tempCS ) );  // 所有划分子 CU 处理完成

  // 划分的子cu区域,在partitioner中出栈
  partitioner.exitCurrSplit();


  m_CurrCtx--;
  // 若色度不划分
  if( chromaNotSplit )
  {
    //Note: In local dual tree region, the chroma CU refers to the central luma CU's QP.
    //If the luma CU QP shall be predQP (no residual in it and before it in the QG), it must be revised to predQP before encoding the chroma CU
    //Otherwise, the chroma CU uses predQP+deltaQP in encoding but is decoded as using predQP, thus causing encoder-decoded mismatch on chroma qp.
    // 若使用 DQP
    if( tempCS->pps->getUseDQP() )
    {
      //find parent CS that including all coded CUs in the QG before this node
      CodingStructure* qgCS = tempCS;
      bool deltaQpCodedBeforeThisNode = false;
      // 若当前区域不是 QG 最上层区域(即有父结点)
      if( partitioner.currArea().lumaPos() != partitioner.currQgPos )
      {
        // 当前结点到 QGCS 经过的父结点数量
        int numParentNodeToQgCS = 0;
        // 将 qgCS 定位到 parent CS that including all coded CUs in the QG before this node
        while( qgCS->area.lumaPos() != partitioner.currQgPos )
        {
          CHECK( qgCS->parent == nullptr, "parent of qgCS shall exsit" );
          qgCS = qgCS->parent;
          numParentNodeToQgCS++;
        }

        //check whether deltaQP has been coded (in luma CU or luma&chroma CU) before this node
        CodingStructure* parentCS = tempCS->parent;
        // 循环逐层遍历当前结点所有父结点
        for( int i = 0; i < numParentNodeToQgCS; i++ )
        {
          //checking each parent
          // 循环遍历当前父结点的所有 CU,检查 deltaQP 在当前结点前是否已经编码
          CHECK( parentCS == nullptr, "parentCS shall exsit" );
          for( const auto &cu : parentCS->cus )
          {
            if( cu->rootCbf && !isChroma( cu->chType ) )
            {
              deltaQpCodedBeforeThisNode = true;
              break;
            }
          }
          parentCS = parentCS->parent;
        }
      }

      //revise luma CU qp before the first luma CU with residual in the SCIPU to predQP
      // 若 deltaQP 在当前结点前未编码
      if( !deltaQpCodedBeforeThisNode )
      {
        //get pred QP of the QG
        // 定位当前 QG 的第一个 CU
        const CodingUnit* cuFirst = qgCS->getCU( CHANNEL_TYPE_LUMA );
        CHECK( cuFirst->lumaPos() != partitioner.currQgPos, "First cu of the Qg is wrong" );
        // 获取当前 QG 的第一个 CU 的预测 QP
        int predQp = CU::predictQP( *cuFirst, qgCS->prevQP[CHANNEL_TYPE_LUMA] );

        //revise to predQP
        int firstCuHasResidual = (int)tempCS->cus.size();
        // 循环定位当前块下的 firstCuHasResidual
        for( int i = 0; i < tempCS->cus.size(); i++ )
        {
          if( tempCS->cus[i]->rootCbf )
          {
            firstCuHasResidual = i;
            break;
          }
        }
        // 将当前块 firstCuHasResidual 前的所有 CU QP 设为预测 QP
        for( int i = 0; i < firstCuHasResidual; i++ )
        {
          tempCS->cus[i]->qp = predQp;
        }
      }
    }  // use DQP
    assert( tempCS->treeType == TREE_L );
    uint32_t numCuPuTu[6];
    tempCS->picture->cs->getNumCuPuTuOffset( numCuPuTu );
    tempCS->picture->cs->useSubStructure( *tempCS, partitioner.chType, CS::getArea( *tempCS, partitioner.currArea(), partitioner.chType ), false, true, false, false, false );

    // 若 chroma 可用,对 chroma 进行处理
    if (isChromaEnabled(tempCS->pcv->chrFormat))
    {
    partitioner.chType = CHANNEL_TYPE_CHROMA;
    tempCS->treeType = partitioner.treeType = TREE_C;

    m_CurrCtx++;

    const unsigned wIdx = gp_sizeIdxInfo->idxFrom( partitioner.currArea().lwidth() );
    const unsigned hIdx = gp_sizeIdxInfo->idxFrom( partitioner.currArea().lheight() );
    CodingStructure *tempCSChroma = m_pTempCS2[wIdx][hIdx];
    CodingStructure *bestCSChroma = m_pBestCS2[wIdx][hIdx];
    tempCS->initSubStructure( *tempCSChroma, partitioner.chType, partitioner.currArea(), false );
    tempCS->initSubStructure( *bestCSChroma, partitioner.chType, partitioner.currArea(), false );
    tempCS->treeType = TREE_D;
    xCompressCU( tempCSChroma, bestCSChroma, partitioner );

    //attach chromaCS to luma CS and update cost
    bool keepResi = KEEP_PRED_AND_RESI_SIGNALS;
    //bestCSChroma->treeType = tempCSChroma->treeType = TREE_C;
    CHECK( bestCSChroma->treeType != TREE_C || tempCSChroma->treeType != TREE_C, "wrong treeType for chroma CS" );
    tempCS->useSubStructure( *bestCSChroma, partitioner.chType, CS::getArea( *bestCSChroma, partitioner.currArea(), partitioner.chType ), KEEP_PRED_AND_RESI_SIGNALS, true, keepResi, true, true );

    //release tmp resource
    tempCSChroma->releaseIntermediateData();
    bestCSChroma->releaseIntermediateData();
    //tempCS->picture->cs->releaseIntermediateData();
      m_CurrCtx--;
    }
    tempCS->picture->cs->clearCuPuTuIdxMap( partitioner.currArea(), numCuPuTu[0], numCuPuTu[1], numCuPuTu[2], numCuPuTu + 3 );


    //recover luma tree status
    partitioner.chType = CHANNEL_TYPE_LUMA;
    partitioner.treeType = TREE_D;
    partitioner.modeType = MODE_TYPE_ALL;
  }

  // Finally, generate split-signaling bits for RD-cost check
  // 最后,生成用于 RD cost check 的划分信号比特
  const PartSplit implicitSplit = partitioner.getImplicitSplit( *tempCS );

  {
    bool enforceQT = implicitSplit == CU_QUAD_SPLIT;

    // LARGE CTU bug
    if( m_pcEncCfg->getUseFastLCTU() )
    {
      unsigned minDepth = 0;
      unsigned maxDepth = floorLog2(tempCS->sps->getCTUSize()) - floorLog2(tempCS->sps->getMinQTSize(slice.getSliceType(), partitioner.chType));

      if( auto ad = dynamic_cast<AdaptiveDepthPartitioner*>( &partitioner ) )
      {
        ad->setMaxMinDepth( minDepth, maxDepth, *tempCS );
      }

      if( minDepth > partitioner.currQtDepth )
      {
        // enforce QT
        enforceQT = true;
      }
    }

    if( !enforceQT )
    {
      m_CABACEstimator->resetBits();

      m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );
      partitioner.modeType = modeTypeParent;
      m_CABACEstimator->mode_constraint( split, *tempCS, partitioner, modeTypeChild );
      tempCS->fracBits += m_CABACEstimator->getEstFracBits(); // split bits
    }
  }

  tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );

  // Check Delta QP bits for splitted structure
  if( !qgEnableChildren ) // check at deepest QG level only
  xCheckDQP( *tempCS, partitioner, true );

  // If the configuration being tested exceeds the maximum number of bytes for a slice / slice-segment, then
  // a proper RD evaluation cannot be performed. Therefore, termination of the
  // slice/slice-segment must be made prior to this CTU.
  // This can be achieved by forcing the decision to be that of the rpcTempCU.
  // The exception is each slice / slice-segment must have at least one CTU.
  if (bestCS->cost != MAX_DOUBLE)
  {
  }
  else
  {
    bestCS->costDbOffset = 0;
  }
  tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
  if( tempCS->cus.size() > 0 && modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTER )
  {
    int areaSizeNoResiCu = 0;
    for( int k = 0; k < tempCS->cus.size(); k++ )
    {
      areaSizeNoResiCu += (tempCS->cus[k]->rootCbf == false) ? tempCS->cus[k]->lumaSize().area() : 0;
    }
    if( areaSizeNoResiCu >= (tempCS->area.lumaSize().area() >> 1) )
    {
      skipInterPass = true;
    }
  }

  // RD check for sub partitioned coding structure.
  xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );

  if (isAffMVInfoSaved)
    m_pcInterSearch->addAffMVInfo(tmpMVInfo);
  if (!tempCS->slice->isIntra() && isUniMvInfoSaved)
  {
    m_pcInterSearch->addUniMvInfo(tmpUniMvInfo);
  }

  tempCS->motionLut = oldMotionLut;

  tempCS->prevPLT   = oldPLT;

  tempCS->releaseIntermediateData();

  tempCS->prevQP[partitioner.chType] = oldPrevQp;
}

标签: dqp9051多参变送器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台