vvc代码阅读 encodeCtus()

CodingStructure&  cs            = *pcPic->cs;
  Slice* pcSlice                  = cs.slice;
  const PreCalcValues& pcv        = *cs.pcv;//提前计算好的数据,所有ctu通用
  const uint32_t        widthInCtus   = pcv.widthInCtus;//可知一帧中一行有的ctu个数
#if ENABLE_QPA
  const int iQPIndex              = pcSlice->getSliceQpBase();
#endif

  CABACWriter*    pCABACWriter    = pEncLib->getCABACEncoder()->getCABACEstimator( pcSlice->getSPS() );
  TrQuant*        pTrQuant        = pEncLib->getTrQuant();//transform 和 quantization
  RdCost*         pRdCost         = pEncLib->getRdCost();//率失真优化
  EncCfg*         pCfg            = pEncLib;
  RateCtrl*       pRateCtrl       = pEncLib->getRateCtrl();
  pRdCost->setLosslessRDCost(pcSlice->isLossless());
#if RDOQ_CHROMA_LAMBDA   //色度的率失真优化量化的lambda
  pTrQuant    ->setLambdas( pcSlice->getLambdas() );
#else
  pTrQuant    ->setLambda ( pcSlice->getLambdas()[0] );
#endif
  pRdCost     ->setLambda ( pcSlice->getLambdas()[0], pcSlice->getSPS()->getBitDepths() );
#if WCG_EXT && ER_CHROMA_QP_WCG_PPS && ENABLE_QPA
  if (!pCfg->getWCGChromaQPControl().isEnabled() && pCfg->getUsePerceptQPA() && !pCfg->getUseRateCtrl())
  {
    pRdCost->saveUnadjustedLambda();
  }
#endif

 CodingStructure&  cs    = *pcPic->cs //存储一帧中所有编码数据的cs。 *pcPic是传输过来的图片指针,指向coding strcture,然后赋值给以引用方式新建立的一个cs对象,可以在后面直接修改

H.266/VVC-VTM代码学习25-VTM中RDcost的计算与λ的设定(一)_liaojq2020的博客-CSDN博客

下面是一些初始化,有些是关于变换和量化的,之后看

 int prevQP[2];
  int currQP[2];
  prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
  currQP[0] = currQP[1] = pcSlice->getSliceQp();

  prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
  if ( pcSlice->getSPS()->getFpelMmvdEnabledFlag() ||
      (pcSlice->getSPS()->getIBCFlag() && m_pcCuEncoder->getEncCfg()->getIBCHashSearch()))
  {
    m_pcCuEncoder->getIbcHashMap().rebuildPicHashMap(cs.picture->getTrueOrigBuf());
    if (m_pcCfg->getIntraPeriod() != -1)
    {
      int hashBlkHitPerc = m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y());
      cs.slice->setDisableSATDForRD(hashBlkHitPerc > 59);
    }
    if ((pcSlice->getSPS()->getSpsRangeExtension().getTSRCRicePresentFlag()) && (m_pcGOPEncoder->getPreQP() != pcSlice->getSliceQp()) && (pcPic->cs->pps->getNumSlicesInPic() == 1) && (pcSlice->get_tsrc_index() > 0) && (pcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) <= 12))
    {
      uint32_t totalCtu  = 0;
      uint32_t hashRatio = 0;
      for (uint32_t ctuIdx = 0; ctuIdx < pcSlice->getNumCtuInSlice(); ctuIdx++)
      {
        const uint32_t ctuRsAddr     = pcSlice->getCtuAddrInSlice(ctuIdx);
        const uint32_t ctuXPosInCtus = ctuRsAddr % widthInCtus;
        const uint32_t ctuYPosInCtus = ctuRsAddr / widthInCtus;
        const Position pos(ctuXPosInCtus * pcv.maxCUWidth, ctuYPosInCtus * pcv.maxCUHeight);
        const UnitArea ctuArea(cs.area.chromaFormat, Area(pos.x, pos.y, pcv.maxCUWidth, pcv.maxCUHeight));

        hashRatio += m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y());
        totalCtu++;
      }
if (totalCtu > 0)
      {
        if ((hashRatio < 4200) || (hashRatio < (41 * totalCtu)))
        {
          pcSlice->set_tsrc_index(0);
        }
      }
    }
  }

prevQP[], currQP[]在compressSlice()中也设置过,当时是设置图片的先前和现在的QP值。0和1分别代表亮度和色度,2代表MAX_NUM_CHANNEL_TYPE

