H.266/VVC代码学习笔记10:VTM5.0中的xIntraCodingTUBlock()函数

xIntraCodingTUBlock()函数是帧内预测函数的一个关键函数,里面进行亮度预测模式的具体实现,以及亮度和色度的残差的变换以及量化,该函数是亮度预测和色度预测都要用的统一函数,今天抽空讲一下该函数的代码细节。

该函数的主要流程如下:
一、 初始化各种参数,定义一些变量。
二、 如果是亮度分量,则进行亮度分量的预测编码(MIp模式或者传统的角度预测)
三、 预测结束之后计算残差(非联合模式下,Cb和Cr分量预测块分别进行残差计算;若为联合模式,首先只对Cb分量预测块进行残差计算),这里残差的计算需要注意一下,首先定义一个piResi,将原始数据复制到piResi中,copyFrom( piOrg );以便后面减去预测值计算残差用。然后再减去预测值得到残差值,subtract(tmpPred)
四、 对残差进行变换和量化,大致步骤如下:

  1. 根据不同的颜色分量选择合适的QP和Lambda,
  2. 如果当前颜色分量的残差是单独编码,则对该残差调用scaleSignal()函数进行适当的微调;
  3. 针对联合色度模式,再为Cr分量预测块单独进行一些初始化的操作;
  4. 如果联合模式启用,首先获取Cr块的残差,然后用之前求好的Cb的残差减去Cr的残差求平均:jointResi = (cbResi - crResi)/2,此处的操作是通过下面的函数实现的,piResi.subtractAndHalve( crResi );这里有关联合色度残差编码模式的具体技术细节我早之前的博客讲过了,链接如下:H.266/VVC相关技术学习笔记:色度残差联合编码技术

五、 如果是联合模式,则为了确保联合模式能够保留单独模式在两个分量的色度块具有相同的残差,要将Lambda设置为更加宽松一些,就是设置Lambda更小一些;若不是联合模式,Lambda的值应该设置大一些
六、 调用m_pcTrQuant->transformNxN()函数对残差信号进行变换和量化,该函数是残差信号的变换以及量化的主函数入口。
七、 然后调用m_pcTrQuant->invTransformNxN()函数对变换量化编码之后的残差信号进行反量化反变换得到一个解码的残差。最后调用piReco.reconstruct()函数计算当前块的重建值,用于计算失真,在外层函数计算RDcost用。这里需要注意的是若为传统模式,则在外层有两次Cb和Cr的循环,计算各自分量块的重建值;若为联合模式,则在外层函数只对Cb预测块进行调用,piResi为联合残差,保存在Cb块内,只计算Cb块的重建值,Cr块的重建值需要在后面单独计算
八、 这里判断是否是是联合色度模式,如果是,则Cr预测块的解码残差和重建值需要单独计算,Cr的残差等于Cb残差的负值(crResi.copyAndNegate( piResi )),因为这里已经是联合色度模式了,所以piResi里存的就是联合残差。然后再调用crReco.reconstruct(crPred, crResi, cs.slice->clpRng( COMPONENT_Cr ))函数计算Cr块的重建值。
九、 最后更新当前预测块的色度分量的失真,用于外层计算RDcost。

以下是该函数的所有代码,重要的地方我都有详细的备注:

//这是帧内预测函数的一个关键函数,里面进行亮度预测模式的具体实现,以及亮度和色度的残差的变换以及量化。亮度预测和色度预测都要用的统一函数
void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &compID, const bool &checkCrossCPrediction, Distortion& ruiDist, const int &default0Save1Load2, uint32_t* numSig, std::vector<TrMode>* trModes, const bool loadTr)
{
  if (!tu.blocks[compID].valid())
  {
    return;
  }

  CodingStructure &cs                       = *tu.cs;
#if JVET_N0671_RDCOST_FIX
  m_pcRdCost->setChromaFormat(cs.sps->getChromaFormatIdc());
#endif

  const CompArea      &area                 = tu.blocks[compID];
  const SPS           &sps                  = *cs.sps;
  const PPS           &pps                  = *cs.pps;

  const ChannelType    chType               = toChannelType(compID);
  const int            bitDepth             = sps.getBitDepth(chType);

  PelBuf         piOrg                      = cs.getOrgBuf    (area);//图像原始数据
  PelBuf         piPred                     = cs.getPredBuf   (area);//图像预测数据
  PelBuf         piResi                     = cs.getResiBuf   (area);//图像残差数据
  PelBuf         piOrgResi                  = cs.getOrgResiBuf(area);//图像原始残差数据
  PelBuf         piReco                     = cs.getRecoBuf   (area);//图像重建数据

  const PredictionUnit &pu                  = *cs.getPU(area.pos(), chType);
  const uint32_t           uiChFinalMode        = PU::getFinalIntraMode(pu, chType);//最终选中的帧内预测模式

  const bool           bUseCrossCPrediction = pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && isChroma( compID ) && PU::isChromaIntraModeCrossCheckMode( pu ) && checkCrossCPrediction;
  const bool           ccUseRecoResi        = m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate();
#if INCLUDE_ISP_CFG_FLAG
  const bool           ispSplitIsAllowed    = sps.getUseISP() && CU::canUseISPSplit( *tu.cu, compID );
#else
  const bool           ispSplitIsAllowed    = CU::canUseISPSplit( *tu.cu, compID );
#endif


  //===== init availability pattern =====
#if JVET_N0054_JOINT_CHROMA
  //定义联合CbCr残差编码,1为启用联合编码,0为关闭
  bool jointCbCr = tu.jointCbCr && compID == COMPONENT_Cb;
  
  //各种亮度预测编码
  if ( compID == COMPONENT_Y )
  {
#endif
  PelBuf sharedPredTS( m_pSharedPredTransformSkip[compID], area );

  if( default0Save1Load2 != 2 )
  {
    initIntraPatternChType( *tu.cu, area );

    //===== get prediction signal =====
    //获取预测信号,得到对应色度候选模式的预测值
    if( compID != COMPONENT_Y && PU::isLMCMode( uiChFinalMode ) )
    {
      {
        xGetLumaRecPixels( pu, area );
      }
      predIntraChromaLM( compID, piPred, pu, area, uiChFinalMode );
    }
    else
    {
#if JVET_N0217_MATRIX_INTRAPRED
      if( PU::isMIP( pu, chType ) )
      {
        predIntraMip( compID, piPred, pu );
      }
      else
      {
#endif
      predIntraAng( compID, piPred, pu );
#if JVET_N0217_MATRIX_INTRAPRED
      }
#endif
    }


    // save prediction
    //保存预测块的信息,包括预测值
    if( default0Save1Load2 == 1 )
    {
      sharedPredTS.copyFrom( piPred );
    }
  }
  else
  {
    // load prediction
    piPred.copyFrom( sharedPredTS );
  }
#if JVET_N0054_JOINT_CHROMA
  }
#endif


  DTRACE( g_trace_ctx, D_PRED, "@(%4d,%4d) [%2dx%2d] IMode=%d\n", tu.lx(), tu.ly(), tu.lwidth(), tu.lheight(), uiChFinalMode );
  //DTRACE_PEL_BUF( D_PRED, piPred, tu, tu.cu->predMode, COMPONENT_Y );

  const Slice           &slice = *cs.slice;
  bool flag = slice.getReshapeInfo().getUseSliceReshaper() && (slice.isIntra() || (!slice.isIntra() && m_pcReshape->getCTUFlag()));
  
  //如果色度的Reshape被激活
  if (flag && slice.getReshapeInfo().getSliceReshapeChromaAdj() && isChroma(compID))
  {
    const Area area = tu.Y().valid() ? tu.Y() : Area(recalcPosition(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].pos()), recalcSize(tu.chromaFormat, tu.chType, CHANNEL_TYPE_LUMA, tu.blocks[tu.chType].size()));
    const CompArea &areaY = CompArea(COMPONENT_Y, tu.chromaFormat, area );
    PelBuf piPredY;
    piPredY = cs.picture->getPredBuf(areaY);
    const Pel avgLuma = piPredY.computeAvg();

    //定义adj是色度残差的规模
    int adj = m_pcReshape->calculateChromaAdj(avgLuma);
    tu.setChromaAdj(adj);
  }
