• VVC系列(三)xCompressCTU、xCompressCU和xCheckModeSplit解析



    主要参考自 VTM3.0代码阅读:xCompressCU函数,针对新版本VTM做了部分补充。

    EncCu::compressCtu

    初始化partitioning manager QTBTPartitioner partitioner,这是VVC相比于HEVC增加的数据类型。

    判断是否需要执行IBC模式(帧内块复制技术,屏幕内容编码的一种技术,参考区域不局限在相邻像素行,而是可以利用当前帧所有已编码区域,预测待编码CU)

    初始化上下文指针,初始化乒乓buffertempCSbestCS,递归执行xCompressCU函数,对亮度分量进行预测。

    判断是否开启了 CTU Dual Tree技术,如果开启,需要对色度分量进行一次预测。

    注释后的代码如下

    void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsigned ctuRsAddr, const int prevQP[], const int currQP[] )
    {
      /*
      input params:
      cs: picture 级别的coding structure
      area: 当前正编码CTU的区域
      ctuRsAddr: 当前正编码CTU的raster-scan顺序
      递归调用xCompressCU
      */
      m_modeCtrl->initCTUEncoding( *cs.slice );
      cs.treeType = TREE_D;
    
      cs.slice->m_mapPltCost[0].clear();
      cs.slice->m_mapPltCost[1].clear();
      // init the partitioning manager
      QTBTPartitioner partitioner;
      partitioner.initCtu(area, CH_L, *cs.slice);
      // IBC:屏幕内容编码,refer to https://blog.csdn.net/BigDream123/article/details/125185687
      if (m_pcEncCfg->getIBCMode())
      {
        if (area.lx() == 0 && area.ly() == 0)
        {
          m_pcInterSearch->resetIbcSearch();
        }
        m_pcInterSearch->resetCtuRecord();
        m_ctuIbcSearchRangeX = m_pcEncCfg->getIBCLocalSearchRangeX();
        m_ctuIbcSearchRangeY = m_pcEncCfg->getIBCLocalSearchRangeY();
      }
      // IBC优先通过哈希搜索的方式进行块匹配
      if (m_pcEncCfg->getIBCMode() && m_pcEncCfg->getIBCHashSearch() && (m_pcEncCfg->getIBCFastMethod() & IBC_FAST_METHOD_ADAPTIVE_SEARCHRANGE))
      {
        const int hashHitRatio = m_ibcHashMap.getHashHitRatio(area.Y()); // in percent
        if (hashHitRatio < 5) // 5%
        {
          m_ctuIbcSearchRangeX >>= 1;
          m_ctuIbcSearchRangeY >>= 1;
        }
        if (cs.slice->getNumRefIdx(REF_PIC_LIST_0) > 0)
        {
          m_ctuIbcSearchRangeX >>= 1;
          m_ctuIbcSearchRangeY >>= 1;
        }
      }
      // init current context pointer
      m_CurrCtx = m_CtxBuffer.data();
      // tempCS和bestCS作为乒乓buffer(ping-pong buffer),即使用多个低速的数据预处理模块处理高速的数据输入流, e.g. 一个buffer写入数据的时候,另外一个buffer进行数据处理,
      // tempCS存储test模式的数据,bestCS存储当前最优模式
      CodingStructure *tempCS = m_pTempCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
      CodingStructure *bestCS = m_pBestCS[gp_sizeIdxInfo->idxFrom( area.lumaSize().width )][gp_sizeIdxInfo->idxFrom( area.lumaSize().height )];
    
      cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);
      cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);
      tempCS->currQP[CH_L] = bestCS->currQP[CH_L] =
      tempCS->baseQP       = bestCS->baseQP       = currQP[CH_L];
      tempCS->prevQP[CH_L] = bestCS->prevQP[CH_L] = prevQP[CH_L];
    
      xCompressCU(tempCS, bestCS, partitioner); // 第一次调用xCompressCU对亮度分量进行预测
      cs.slice->m_mapPltCost[0].clear();
      cs.slice->m_mapPltCost[1].clear();
      // all signals were already copied during compression if the CTU was split - at this point only the structures are copied to the top level CS
      const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;
      cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType), copyUnsplitCTUSignals,
                         false, false, copyUnsplitCTUSignals, true);
    
      // CTU Dual-Tree: for HEVC, 亮度和色度的划分方式相同;for VVC, P&B Slice的亮度和色度划分方式相同, I Slice的亮度和色度划分方式不同, 即dual-tree
      // 大多数时候,亮度的纹理比色度更加细节, refer to https://blog.csdn.net/fangz142/article/details/123080611
      if (CS::isDualITree (cs) && isChromaEnabled (cs.pcv->chrFormat))
      {
        m_CABACEstimator->getCtx() = m_CurrCtx->start;
    
        partitioner.initCtu(area, CH_C, *cs.slice);
    
        cs.initSubStructure(*tempCS, partitioner.chType, partitioner.currArea(), false);
        cs.initSubStructure(*bestCS, partitioner.chType, partitioner.currArea(), false);
        tempCS->currQP[CH_C] = bestCS->currQP[CH_C] =
        tempCS->baseQP       = bestCS->baseQP       = currQP[CH_C];
        tempCS->prevQP[CH_C] = bestCS->prevQP[CH_C] = prevQP[CH_C];
    
        xCompressCU(tempCS, bestCS, partitioner);  // 第二次调用xCompressCU对色度分量进行预测
    
        const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;
        cs.useSubStructure(*bestCS, partitioner.chType, CS::getArea(*bestCS, area, partitioner.chType),
                           copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals, true);
      }
    
      if (m_pcEncCfg->getUseRateCtrl())
      {
        (m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr)).m_actualMSE = (double)bestCS->dist / (double)m_pcRateCtrl->getRCPic()->getLCU(ctuRsAddr).m_numberOfPixel;
      }
      // reset context states and uninit context pointer
      m_CABACEstimator->getCtx() = m_CurrCtx->start;
      m_CurrCtx                  = 0;
    
      // Ensure that a coding was found
      // Selected mode's RD-cost must be not MAX_DOUBLE.
      CHECK( bestCS->cus.empty()                                   , "No possible encoding found" );
      CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );
      CHECK( bestCS->cost             == MAX_DOUBLE                , "No possible encoding found" );
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    EncCu::xCompressCU

    xCheckModeSplit函数会调用xCompressCU尝试QT、BT和TT划分,tempCS用来存储当前compress区域各种模式下的数据,m_modeCtrl维护需要测试的mode,通过一个do-while循环,尝试各种划分模式。

    void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Partitioner& partitioner, double maxCostAllowed )
    {
      /*
      xCheckModeSplit函数会调用xCompressCU,尝试更小的划分
      */
      CHECK(maxCostAllowed < 0, "Wrong value of maxCostAllowed!");
      // ============================== INITIALIZE ====================================== //
      uint32_t compBegin;
      uint32_t numComp;
      bool jointPLT = false; // palette模式
      if (partitioner.isSepTree( *tempCS ))
      {
        if( !CS::isDualITree(*tempCS) && partitioner.treeType != TREE_D )
        {
          compBegin = COMPONENT_Y;
          numComp = (tempCS->area.chromaFormat != CHROMA_400)?3: 1;
          jointPLT = true;
        }
        else
        {
          if (isLuma(partitioner.chType))
          {
            compBegin = COMPONENT_Y;
            numComp   = 1;
          }
          else
          {
            compBegin = COMPONENT_Cb;
            numComp   = 2;
          }
        }
      }
      else
      {
        compBegin = COMPONENT_Y;
        numComp = (tempCS->area.chromaFormat != CHROMA_400) ? 3 : 1;
        jointPLT = true;
      }
      SplitSeries splitmode = -1;
      uint8_t   bestLastPLTSize[MAX_NUM_CHANNEL_TYPE];
      Pel       bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT for
      uint8_t   curLastPLTSize[MAX_NUM_CHANNEL_TYPE];
      Pel       curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partition
      for (int i = compBegin; i < (compBegin + numComp); i++)
      {
        ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
        bestLastPLTSize[comID] = 0;
        curLastPLTSize[comID] = tempCS->prevPLT.curPLTSize[comID];
        memcpy(curLastPLT[i], tempCS->prevPLT.curPLT[i], tempCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
      }
      // begin
      Slice&   slice      = *tempCS->slice;
      const PPS &pps      = *tempCS->pps;
      const SPS &sps      = *tempCS->sps;
      const uint32_t uiLPelX  = tempCS->area.Y().lumaPos().x;
      const uint32_t uiTPelY  = tempCS->area.Y().lumaPos().y;
    
      const ModeType modeTypeParent  = partitioner.modeType;
      const TreeType treeTypeParent  = partitioner.treeType;
      const ChannelType chTypeParent = partitioner.chType;
      const UnitArea currCsArea = clipArea( CS::getArea( *bestCS, bestCS->area, partitioner.chType ), *tempCS->picture );
    
      tempCS->splitRdCostBest = nullptr;
      m_modeCtrl->initCULevel( partitioner, *tempCS );  // 该函数对m_modeCtrl进行初始化,对需要test的各个模式进行压栈
    #if GDR_ENABLED
      // DGR(Gradual Decoder Refresh)帧,将一个完整的I帧切片,分别放在前面的多个连续P帧中,可以避免传递I帧造成的网络流量抖动和堵塞
      // refer to https://blog.csdn.net/zg_xd/article/details/107870369
      if (m_pcEncCfg->getGdrEnabled())
      {
        bool isInGdrInterval = slice.getPicHeader()->getInGdrInterval();
    
        // 1.0 applicable to inter picture only
        if (isInGdrInterval)
        {
          int gdrPocStart = m_pcEncCfg->getGdrPocStart();
          int gdrInterval = m_pcEncCfg->getGdrInterval();
          int gdrPeriod = m_pcEncCfg->getGdrPeriod();
    
          int picWidth = slice.getPPS()->getPicWidthInLumaSamples();
          int m1, m2, n1;
    
          int curPoc = slice.getPOC();
          int gdrPoc = (curPoc - gdrPocStart) % gdrPeriod;
    
          int begGdrX = 0;
          int endGdrX = 0;
    
          double dd = (picWidth / (double)gdrInterval);
          int mm = (int)((picWidth / (double)gdrInterval) + 0.49999);
          m1 = ((mm + 7) >> 3) << 3;
          m2 = ((mm + 0) >> 3) << 3;
    
          if (dd > mm && m1 == m2)
          {
            m1 = m1 + 8;
          }
    
          n1 = (picWidth - m2 * gdrInterval) / 8;
    
          if (gdrPoc < n1)
          {
            begGdrX = m1 * gdrPoc;
            endGdrX = begGdrX + m1;
          }
          else
          {
            begGdrX = m1 * n1 + m2 * (gdrPoc - n1);
            endGdrX = begGdrX + m2;
            if (picWidth <= endGdrX)
            {
              begGdrX = picWidth;
              endGdrX = picWidth;
            }
          }
    
          bool isInRefreshArea = tempCS->withinRefresh(begGdrX, endGdrX);
    
          if (isInRefreshArea)
          {
            m_modeCtrl->forceIntraMode();
          }
          else if (tempCS->containRefresh(begGdrX, endGdrX) || tempCS->overlapRefresh(begGdrX, endGdrX))
          {
            // 1.3.1 enable only vertical splits (QT, BT_V, TT_V)
            m_modeCtrl->forceVerSplitOnly();
    
            // 1.3.2 remove TT_V if it does not satisfy the condition
            if (tempCS->refreshCrossTTV(begGdrX, endGdrX))
            {
              m_modeCtrl->forceRemoveTTV();
            }
          }
    
          if (tempCS->area.lwidth() != tempCS->area.lheight())
          {
            m_modeCtrl->forceRemoveQT();
          }
    
          if (!m_modeCtrl->anyPredModeLeft())
          {
            m_modeCtrl->forceRemoveDontSplit();
          }
    
          if (isInRefreshArea && !m_modeCtrl->anyIntraIBCMode() && (tempCS->area.lwidth() == 4 || tempCS->area.lheight() == 4))
          {
            m_modeCtrl->finishCULevel(partitioner);
            return;
          }
        }
      }
    #endif
    
      if (partitioner.currQtDepth == 0 && partitioner.currMtDepth == 0 && !tempCS->slice->isIntra()
          && (sps.getUseSBT() || sps.getExplicitMtsInterEnabled()))
      {
        auto slsSbt = dynamic_cast( m_modeCtrl );
        int maxSLSize = sps.getUseSBT() ? tempCS->slice->getSPS()->getMaxTbSize() : MTS_INTER_MAX_CU_SIZE;
        slsSbt->resetSaveloadSbt( maxSLSize );
      }
      m_sbtCostSave[0] = m_sbtCostSave[1] = MAX_DOUBLE;
    
      m_CurrCtx->start = m_CABACEstimator->getCtx();
    
      if( slice.getUseChromaQpAdj() )
      {
        // TODO M0133 : double check encoder decisions with respect to chroma QG detection and actual encode
        int lgMinCuSize = sps.getLog2MinCodingBlockSize() +
          std::max(0, floorLog2(sps.getCTUSize()) - sps.getLog2MinCodingBlockSize() - int((slice.getCuChromaQpOffsetSubdiv()+1) / 2));
        if( partitioner.currQgChromaEnable() )
        {
          m_cuChromaQpOffsetIdxPlus1 = ( ( uiLPelX >> lgMinCuSize ) + ( uiTPelY >> lgMinCuSize ) ) % ( pps.getChromaQpOffsetListLen() + 1 );
        }
      }
      else
      {
        m_cuChromaQpOffsetIdxPlus1 = 0;
      }
    
      // m_modeCtrl初始化后,如果没有可以test的模式,那么直接return
      if( !m_modeCtrl->anyMode() )
      {
        m_modeCtrl->finishCULevel( partitioner );
        return;
      }
    
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cux", uiLPelX ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuy", uiTPelY ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuw", tempCS->area.lwidth() ) );
      DTRACE_UPDATE( g_trace_ctx, std::make_pair( "cuh", tempCS->area.lheight() ) );
      DTRACE( g_trace_ctx, D_COMMON, "@(%4d,%4d) [%2dx%2d]\n", tempCS->area.lx(), tempCS->area.ly(), tempCS->area.lwidth(), tempCS->area.lheight() );
    
    
      m_pcInterSearch->resetSavedAffineMotion();
    
      double bestIntPelCost = MAX_DOUBLE;
    
      if (tempCS->slice->getSPS()->getUseColorTrans())
      {
        tempCS->tmpColorSpaceCost = MAX_DOUBLE;
        bestCS->tmpColorSpaceCost = MAX_DOUBLE;
        tempCS->firstColorSpaceSelected = true;
        bestCS->firstColorSpaceSelected = true;
      }
    
      if (tempCS->slice->getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
      {
        tempCS->firstColorSpaceTestOnly = false;
        bestCS->firstColorSpaceTestOnly = false;
        tempCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
        tempCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
        bestCS->tmpColorSpaceIntraCost[0] = MAX_DOUBLE;
        bestCS->tmpColorSpaceIntraCost[1] = MAX_DOUBLE;
    
        if (tempCS->bestParent && tempCS->bestParent->firstColorSpaceTestOnly)
        {
          tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
        }
      }
    
      double splitRdCostBest[NUM_PART_SPLIT];
      std::fill(std::begin(splitRdCostBest), std::end(splitRdCostBest), MAX_DOUBLE);
      if (tempCS->slice->getCheckLDC())
      {
        m_bestBcwCost[0] = m_bestBcwCost[1] = std::numeric_limits::max();
        m_bestBcwIdx[0] = m_bestBcwIdx[1] = -1;
      }
      // ============================== MAIN LOOP ====================================== //
      do
      {
        for (int i = compBegin; i < (compBegin + numComp); i++)
        {
          ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
          tempCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
          memcpy(tempCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
        }
        EncTestMode currTestMode = m_modeCtrl->currTestMode();  // 获取需要test的模式
        currTestMode.maxCostAllowed = maxCostAllowed;
    
        if (pps.getUseDQP() && partitioner.isSepTree(*tempCS) && isChroma( partitioner.chType ))
        {
          const Position chromaCentral(tempCS->area.Cb().chromaPos().offset(tempCS->area.Cb().chromaSize().width >> 1, tempCS->area.Cb().chromaSize().height >> 1));
          const Position lumaRefPos(chromaCentral.x << getComponentScaleX(COMPONENT_Cb, tempCS->area.chromaFormat), chromaCentral.y << getComponentScaleY(COMPONENT_Cb, tempCS->area.chromaFormat));
          const CodingStructure* baseCS = bestCS->picture->cs;
          const CodingUnit* colLumaCu = baseCS->getCU(lumaRefPos, CHANNEL_TYPE_LUMA);
    
          if (colLumaCu)
          {
            currTestMode.qp = colLumaCu->qp;
          }
        }
    
    #if SHARP_LUMA_DELTA_QP || ENABLE_QPA_SUB_CTU
        if (partitioner.currQgEnable() && (
            (m_pcEncCfg->getBIM()) ||
    #if SHARP_LUMA_DELTA_QP
            (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()) ||
    #endif
            (m_pcEncCfg->getSmoothQPReductionEnable()) ||
    #if ENABLE_QPA_SUB_CTU
            (m_pcEncCfg->getUsePerceptQPA() && !m_pcEncCfg->getUseRateCtrl() && pps.getUseDQP())
    #else
            false
    #endif
          ))
        {
          if (currTestMode.qp >= 0)
          {
            updateLambda (&slice, currTestMode.qp,
     #if WCG_EXT && ER_CHROMA_QP_WCG_PPS
                          m_pcEncCfg->getWCGChromaQPControl().isEnabled(),
     #endif
                          CS::isDualITree (*tempCS) || (partitioner.currDepth == 0));
          }
        }
    #endif
    
        if( currTestMode.type == ETM_INTER_ME )
        {
          if( ( currTestMode.opts & ETO_IMV ) != 0 )
          {
            const bool skipAltHpelIF = ( int( ( currTestMode.opts & ETO_IMV ) >> ETO_IMV_SHIFT ) == 4 ) && ( bestIntPelCost > 1.25 * bestCS->cost );
            if (!skipAltHpelIF)
            {
              tempCS->bestCS = bestCS;
              xCheckRDCostInterIMV(tempCS, bestCS, partitioner, currTestMode, bestIntPelCost); // 整像素inter ME, 1 pel and 4 pel
              tempCS->bestCS = nullptr;
              splitRdCostBest[CTU_LEVEL] = bestCS->cost;
              tempCS->splitRdCostBest = splitRdCostBest;
            }
          }
          else
          {
            tempCS->bestCS = bestCS;
            xCheckRDCostInter( tempCS, bestCS, partitioner, currTestMode );  // 帧间inter ME模式
            tempCS->bestCS = nullptr;
            splitRdCostBest[CTU_LEVEL] = bestCS->cost;
            tempCS->splitRdCostBest = splitRdCostBest;
          }
    
        }
        else if (currTestMode.type == ETM_HASH_INTER)
        {
          xCheckRDCostHashInter( tempCS, bestCS, partitioner, currTestMode );
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if( currTestMode.type == ETM_AFFINE )
        {
          xCheckRDCostAffineMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode ); // Affine merge模式
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
    #if REUSE_CU_RESULTS
        else if( currTestMode.type == ETM_RECO_CACHED )
        {
          xReuseCachedResult( tempCS, bestCS, partitioner );
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
    #endif
        else if( currTestMode.type == ETM_MERGE_SKIP )
        {
          xCheckRDCostMerge2Nx2N( tempCS, bestCS, partitioner, currTestMode );  // merge模式
          CodingUnit* cu = bestCS->getCU(partitioner.chType);
          if (cu)
          {
            cu->mmvdSkip = cu->skip == false ? false : cu->mmvdSkip;
          }
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if( currTestMode.type == ETM_MERGE_GEO )
        {
          xCheckRDCostMergeGeo2Nx2N( tempCS, bestCS, partitioner, currTestMode );  // mergeGeo模式
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if( currTestMode.type == ETM_INTRA )
        {
          if (slice.getSPS()->getUseColorTrans() && !CS::isDualITree(*tempCS))
          {
            bool skipSecColorSpace = false;
            skipSecColorSpace = xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? true : false));  // intra模式
            if ((m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()) && !m_pcEncCfg->getRGBFormatFlag())
            {
              skipSecColorSpace = true;
            }
            if (!skipSecColorSpace && !tempCS->firstColorSpaceTestOnly)
            {
              xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, (m_pcEncCfg->getRGBFormatFlag() ? false : true));
            }
    
            if (!tempCS->firstColorSpaceTestOnly)
            {
              if (tempCS->tmpColorSpaceIntraCost[0] != MAX_DOUBLE && tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE)
              {
                double skipCostRatio = m_pcEncCfg->getRGBFormatFlag() ? 1.1 : 1.0;
                if (tempCS->tmpColorSpaceIntraCost[1] > (skipCostRatio*tempCS->tmpColorSpaceIntraCost[0]))
                {
                  tempCS->firstColorSpaceTestOnly = bestCS->firstColorSpaceTestOnly = true;
                }
              }
            }
            else
            {
              CHECK(tempCS->tmpColorSpaceIntraCost[1] != MAX_DOUBLE, "the RD test of the second color space should be skipped");
            }
          }
          else
          {
            xCheckRDCostIntra(tempCS, bestCS, partitioner, currTestMode, false);
          }
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if (currTestMode.type == ETM_PALETTE)
        {
          xCheckPLT( tempCS, bestCS, partitioner, currTestMode );
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if (currTestMode.type == ETM_IBC)
        {
          xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if (currTestMode.type == ETM_IBC_MERGE)
        {
          xCheckRDCostIBCModeMerge2Nx2N(tempCS, bestCS, partitioner, currTestMode);  // IBC模式
          splitRdCostBest[CTU_LEVEL] = bestCS->cost;
          tempCS->splitRdCostBest = splitRdCostBest;
        }
        else if( isModeSplit( currTestMode ) )
        {
        // QT,BT, TT划分
          if (bestCS->cus.size() != 0)
          {
            splitmode = bestCS->cus[0]->splitSeries;
          }
          assert( partitioner.modeType == tempCS->modeType );
          int signalModeConsVal = tempCS->signalModeCons( getPartSplit( currTestMode ), partitioner, modeTypeParent );
          int numRoundRdo = signalModeConsVal == LDT_MODE_TYPE_SIGNAL ? 2 : 1;
          bool skipInterPass = false;
          for( int i = 0; i < numRoundRdo; i++ )
          {
            //change cons modes
            if( signalModeConsVal == LDT_MODE_TYPE_SIGNAL )
            {
              CHECK( numRoundRdo != 2, "numRoundRdo shall be 2 - [LDT_MODE_TYPE_SIGNAL]" );
              tempCS->modeType = partitioner.modeType = (i == 0) ? MODE_TYPE_INTER : MODE_TYPE_INTRA;
            }
            else if( signalModeConsVal == LDT_MODE_TYPE_INFER )
            {
              CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INFER]" );
              tempCS->modeType = partitioner.modeType = MODE_TYPE_INTRA;
            }
            else if( signalModeConsVal == LDT_MODE_TYPE_INHERIT )
            {
              CHECK( numRoundRdo != 1, "numRoundRdo shall be 1 - [LDT_MODE_TYPE_INHERIT]" );
              tempCS->modeType = partitioner.modeType = modeTypeParent;
            }
    
            //for lite intra encoding fast algorithm, set the status to save inter coding info
            if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType == MODE_TYPE_INTER )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( true );
              m_pcIntraSearch->setNumCuInSCIPU( 0 );
            }
            else if( modeTypeParent == MODE_TYPE_ALL && tempCS->modeType != MODE_TYPE_INTER )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( false );
              if( tempCS->modeType == MODE_TYPE_ALL )
              {
                m_pcIntraSearch->setNumCuInSCIPU( 0 );
              }
            }
            // check QT、BT和TT划分
            xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode, modeTypeParent, skipInterPass, splitRdCostBest );
            tempCS->splitRdCostBest = splitRdCostBest;
            //recover cons modes
            tempCS->modeType = partitioner.modeType = modeTypeParent;
            tempCS->treeType = partitioner.treeType = treeTypeParent;
            partitioner.chType = chTypeParent;
            if( modeTypeParent == MODE_TYPE_ALL )
            {
              m_pcIntraSearch->setSaveCuCostInSCIPU( false );
              if( numRoundRdo == 2 && tempCS->modeType == MODE_TYPE_INTRA )
              {
                m_pcIntraSearch->initCuAreaCostInSCIPU();
              }
            }
            if( skipInterPass )
            {
              break;
            }
          }
    #if GDR_ENABLED
          if (bestCS->cus.size() > 0 && splitmode != bestCS->cus[0]->splitSeries)
    #else
          if (splitmode != bestCS->cus[0]->splitSeries)
    #endif
          {
            splitmode = bestCS->cus[0]->splitSeries;
            const CodingUnit&     cu = *bestCS->cus.front();
            cu.cs->prevPLT = bestCS->prevPLT;
            for (int i = compBegin; i < (compBegin + numComp); i++)
            {
              ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
              bestLastPLTSize[comID] = bestCS->cus[0]->cs->prevPLT.curPLTSize[comID];
              memcpy(bestLastPLT[i], bestCS->cus[0]->cs->prevPLT.curPLT[i], bestCS->cus[0]->cs->prevPLT.curPLTSize[comID] * sizeof(Pel));
            }
          }
        }
        else
        {
          THROW( "Don't know how to handle mode: type = " << currTestMode.type << ", options = " << currTestMode.opts );
        }
      } while( m_modeCtrl->nextMode( *tempCS, partitioner ) );  // m_modeCtrl->nextMode将当前mode出栈,返回bool值判断是否还有mode需要test
      // ============================== END LOOP ====================================== //
    
      // ============================== FINISH ====================================== //
      // Finishing CU, 将最优划分模式和最优划分方法都存储在bestCS中
      if( tempCS->cost == MAX_DOUBLE && bestCS->cost == MAX_DOUBLE )
      {
        //although some coding modes were planned to be tried in RDO, no coding mode actually finished encoding due to early termination
        //thus tempCS->cost and bestCS->cost are both MAX_DOUBLE; in this case, skip the following process for normal case
        m_modeCtrl->finishCULevel( partitioner );
        return;
      }
    
      // 更新参数
      // set context states
      m_CABACEstimator->getCtx() = m_CurrCtx->best;
    
      // QP from last processed CU for further processing
      // copy the qp of the last non-chroma CU
      int numCUInThisNode = (int)bestCS->cus.size();
      if( numCUInThisNode > 1 && bestCS->cus.back()->chType == CHANNEL_TYPE_CHROMA && !CS::isDualITree( *bestCS ) )
      {
        CHECK( bestCS->cus[numCUInThisNode-2]->chType != CHANNEL_TYPE_LUMA, "wrong chType" );
        bestCS->prevQP[partitioner.chType] = bestCS->cus[numCUInThisNode-2]->qp;
      }
      else
      {
        bestCS->prevQP[partitioner.chType] = bestCS->cus.back()->qp;
      }
      if ((!slice.isIntra() || slice.getSPS()->getIBCFlag()) && partitioner.chType == CHANNEL_TYPE_LUMA
          && bestCS->cus.size() == 1 && (CU::isInter(*bestCS->cus.back()) || CU::isIBC(*bestCS->cus.back()))
          && bestCS->area.Y() == (*bestCS->cus.back()).Y())
      {
        const CodingUnit&     cu = *bestCS->cus.front();
    
        bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);
        CU::saveMotionInHMVP( cu, isIbcSmallBlk );   // 更新HMVP列表
      }
      // 将最优模式的重建像素copy给picture
      bestCS->picture->getPredBuf(currCsArea).copyFrom(bestCS->getPredBuf(currCsArea));
      bestCS->picture->getRecoBuf(currCsArea ).copyFrom( bestCS->getRecoBuf( currCsArea ) );
      m_modeCtrl->finishCULevel( partitioner );  // m_modelCtrl->finishCULevel
      if( m_pcIntraSearch->getSaveCuCostInSCIPU() && bestCS->cus.size() == 1 )
      {
        m_pcIntraSearch->saveCuAreaCostInSCIPU( Area( partitioner.currArea().lumaPos(), partitioner.currArea().lumaSize() ), bestCS->cost );
      }
    
      if (bestCS->cus.size() == 1) // no partition
      {
        CHECK(bestCS->cus[0]->tileIdx != bestCS->pps->getTileIdx(bestCS->area.lumaPos()), "Wrong tile index!");
        if (CU::isPLT(*bestCS->cus[0]))
        {
          for (int i = compBegin; i < (compBegin + numComp); i++)
          {
            ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
            bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
            memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
          }
          bestCS->reorderPrevPLT(bestCS->prevPLT, bestCS->cus[0]->curPLTSize, bestCS->cus[0]->curPLT, bestCS->cus[0]->reuseflag, compBegin, numComp, jointPLT);
        }
        else
        {
          for (int i = compBegin; i<(compBegin + numComp); i++)
          {
            ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
            bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
            memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
          }
        }
      }
      else
      {
        for (int i = compBegin; i<(compBegin + numComp); i++)
        {
          ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
          bestCS->prevPLT.curPLTSize[comID] = bestLastPLTSize[comID];
          memcpy(bestCS->prevPLT.curPLT[i], bestLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
        }
      }
      const CodingUnit&     cu = *bestCS->cus.front();
      cu.cs->prevPLT = bestCS->prevPLT;
      // Assert if Best prediction mode is NONE
      // Selected mode's RD-cost must be not MAX_DOUBLE.
      CHECK( bestCS->cus.empty()                                   , "No possible encoding found" );
      CHECK( bestCS->cus[0]->predMode == NUMBER_OF_PREDICTION_MODES, "No possible encoding found" );
      CHECK( bestCS->cost             == MAX_DOUBLE                , "No possible encoding found" );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565

    块划分xCheckModeSplit

    该函数的主要部分是一个do-while循环,遍历子CU的区域,和x265相同的是,也会比较当前累计的cost和最优cost,用来进行提前终止。

    该部分的代码和注释:

    void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode, const ModeType modeTypeParent, bool &skipInterPass, double *splitRdCostBest )
    {
      const int qp                = encTestMode.qp;
      const Slice &slice          = *tempCS->slice;
      const int oldPrevQp         = tempCS->prevQP[partitioner.chType];
      const auto oldMotionLut     = tempCS->motionLut;  // LUT(Look Up Table) 用于熵解码
    #if ENABLE_QPA_SUB_CTU
      const PPS &pps              = *tempCS->pps;
      const uint32_t currDepth    = partitioner.currDepth;
    #endif
      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();
    
      m_CABACEstimator->split_cu_mode( split, *tempCS, partitioner );  // 设置划分CU的模式
      m_CABACEstimator->mode_constraint( split, *tempCS, partitioner, modeTypeChild ); // 设置模式限制
    
      double costTemp = 0;
      // m_pcEncCfg->getFastAdaptCostPredMode()   "Mode for split cost prediction, 0..2 (Default: 0)"
      if( m_pcEncCfg->getFastAdaptCostPredMode() == 2 )
      {
        int numChild = 3;
        if( split == CU_VERT_SPLIT || split == CU_HORZ_SPLIT )
        {
          numChild--;
        }
        else if( split == CU_QUAD_SPLIT )
        {
          numChild++;
        }
    
        int64_t approxBits = numChild << SCALE_BITS;
    
        const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.11 : 1.085 )
          + ( isChroma( partitioner.chType ) ? 0.2 : 0.0 ); // 计算cost的因子,如果当前块的QP>30,设置为1.11,否则设置为1.085
    
        costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + approxBits + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;
      }
      else if ( m_pcEncCfg->getFastAdaptCostPredMode() == 1) 
      {
        const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 )
        + (isChroma(partitioner.chType) ? 0.2 : 0.0);
        costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;
      }
      else
      {
        // Default
        const double factor = ( tempCS->currQP[partitioner.chType] > 30 ? 1.1 : 1.075 );
        costTemp = m_pcRdCost->calcRdCost( uint64_t( m_CABACEstimator->getEstFracBits() + ( ( bestCS->fracBits ) / factor ) ), Distortion( bestCS->dist / factor ) ) + bestCS->costDbOffset / factor;
      }
      tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
      if( !tempCS->useDbCost )
      {
        CHECK( bestCS->costDbOffset != 0, "error" );
      }
      const double cost = costTemp;	
      // 更新上下文参数
      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,则重置上下文状态并确认beseCS
      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;
      }
    
    
      const bool chromaNotSplit = modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTRA ? true : false; // 父节点的所有类型都ok,且子节点为MODE_TYPE_INTRA时, chromaNotSplit=True
      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才是dual tree
          tempCS->treeType = partitioner.treeType = TREE_D;
        }
      }
    
    
      partitioner.splitCurrArea( split, *tempCS ); // 根据划分模式确定划分区域, refer to https://blog.csdn.net/dfhg54/article/details/124828138
    
      // QG(Quantization group): 把一帧图像划分为固定大小的正方形像素块,大小由PPS指定,且必须处于CTU和最小CU之间,
      //                         同一个QG内的所有非零系数的CU共享一个QP; HEVC中使用相邻QG的信息预测当前QG内的QP, predQP = (QP_A + Qp_B + 1) >> 1
      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;
      if (!tempCS->slice->isIntra())
      {
        m_pcInterSearch->savePrevUniMvInfo(tempCS->area.Y(), tmpUniMvInfo, isUniMvInfoSaved);
      }
    
      do
      {
        const auto &subCUArea  = partitioner.currArea();  // 将测试的子区域的YCbCr分量的位置和宽高赋给subCUArea;
                                                          // auto作为C/C++存储类型之一,自动存储期,即当退出程序块的时候自动销毁;
                                                          // auto可以自动推断数据类型(编译期),可以代替复杂冗长的类型声明
                                                          // auto类型不能被声明为返回值、形参 和 模板参数
        if( tempCS->picture->Y().contains( subCUArea.lumaPos() ) )
        {
          const unsigned wIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lwidth () );  // 返回宽高的对数作为索引值,例如宽=64,wIdx=6
          const unsigned hIdx    = gp_sizeIdxInfo->idxFrom( subCUArea.lheight() );
    
          CodingStructure *tempSubCS = m_pTempCS[wIdx][hIdx]; // 从已划分CU的wIdx和hIdx推出新的tempCs
          CodingStructure *bestSubCS = m_pBestCS[wIdx][hIdx];
    
          tempCS->initSubStructure( *tempSubCS, partitioner.chType, subCUArea, false );  // 初始化子CU的节点,将CTU级别的信息传输给子CU节点, refer to https://blog.csdn.net/dfhg54/article/details/124304502
          tempCS->initSubStructure( *bestSubCS, partitioner.chType, subCUArea, false );
          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);  // 递归调用xCompressCU
          tempSubCS->bestParent = bestSubCS->bestParent = nullptr;
          // 子CU的cost是正无穷(?),结束划分
          if( bestSubCS->cost == MAX_DOUBLE )
          {
            CHECK( split == CU_QUAD_SPLIT, "Split decision reusing cannot skip quad split" );
            tempCS->cost = MAX_DOUBLE;
            tempCS->costDbOffset = 0;
            tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
            m_CurrCtx--;
            partitioner.exitCurrSplit();
            xCheckBestMode( tempCS, bestCS, partitioner, encTestMode ); // 检查当前模式是否为最佳模式,若为最佳模式则与 bestCS 交换
            if( partitioner.chType == CHANNEL_TYPE_LUMA )
            {
              tempCS->motionLut = oldMotionLut;
            }
            return;
          }
    
          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, true ); // 将子CU的最优模式copy到tempCS中
    
          if( partitioner.currQgEnable() )
          {
            tempCS->prevQP[partitioner.chType] = bestSubCS->prevQP[partitioner.chType]; // 如果当前QG可用,使用子CU prevQP 更新当前 tempCS 的prevQP
          }
          // 循环遍历所有子CU,check是否有不相容的模式
          if( partitioner.isConsInter() )
          {
            for( int i = 0; i < bestSubCS->cus.size(); i++ )
            {
              CHECK(!CU::isInter(*bestSubCS->cus[i]), "all CUs must be inter mode in an Inter coding region (SCIPU)");
            }
          }
          else if( partitioner.isConsIntra() )
          {
            for( int i = 0; i < bestSubCS->cus.size(); i++ )
            {
              CHECK(CU::isInter(*bestSubCS->cus[i]), "all CUs must not be inter mode in an Intra coding region (SCIPU)");
            }
          }
    
          tempSubCS->releaseIntermediateData();
          bestSubCS->releaseIntermediateData();
          // 如果当前不在I Slice,而且限制只能test帧内
          if( !tempCS->slice->isIntra() && partitioner.isConsIntra() )
          {
            tempCS->cost = m_pcRdCost->calcRdCost( tempCS->fracBits, tempCS->dist );
            // 如果计算到目前的tempCS的cost已经大于bestCS的cost,则提前终止块划分的尝试
            if( tempCS->cost > bestCS->cost )
            {
              tempCS->cost = MAX_DOUBLE;
              tempCS->costDbOffset = 0;
              tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
              m_CurrCtx--;
              partitioner.exitCurrSplit();
              if( partitioner.chType == CHANNEL_TYPE_LUMA )
              {
                tempCS->motionLut = oldMotionLut;
              }
              return;
            }
          }
        }
      } while( partitioner.nextPart( *tempCS ) ); // 尝试所有的划分子CU
    
      partitioner.exitCurrSplit();
    
    
      m_CurrCtx--;
    
      if( chromaNotSplit )
      {
        // 这部分代码需要refer to https://blog.csdn.net/dfhg54/article/details/124291272
    
        //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.
        if( tempCS->pps->getUseDQP() )
        {
          //find parent CS that including all coded CUs in the QG before this node
          CodingStructure* qgCS = tempCS;
          bool deltaQpCodedBeforeThisNode = false;
          if( partitioner.currArea().lumaPos() != partitioner.currQgPos )
          {
            int numParentNodeToQgCS = 0;
            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;
            // 逐层遍历当前父节点的所有CU,检查deltaQP在当前节点之前是否已经编码
            for( int i = 0; i < numParentNodeToQgCS; i++ )
            {
              //checking each parent
              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
          if( !deltaQpCodedBeforeThisNode )
          {
            //get pred QP of the QG
            const CodingUnit* cuFirst = qgCS->getCU( CHANNEL_TYPE_LUMA );
            CHECK( cuFirst->lumaPos() != partitioner.currQgPos, "First cu of the Qg is wrong" );
            int predQp = CU::predictQP( *cuFirst, qgCS->prevQP[CHANNEL_TYPE_LUMA] );
    
            //revise to predQP
            int firstCuHasResidual = (int)tempCS->cus.size();
            for( int i = 0; i < tempCS->cus.size(); i++ )
            {
              if( tempCS->cus[i]->rootCbf )
              {
                firstCuHasResidual = i;
                break;
              }
            }
    
            for( int i = 0; i < firstCuHasResidual; i++ )
            {
              tempCS->cus[i]->qp = predQp;
            }
          }
        }
        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 );
    
        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;
      }
      else
      {
        if (!qgChromaEnableChildren) // check at deepest cQG level only
        {
          xCheckChromaQPOffset( *tempCS, partitioner );
        }
      }
    
      // Finally, generate split-signaling bits for 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( &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;
        }
      }
    
      splitRdCostBest[getPartSplit(encTestMode)] = tempCS->cost;
      // RD check for sub partitioned coding structure.
      xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );  // 比较tempCS和bestCS
    
    #if GDR_ENABLED
      if (isAffMVInfoSaved)
      {
        m_pcInterSearch->addAffMVInfo(tmpMVInfo, tmpMVInfoSolid);
      }
    #else
      if (isAffMVInfoSaved)
      {
        m_pcInterSearch->addAffMVInfo(tmpMVInfo);
      }
    #endif
    
      if (!tempCS->slice->isIntra() && isUniMvInfoSaved)
      {
        m_pcInterSearch->addUniMvInfo(tmpUniMvInfo);
      }
    
      tempCS->motionLut = oldMotionLut;
    
      tempCS->prevPLT   = oldPLT;
    
      tempCS->releaseIntermediateData();
    
      tempCS->prevQP[partitioner.chType] = oldPrevQp;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
  • 相关阅读:
    pytorch数据增强
    未来的嵌入式行业岗位发展怎么样?会不会越来越少?
    Spring Cloud Gateway + Knife4j 4.3 实现微服务网关聚合接口文档
    独立站源码搭建,跨境独立站怎么搭建?
    YOLOV8目标检测——最全最完整模型训练过程记录
    LoRaWan协议解析
    134. 加油站
    Prometheus Operator 自定义监控添加redis explorer
    自动驾驶---Control之LQR控制
    STTextBox - 一个纯GDI开发的开源WinForm控件
  • 原文地址:https://blog.csdn.net/weixin_43721070/article/details/127602200