现在是设置这两个参数,然后读取之前设置的QP值,然后赋值

​​​​​​10分钟拿下 HashMap_一小页的博客-CSDN博客

getIBCFlag():IntraBlockCopy(帧内块复制)模式标志,IBC默认false

IntraPeriod:I帧间隔。当为-1时代表没有间隔

第一个if语句:求hashBlkHitPerc值,perc推测是perceptually的缩写,如果大于59,则不适用SATD

SAD和SATD的区别_yanbdsky的博客-CSDN博客_satd

第二个if语句:同时满足五个条件,(1)TSRCRicePresent  开启(2)亮度的encodeGOP的preQP值 不等于现在slice的QP值   (3)一幅图中只有一个Slice (4)TSRC(Transform Skip Residual Coding),变换跳跃残差编码的目录大于0  (5)亮度通道的比特深度小于12

TSRC默认为false,所以这个if语句跳过。

// for every CTU in the slice
  for( uint32_t ctuIdx = 0; ctuIdx < pcSlice->getNumCtuInSlice(); ctuIdx++ )
  {
    const int32_t ctuRsAddr = pcSlice->getCtuAddrInSlice( ctuIdx );

    // update CABAC state
    const uint32_t ctuXPosInCtus        = ctuRsAddr % widthInCtus;
    const uint32_t ctuYPosInCtus        = ctuRsAddr / widthInCtus;

    const Position pos (ctuXPosInCtus * pcv.maxCUWidth, ctuYPosInCtus * pcv.maxCUHeight);
    const UnitArea ctuArea( cs.area.chromaFormat, Area( pos.x, pos.y, pcv.maxCUWidth, pcv.maxCUHeight ) );
    DTRACE_UPDATE( g_trace_ctx, std::make_pair( "ctu", ctuRsAddr ) );

    if( pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU() )
    if ((cs.slice->getSliceType() != I_SLICE || cs.sps->getIBCFlag()) && cs.pps->ctuIsTileColBd( ctuXPosInCtus ))
    {
      cs.motionLut.lut.resize(0);
      cs.motionLut.lutIbc.resize(0);
    }

对Slice中所有Ctu进行处理

注意这里

ctuRsAddr:这里的RS是raster-scan的缩写,表示光栅扫描模式下slice中的每一个ctu的地址。

值跟ctuIdx一样

ctuXPosInCtus,ctuYPosInCtus:算出当前Ctu的x,y位置坐标。比如ctuRsAddr = 0时,x,y分别等于0,0,表明这个CTU的位置为(0,0).ctuRsAddr = 13时,x,y分别等于6,1,表明这个CTU的位置为(6,1)。这里面每一个块都是一个CTU,这个地址是CTU的相对地址

注意:这个图为832X480,CTU大小默认设置为128 X 128,而如果除下来,CTU应该只有24.3个,这与代码中28个的结果不同。原因是在边界不足以划分一个完整的CTU时,图像做了填充,加大了一部分面积,使一个CTU被完整划分出来。填充前图像宽度叫width,填充后的图像宽度叫stride。之后再具体了解

Image Stride(内存图像行跨度)_jsn_ze的博客-CSDN博客

pcv.maxCUWidth:PreCalcValue下的maxCUWidth,好像锁定为128,我改cfg文件中的maxCUWidth也没有变化。之后再看。推测可认定这个就为CTU的宽度,之后再看是否正确

pos():因为cu的大小可以像CTU一样大,这里又设置为128.所以ctuXPosInCtus * pcv.maxCUWidth即可表示当前CTU的位置,像素为基本单位。

ctuArea():表示要处理的CTU的位置和大小还有这个位置的色度采样格式

ctuIsTileColBd():设置m_tileColBd,为ture时代表CTU在Tile的左边界线

第一个if语句:当前帧是B或P帧,或者允许开启IBC且CTU在Tile的左边界线,需要清空motionLut,里面存储着HMVP候选列表

const SubPic &curSubPic = pcSlice->getPPS()->getSubPicFromPos(pos);
    // padding/restore at slice level
    if (pcSlice->getPPS()->getNumSubPics() >= 2 && curSubPic.getTreatedAsPicFlag() && ctuIdx == 0)
    {
      int subPicX = (int)curSubPic.getSubPicLeft();
      int subPicY = (int)curSubPic.getSubPicTop();
      int subPicWidth = (int)curSubPic.getSubPicWidthInLumaSample();
      int subPicHeight = (int)curSubPic.getSubPicHeightInLumaSample();

      for (int rlist = REF_PIC_LIST_0; rlist < NUM_REF_PIC_LIST_01; rlist++)
      {
        int n = pcSlice->getNumRefIdx((RefPicList)rlist);//n应该是参考帧的总数量
        for (int idx = 0; idx < n; idx++)
        {
          Picture *refPic = pcSlice->getRefPic((RefPicList)rlist, idx);//从参考帧列表中得到参考帧及其序号

          if( !refPic->getSubPicSaved() && refPic->subPictures.size() > 1 )//如果参考帧的subpicture的边界没有被保存,且参考帧的有subpicure划分
          {
            refPic->saveSubPicBorder(refPic->getPOC(), subPicX, subPicY, subPicWidth, subPicHeight);
            refPic->extendSubPicBorder(refPic->getPOC(), subPicX, subPicY, subPicWidth, subPicHeight);
            refPic->setSubPicSaved(true);
          }
        }
      }
    }

VTM10.0代码学习3:DecSlice_decompressSlice()_柴门风雪夜的博客-CSDN博客

curSubPic:包含当前CTU的subpicture

curSubPic.getTreatedAsPicFlag():将子图片看做图片,为true时则子图片的边界也被当成图片的边界,环路滤波时不执行这个。在当前设置下,默认为true。

第一个if语句:在子图片数量大于2,getTreatedAsPicFlag为TRUE且CTU序号为0时。将子图片的左和上边界设为子图片的X,Y,将子图片在亮度分量中的宽度和高度设为子图片的宽度和高度。

for循环:猜测NUM_REF_PIC_LIST_01为参考帧列表0和1都放在一起,全包括。此处遍历所有参考帧列表的每一个参考帧,如果参考帧也有subpicture划分,且没有保存的边界,那么就要进行以下操作(这个没有验证,之后再看):

  • saveSubPicBorder():保存未被扩展的边界
  • extendSubPicBorder():扩展边界
  • setSubPicSaved():设置有边界被保存
if (cs.pps->ctuIsTileColBd( ctuXPosInCtus ) && cs.pps->ctuIsTileRowBd( ctuYPosInCtus ))
    {
      pCABACWriter->initCtxModels( *pcSlice );
      cs.resetPrevPLT(cs.prevPLT);
      prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
    }
    else if (cs.pps->ctuIsTileColBd( ctuXPosInCtus ) && pEncLib->getEntropyCodingSyncEnabledFlag())
    {
      // reset and then update contexts to the state at the end of the top CTU (if within current slice and tile).
      pCABACWriter->initCtxModels( *pcSlice );
      cs.resetPrevPLT(cs.prevPLT);
      if( cs.getCURestricted( pos.offset(0, -1), pos, pcSlice->getIndependentSliceIdx(), cs.pps->getTileIdx( pos ), CH_L ) )
      {
        // Top is available, we use it.
        pCABACWriter->getCtx() = pEncLib->m_entropyCodingSyncContextState;
        cs.setPrevPLT(pEncLib->m_palettePredictorSyncState);
      }
      prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
    }

第一个if语句:如果CTU同时在Tile的上和左边界,则

  • initCtxModels():初始化上下文模型。看CABAC时注意这里,H.264将一个片内可能出现的数据划分为 399 个上下文模型,每个模型以 ctxIdx 标识
  • 重置prePLT,palette mode coding
  • 重置色度和亮度的preQP

else if语句:如果CTU在Tile的左边界且开启了EntropyCodingSync

  • initCtxModels():初始化上下文模型。看CABAC时注意这里
  • 重置prePLT,palette mode coding
  • 重置色度和亮度的preQP
  • 其中如果getCURestricted()为true,重新设置CABAC上下文模型的ctx和prePLT模式
  const double oldLambda = pRdCost->getLambda();
    if ( pCfg->getUseRateCtrl() )
    {
      int estQP        = pcSlice->getSliceQp();
      double estLambda = -1.0;
      double bpp       = -1.0;

      if( ( pcPic->slices[0]->isIRAP() && pCfg->getForceIntraQP() ) || !pCfg->getLCULevelRC() )
      {
        estQP = pcSlice->getSliceQp();
      }
      else
      {
        bpp = pRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->isIRAP());
        if ( pcPic->slices[0]->isIntra())
        {
          estLambda = pRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
        }
        else
        {
          estLambda = pRateCtrl->getRCPic()->getLCUEstLambda( bpp );
          estQP     = pRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );
        }

        estQP     = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP );

        pRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths());