//获取残差信号
  //===== get residual signal =====
  piResi.copyFrom( piOrg  );//首先将原始数据复制到piResi中,以便后面减去预测值计算残差用

  //如果是亮度TU
  if (slice.getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() && compID==COMPONENT_Y)
  {
    CompArea      tmpArea(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
    PelBuf tmpPred = m_tmpStorageLCU.getBuf(tmpArea);
    tmpPred.copyFrom(piPred);
    piResi.rspSignal(m_pcReshape->getFwdLUT());

    //减去预测值计算残差
    piResi.subtract(tmpPred);
  }
  else//如果是色度TU
  //减去预测值计算残差
  piResi.subtract( piPred );

  if (pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && isLuma(compID))
  {
    piOrgResi.copyFrom (piResi);//piOrgResi和piResi一样
  }

  if (bUseCrossCPrediction)
  {
    if (xCalcCrossComponentPredictionAlpha(tu, compID, ccUseRecoResi) == 0)
    {
      return;
    }
    CrossComponentPrediction::crossComponentPrediction(tu, compID, cs.getResiBuf(tu.Y()), piResi, piResi, false);
  }

  //对残差进行变换和量化
  //===== transform and quantization =====
  //为RDOQ初始化估计数组
  //--- init rate estimation arrays for RDOQ ---


  //--- transform and quantization           ---
  TCoeff uiAbsSum = 0;//变换量化后的残差信号的总和

  const QpParam cQP(tu, compID);//定义色度的量化参数

#if RDOQ_CHROMA_LAMBDA
  m_pcTrQuant->selectLambda(compID);//根据不同的颜色分量选择合适的Lambda
#endif

  flag =flag && (tu.blocks[compID].width*tu.blocks[compID].height > 4);
  if (flag && isChroma(compID) && slice.getReshapeInfo().getSliceReshapeChromaAdj() )
  {
    int cResScaleInv = tu.getChromaAdj();
    double cResScale = round((double)(1 << CSCALE_FP_PREC) / (double)cResScaleInv);
    m_pcTrQuant->setLambda(m_pcTrQuant->getLambda() / (cResScale*cResScale));
#if JVET_N0054_JOINT_CHROMA
    //如果联合色度关闭
    if ( !jointCbCr ) // Joint CbCr signal is to be scaled in the case of joint chroma
    //联合色度残差的情况下,CbCr的残差将会被缩减
#endif
    //缩减残差信号
    piResi.scaleSignal(cResScaleInv, 1, tu.cu->cs->slice->clpRng(compID));
  }

#if JVET_N0054_JOINT_CHROMA
  //定义Cr预测块的预测区域、原始Cr值、预测值、残差值、重建值
  const CompArea &crArea = tu.blocks     [ COMPONENT_Cr ];
  PelBuf          crOrg  = cs.getOrgBuf  ( crArea );
  PelBuf          crPred = cs.getPredBuf ( crArea );
  PelBuf          crResi = cs.getResiBuf ( crArea );
  PelBuf          crReco = cs.getRecoBuf ( crArea );
  //联合色度模式开启
  if ( jointCbCr )
  {
    // Get Cr prediction and residual、
    //获取Cr的预测值和残差
    crResi.copyFrom( crOrg  );
    crResi.subtract( crPred );
    
    // Create joint residual and store it for Cb component: jointResi = (cbResi - crResi)/2
    //定义CbCr联合残差并且将其存储到Cb分量的预测块中,jointResi = (cbResi - crResi)/2
    //残差相减并且求平均
    piResi.subtractAndHalve( crResi );
    
    // Scale the joint signal
    //缩放联合残差信号
    if ( flag && slice.getReshapeInfo().getSliceReshapeChromaAdj() )
      piResi.scaleSignal(tu.getChromaAdj(), 1, tu.cu->cs->slice->clpRng(compID));
    
    // Lambda is loosened for the joint mode with respect to single modes as the same residual is used for both chroma blocks
    //为了确保联合模式保留单独模式在两个分量的色度块具有相同的残差这样的特性,要将Lambda设置为更加宽松一些,就是设置Lambda更小一些
    m_pcTrQuant->setLambda( 0.60 * m_pcTrQuant->getLambda() );//计算setLambda的值并返回,该Lambda
  }

  else if ( isChroma(compID) && tu.cu->cs->slice->getSliceQp() > 18 )//如果不是联合模式,且是色度分量且QP>18
    m_pcTrQuant->setLambda( 1.10 * m_pcTrQuant->getLambda());//计算setLambda的值并返回,Lambda的值设置大一些
#endif
  
  double diagRatio = 0, horVerRatio = 0;

  if( trModes )//具体用什么变换模式
  {
    //残差信号的变换以及量化的主函数入口
    m_pcTrQuant->transformNxN( tu, compID, cQP, trModes, CU::isIntra( *tu.cu ) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand(), ispSplitIsAllowed ? &diagRatio : nullptr, ispSplitIsAllowed ? &horVerRatio : nullptr );
    tu.mtsIdx = trModes->at(0).first;
  }

  m_pcTrQuant->transformNxN( tu, compID, cQP, uiAbsSum, m_CABACEstimator->getCtx(), loadTr, &diagRatio, &horVerRatio );
#if INCLUDE_ISP_CFG_FLAG
    if ( !tu.cu->ispMode && isLuma(compID) && ispSplitIsAllowed && tu.mtsIdx == MTS_DCT2_DCT2 && ispSplitIsAllowed )
#else
  if ( !tu.cu->ispMode && isLuma(compID) && ispSplitIsAllowed && tu.mtsIdx == MTS_DCT2_DCT2 )
#endif
  {
    m_intraModeDiagRatio        .push_back(diagRatio);
    m_intraModeHorVerRatio      .push_back(horVerRatio);
    m_intraModeTestedNormalIntra.push_back((int)uiChFinalMode);
  }


  DTRACE( g_trace_ctx, D_TU_ABS_SUM, "%d: comp=%d, abssum=%d\n", DTRACE_GET_COUNTER( g_trace_ctx, D_TU_ABS_SUM ), compID, uiAbsSum );


  //--- inverse transform ---
  
  if (uiAbsSum > 0)//若果变换量化后的信号总和存在,则反量化反变换
  {
    //反量化反变换主函数
    m_pcTrQuant->invTransformNxN(tu, compID, piResi, cQP);
  }
  else//否则初始化为0
  {
    //初始化残差信号为0
    piResi.fill(0);
  }

  //===== reconstruction =====
  //在解码器端计算重建值
  if (flag && uiAbsSum > 0 && isChroma(compID) && slice.getReshapeInfo().getSliceReshapeChromaAdj() )
  {
    //对解码后的残差信号进行微调
    piResi.scaleSignal(tu.getChromaAdj(), 0, tu.cu->cs->slice->clpRng(compID));
  }
  if (bUseCrossCPrediction)
  {
    CrossComponentPrediction::crossComponentPrediction(tu, compID, cs.getResiBuf(tu.Y()), piResi, piResi, true);
  }

  if (slice.getReshapeInfo().getUseSliceReshaper() && m_pcReshape->getCTUFlag() && compID == COMPONENT_Y)
  {
    CompArea      tmpArea(COMPONENT_Y, area.chromaFormat, Position(0,0), area.size());
    PelBuf tmpPred = m_tmpStorageLCU.getBuf(tmpArea);
    tmpPred.copyFrom(piPred);//拿到预测信号,用于重建信号的计算

    //根据compID计算单个色度分量的重建图像数据(若为传统模式,则在外层有两次Cb和Cr的循环,计算各自分量的重建值;
    //                                       若为联合模式,则在外层函数只对Cb预测块进行调用,piResi为联合残差,保存在Cb块内,只计算Cb块的重建值)
    piReco.reconstruct(tmpPred, piResi, cs.slice->clpRng(compID));//残差+预测值
  }
  else
  piReco.reconstruct(piPred, piResi, cs.slice->clpRng( compID ));

#if JVET_N0054_JOINT_CHROMA
  if ( jointCbCr )//如果是联合模式,则Cr预测块的解码残差和重建值需要单独计算
  {
    // Cr uses negative of the signalled Cb residual
    
    if (uiAbsSum > 0)//Cr的残差等于Cb残差的负值,因为这里已经是联合色度模式了,所以piResi里存的就是联合残差
      crResi.copyAndNegate( piResi );
    else
      crResi.fill(0);
    
    tu.getCoeffs(COMPONENT_Cr).fill(0);
    
    // Set cbf also for Cr
    TU::setCbfAtDepth (tu, COMPONENT_Cr, tu.depth, uiAbsSum > 0 ? true : false);
    
    // Cr reconstruction and its contribution to the total error
    //计算Cr预测块的重建值
    crReco.reconstruct(crPred, crResi, cs.slice->clpRng( COMPONENT_Cr ));
    
#if WCG_EXT
    if ( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() ||
        (m_pcEncCfg->getReshaper()
         && slice.getReshapeInfo().getUseSliceReshaper()
         && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD()))))
    {
      const CPelBuf orgLuma = cs.getOrgBuf( cs.area.blocks[COMPONENT_Y] );
      ruiDist += m_pcRdCost->getDistPart( crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE_WTD, &orgLuma );
    }
    else
