HM代码-码控(3)-compressslice()中的RC

大神传送门
https://blog.csdn.net/cpp12341234/article/details/45867631
https://blog.csdn.net/HEVC_CJL/article/details/11128745

//compressslice()中的关于RC的部分
    Double oldLambda = m_pcRdCost->getLambda();
    if ( m_pcCfg->getUseRateCtrl() )
    {
      Int estQP        = pcSlice->getSliceQp();//定义各种变量并且初始化
      Double estLambda = -1.0;
      Double bpp       = -1.0;

      if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )
      {//!< 如果当前slice为I slice或者不进行LCU level RC,则LCU直接使用当前slice的QP

        estQP = pcSlice->getSliceQp();
      }
      else
      {
        bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());//获得当前的LCU的目标Bpp
        if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)//若为I帧
        {
          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
		  //根据Bpp、slice的QP和估计QP来对lambda进行估计
        }
        else//若不是I帧
        {
          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );//根据Bpp对lambda进行估计
          estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );
		  //然后根据估计lambda和slice的QP对LCU的QP进行估计
        }

        estQP     = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP );//最终对QP进行确定

        m_pcRdCost->setLambda(estLambda);//对估计的lambda进行设置

#if RDOQ_CHROMA_LAMBDA
        // set lambda for RDOQ
        const Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight();
        const Double lambdaArray[MAX_NUM_COMPONENT] = { estLambda, chromaLambda, chromaLambda };
        m_pcTrQuant->setLambdas( lambdaArray );
#else
        m_pcTrQuant->setLambda( estLambda );
#endif
      }

      m_pcRateCtrl->setRCQP( estQP );
#if ADAPTIVE_QP_SELECTION
      pcCU->getSlice()->setSliceQpBase( estQP );//对编码时使用的QP进行设置
#endif
    }

在这里插入图片描述
①其中调用了 bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());//获得当前的LCU的目标Bpp

Double TEncRCPic::getLCUTargetBpp(SliceType eSliceType)
{
  Int   LCUIdx    = getLCUCoded();
  // Int  getLCUCoded() { return m_numberOfLCU - m_LCULeft; }返回已经编码的LCU的数目
  Double bpp      = -1.0;//定义变量并且初始化
  Int avgBits     = 0;

  if (eSliceType == I_SLICE)//如果当前帧为I帧
  {
    Int noOfLCUsLeft = m_numberOfLCU - LCUIdx + 1;//剩余LCU的个数=LCU的总个数-已经编码的LCU的个数+1
    Int bitrateWindow = min(4,noOfLCUsLeft);//比特率窗,最小为4,不能小于GOP的size
    Double MAD      = getLCU(LCUIdx).m_costIntra;
	//  TRCLCU& getLCU( Int LCUIdx ){ return m_LCUs[LCUIdx]; }返回TRCLCU类型的结构体,具体见上图,编号为LCUIdx的LCU块的比重值为MAD

    if (m_remainingCostIntra > 0.1 )//如果剩余帧内LCU的比重>0.1
    {
      Double weightedBitsLeft = (m_bitsLeft*bitrateWindow+(m_bitsLeft-getLCU(LCUIdx).m_targetBitsLeft)*noOfLCUsLeft)/(Double)bitrateWindow;
	  //剩余比特
	  //*******解释见下方******
      avgBits = Int( MAD*weightedBitsLeft/m_remainingCostIntra );
    }
    else//如果剩余帧内消耗比重<=0.1
    {
      avgBits = Int( m_bitsLeft / m_LCULeft );//平均每个LCU的比特数=剩余比特/剩余LCU个数
    }
    m_remainingCostIntra -= MAD;//m_remainingCostIntra =m_remainingCostIntra -MAD  更新剩余LCU所占的比重
  }
  else//如果当前帧不为I帧
  {
    Double totalWeight = 0;
    for ( Int i=LCUIdx; i<m_numberOfLCU; i++ )//从当前的LCU向后进行遍历
    {
      totalWeight += m_LCUs[i].m_bitWeight;//计算出包括当前LCU在内的剩余LCU所需要的比特和
    }
    Int realInfluenceLCU = min( g_RCLCUSmoothWindowSize, getLCULeft() );//真正有用的(影响大的)LCU数目=平滑窗口和剩余LCU数目的最小值
    avgBits = (Int)( m_LCUs[LCUIdx].m_bitWeight - ( totalWeight - m_bitsLeft ) / realInfluenceLCU + 0.5 );
	//平均每个LCU的比特数=当前LCU的需要比特-(包括当前LCU在内的剩余LCU所需要的比特和-实际剩余的比特)/有用的LCU数目+0.5
  }

  if ( avgBits < 1 )//平均每个LCU至少分配1个比特
  {
    avgBits = 1;
  }

  bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel;//计算当前LCU的Bpp值 
  m_LCUs[ LCUIdx ].m_targetBits = avgBits;//平均比特即作为当前LCU的目标比特

  return bpp;//返回Bpp的值
}

在这里插入图片描述
②其中 estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP); //如果是I帧,根据Bpp、slice的QP和估计QP来对lambda进行估计