#if WCG_EXT
        pRdCost->saveUnadjustedLambda();
#endif
        for (uint32_t compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++)
        {
          const ComponentID compID = ComponentID(compIdx);
          int chromaQPOffset = pcSlice->getPPS()->getQpOffset(compID) + pcSlice->getSliceChromaQpDelta(compID);
          int qpc = pcSlice->getSPS()->getMappedChromaQpValue(compID, estQP) + chromaQPOffset;
          double tmpWeight = pow(2.0, (estQP - qpc) / 3.0);  // takes into account of the chroma qp mapping and chroma qp Offset
          if (m_pcCfg->getDepQuantEnabledFlag())
          {
            tmpWeight *= (m_pcCfg->getGOPSize() >= 8 ? pow(2.0, 0.1 / 3.0) : pow(2.0, 0.2 / 3.0));  // increase chroma weight for dependent quantization (in order to reduce bit rate shift from chroma to luma)
          }
          m_pcRdCost->setDistortionWeight(compID, tmpWeight);
        }
#if RDOQ_CHROMA_LAMBDA
        const double lambdaArray[MAX_NUM_COMPONENT] = {estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Y),
                                                       estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cb),
                                                       estLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cr)};
        pTrQuant->setLambdas( lambdaArray );
#else
        pTrQuant->setLambda( estLambda );