#endif
    {
      ruiDist += m_pcRdCost->getDistPart( crOrg, crReco, bitDepth, COMPONENT_Cr, DF_SSE );
    }
  }
#endif

  //===== update distortion =====
#if WCG_EXT
  if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (m_pcEncCfg->getReshaper()
    && slice.getReshapeInfo().getUseSliceReshaper() && (m_pcReshape->getCTUFlag() || (isChroma(compID) && m_pcEncCfg->getReshapeIntraCMD()))))
  {
    const CPelBuf orgLuma = cs.getOrgBuf( cs.area.blocks[COMPONENT_Y] );
    if (compID == COMPONENT_Y  && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()))
    {
      CompArea      tmpArea1(COMPONENT_Y, area.chromaFormat, Position(0, 0), area.size());
      PelBuf tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1);
      tmpRecLuma.copyFrom(piReco);
      tmpRecLuma.rspSignal(m_pcReshape->getInvLUT());
      ruiDist += m_pcRdCost->getDistPart(piOrg, tmpRecLuma, sps.getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
    }
    else
      ruiDist += m_pcRdCost->getDistPart(piOrg, piReco, bitDepth, compID, DF_SSE_WTD, &orgLuma);
  }
  else
#endif
  {
    ruiDist += m_pcRdCost->getDistPart( piOrg, piReco, bitDepth, compID, DF_SSE );
  }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值