Double TEncRCPic::getLCUEstLambdaAndQP(Double bpp, Int clipPicQP, Int *estQP)
{
  Int   LCUIdx = getLCUCoded();//获得已经编码完的帧数

  Double   alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;//获得alpha、beta
  Double   beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;

  Double costPerPixel = getLCU(LCUIdx).m_costIntra/(Double)getLCU(LCUIdx).m_numberOfPixel;//每个像素在帧内的比重=当前LCU在帧内的比重/总像素数
  costPerPixel = pow(costPerPixel, BETA1);//对比重进行修正
  Double estLambda = calculateLambdaIntra(alpha, beta, costPerPixel, bpp);//通过计算帧内lambda的函数求出估计lambda
  //Double TEncRCPic::calculateLambdaIntra(Double alpha, Double beta, Double MADPerPixel, Double bitsPerPixel)
//{
 // return ( (alpha/256.0) * pow( MADPerPixel/bitsPerPixel, beta ) );
//}

  Int clipNeighbourQP = g_RCInvalidQPValue;//const Int g_RCInvalidQPValue = -999;定义变量存储邻域QP
  for (Int i=LCUIdx-1; i>=0; i--)
  {
    if ((getLCU(i)).m_QP > g_RCInvalidQPValue)//从当前LCU向前遍历,若QP为有效值
    {
      clipNeighbourQP = getLCU(i).m_QP;//将该邻域QP赋给变量并跳出循环
      break;
    }
  }

  Int minQP = clipPicQP - 2;//clipPicQP为slice的QP值
  Int maxQP = clipPicQP + 2;

  if ( clipNeighbourQP > g_RCInvalidQPValue )//若邻域QP有效,对Max和minQP进行修正
  {
    maxQP = min(clipNeighbourQP + 1, maxQP);
    minQP = max(clipNeighbourQP - 1, minQP);
  }

  Double maxLambda=exp(((Double)(maxQP+0.49)-13.7122)/4.2005);//通过maxQP和minQP分别计算Maxlambda和minlambda
  Double minLambda=exp(((Double)(minQP-0.49)-13.7122)/4.2005);

  estLambda = Clip3(minLambda, maxLambda, estLambda);//选出最适合的作为当前LCU的估计lambda

  *estQP = Int( 4.2005 * log(estLambda) + 13.7122 + 0.5 );//根据得到的估计lambda对传进来的估计QP进行修正
  *estQP = Clip3(minQP, maxQP, *estQP);

  return estLambda;//返回估计的lambda值
}

③estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );//如果不是I帧,根据Bpp对lambda进行估计

Double TEncRCPic::getLCUEstLambda( Double bpp )
{
  Int   LCUIdx = getLCUCoded();//获得已经编码的帧数
  Double alpha;//新建变量
  Double beta;
  if ( m_encRCSeq->getUseLCUSeparateModel() )//如果每个LCU的alpha和beta互相独立
  {
    alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;//获得当前LCU的alpha和beta参数
    beta  = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;
  }
  else
  {
    alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;//否则使用图片级的alpha和beta参数
    beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
  }

  Double estLambda = alpha * pow( bpp, beta );//新建估计lambda变量,并且计算赋值
  //for Lambda clip, picture level clip
  Double clipPicLambda = m_estPicLambda;//得到图片的估计lambda

  //for Lambda clip, LCU level clip
  Double clipNeighbourLambda = -1.0;//新建邻域lambda变量,并初始化
  for ( Int i=LCUIdx - 1; i>=0; i-- )//从当前LCU向前进行遍历
  {
    if ( m_LCUs[i].m_lambda > 0 )//若lambda>0,则有效lambda的值就赋给邻域lambda变量
    {
      clipNeighbourLambda = m_LCUs[i].m_lambda;
      break;
    }
  }

  if ( clipNeighbourLambda > 0.0 )//若邻域lambda>0
  {
    estLambda = Clip3( clipNeighbourLambda * pow( 2.0, -1.0/3.0 ), clipNeighbourLambda * pow( 2.0, 1.0/3.0 ), estLambda );//对估计lambda进行修正
  }

  if ( clipPicLambda > 0.0 )//若图片层估计lambda>0
  {
    estLambda = Clip3( clipPicLambda * pow( 2.0, -2.0/3.0 ), clipPicLambda * pow( 2.0, 2.0/3.0 ), estLambda );//对估计lambda进行修正
  }
  else
  {
    estLambda = Clip3( 10.0, 1000.0, estLambda );//否则用常数进行修正
  }

  if ( estLambda < 0.1 )//估计的LCUlambda至少为0.1
  {
    estLambda = 0.1;
  }

  return estLambda;//返回估计的lambda
}

④ estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
//若不是I帧,根据估计lambda和slice的QP对LCU的QP进行估计

Int TEncRCPic::getLCUEstQP( Double lambda, Int clipPicQP )
{
  Int LCUIdx = getLCUCoded();//获得已经编码的帧数
  Int estQP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );//根据传入的LCU的估计lambda对QP进行估计

  //for Lambda clip, LCU level clip
  Int clipNeighbourQP = g_RCInvalidQPValue;
  //const Int g_RCInvalidQPValue = -999;新建邻域QP变量并且初始化
  for ( Int i=LCUIdx - 1; i>=0; i-- )//从当前LCU向前遍历
  {
    if ( (getLCU(i)).m_QP > g_RCInvalidQPValue )
    {
      clipNeighbourQP = getLCU(i).m_QP;//若为有效的QP则赋给邻域QP
      break;
    }
  }

  if ( clipNeighbourQP > g_RCInvalidQPValue )//若邻域QP有效
  {
    estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP );//对预测QP进行修正
  }

  estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP );//根据图片层的QP再次进行修正

  return estQP;//返回预测的QP值
}

本博文仅作小白学习记录使用,所有文章出处均在开头链接处可见,所有疑惑点均作了标注。欢迎各位大佬交流批评指正,侵删。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值