#endif
      }

      pRateCtrl->setRCQP( estQP );
    }
#if ENABLE_QPA
    else if (pCfg->getUsePerceptQPA() && pcSlice->getPPS()->getUseDQP())
    {
#if ENABLE_QPA_SUB_CTU
      const int adaptedQP    = applyQPAdaptationSubCtu (cs, ctuArea, ctuRsAddr, m_pcCfg->getLumaLevelToDeltaQPMapping().mode == LUMALVL_TO_DQP_NUM_MODES);
#else
      const int adaptedQP    = pcPic->m_iOffsetCtu[ctuRsAddr];
#endif
      const double newLambda = pcSlice->getLambdas()[0] * pow (2.0, double (adaptedQP - iQPIndex) / 3.0);
      pcPic->m_uEnerHpCtu[ctuRsAddr] = newLambda; // for ALF and SAO
#if !ENABLE_QPA_SUB_CTU
#if RDOQ_CHROMA_LAMBDA
      pTrQuant->getLambdas (oldLambdaArray); // save the old lambdas
      const double lambdaArray[MAX_NUM_COMPONENT] = {newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Y),
                                                     newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cb),
                                                     newLambda / m_pcRdCost->getDistortionWeight (COMPONENT_Cr)};
      pTrQuant->setLambdas (lambdaArray);
#else
      pTrQuant->setLambda (newLambda);
#endif
      pRdCost->setLambda (newLambda, pcSlice->getSPS()->getBitDepths());
#endif
      currQP[0] = currQP[1] = adaptedQP;
    }
#endif

第一个if语句:ratecontrol  默认是false,先跳过

else if语句:关于QPA,也是默认false,跳过

 bool updateBcwCodingOrder = cs.slice->getSliceType() == B_SLICE && ctuIdx == 0;
    if( updateBcwCodingOrder )
    {
      resetBcwCodingOrder(false, cs);
      m_pcInterSearch->initWeightIdxBits();
    }
    if (pcSlice->getSPS()->getUseLmcs())
    {
      m_pcCuEncoder->setDecCuReshaperInEncCU(m_pcLib->getReshaper(), pcSlice->getSPS()->getChromaFormatIdc());
    }
    if( !cs.slice->isIntra() && pCfg->getMCTSEncConstraint() )
    {
      pcPic->mctsInfo.init( &cs, ctuRsAddr );
    }

    if (pCfg->getSwitchPOC() != pcPic->poc || ctuRsAddr >= pCfg->getDebugCTU())
    {
      m_pcCuEncoder->compressCtu(cs, ctuArea, ctuRsAddr, prevQP, currQP);
    }

updateBcwCodingOrder:如果当前为B 帧且是slice里第一个CTU,则开启BCW

if语句(BCW为true):没仔细看,应该是重置BCW且根据序号初始化加权系数

setDecCuReshaperInEncCU():因为与lmcs有关,推测与对亮度色度分量的形状改变有关,之后再仔细看

最后一个if语句:如果poc和CTU未出现错误,则进入compressCtu()函数

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值