H.266/VVC代码学习6:VTM4.0中lambda与QP的关系

lambda是率失真优化中的一个重要参数,其取值与量化参数QP有比较固定的函数关系。接下来将深入VTM4.0代码中探究lambda与QP的关系。

首先在cfg中可以得到QP值,本文档视为亮度QP,记作QP(Y);在cfg中还可以得到CbQpOffset和 CrQpOffset,供后续计算色度QP,记作QP( C);亮度lambda和色度lambda由以上两种QP算得,记作lambda(Y)和lambda( C)。

一、设置亮度lambda(Y)

亮度lambda的计算公式为:

lambda(Y) = 0.57 * 2^(x/3)

其中

x = QP(Y)+ (bitdepth - 8) * 6 – 12

即比特深度为10时x等于QP(Y)值。如果在ME过程中使用hadamard变换,应在原基础上乘以0.95作为lambda(Y)最终值。

lambda(Y)= 0.57 * 2^(x/3) * 0.95

亮度部分在代码中的对应函数为calculateLambda。

#if SHARP_LUMA_DELTA_QP
double EncSlice::calculateLambda( const Slice*     slice,
                                  const int        GOPid, // entry in the GOP table
                                  const int        depth, // slice GOP hierarchical depth.
                                  const double     refQP, // initial slice-level QP			初始的slice级QP
                                  const double     dQP,   // initial double-precision QP	初始的双精度QP
                                        int       &iQP )  // returned integer QP.
{
  enum   SliceType eSliceType    = slice->getSliceType();
  const  bool      isField       = slice->getPic()->fieldPic;
  const  int       NumberBFrames = ( m_pcCfg->getGOPSize() - 1 );
  const  int       SHIFT_QP      = 12;
#if X0038_LAMBDA_FROM_QP_CAPABILITY
  const int temporalId=m_pcCfg->getGOPEntry(GOPid).m_temporalId;//CFG里的
  const std::vector<double> &intraLambdaModifiers=m_pcCfg->getIntraLambdaModifier();//CFG里的
#endif

  int bitdepth_luma_qp_scale = 6
                               * (slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8
                                  - DISTORTION_PRECISION_ADJUSTMENT(slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA)));//亮度深度
  double qp_temp = dQP + bitdepth_luma_qp_scale - SHIFT_QP;//记作t,临时QP = 原QP+比特深度控制的QP-QP偏移(12)
  // Case #1: I or P-slices (key-frame)
  double dQPFactor = m_pcCfg->getGOPEntry(GOPid).m_QPFactor;//CFG里的

  /********如果是I帧*********/
  if /*true*/( eSliceType==I_SLICE )
  {
    if /*false*/(m_pcCfg->getIntraQpFactor()>=0.0 && m_pcCfg->getGOPEntry(GOPid).m_sliceType != I_SLICE)
    {
      dQPFactor=m_pcCfg->getIntraQpFactor();
    }
    else/*true*/
    {
#if X0038_LAMBDA_FROM_QP_CAPABILITY
      if/*false*/ (m_pcCfg->getLambdaFromQPEnable())
      {
        dQPFactor=0.57;
      }
      else/*true*/
      {
#endif
        double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(double)(isField ? NumberBFrames/2 : NumberBFrames) );//记作a
        dQPFactor=0.57*dLambda_scale;//记作b,b=0.57*a
#if X0038_LAMBDA_FROM_QP_CAPABILITY
      }
#endif
    }
  }
#if X0038_LAMBDA_FROM_QP_CAPABILITY/****************如果CFG中使用了“从QP启用获取Lambda”***********/
  
  else if/*false*/( m_pcCfg->getLambdaFromQPEnable() )
  {
    dQPFactor=0.57;
  }
#endif

  double dLambda = dQPFactor*pow( 2.0, qp_temp/3.0 );//记作x,	x = b * 2^(t/3) = 0.57*a * 2^(t/3)

#if X0038_LAMBDA_FROM_QP_CAPABILITY  /************如果CFG中没有使用“从QP启用获取Lambda”***********/
  if/*false*/( !(m_pcCfg->getLambdaFromQPEnable()) && depth>0 )//当深度大于0时
#else
  if ( depth>0 )
#endif
  {
    double qp_temp_ref = refQP + bitdepth_luma_qp_scale - SHIFT_QP;
    dLambda *= Clip3(2.00, 4.00, (qp_temp_ref / 6.0));   // (j == B_SLICE && p_cur_frm->layer != 0 )
  }

  // if hadamard is used in ME process 如果在ME过程中使用hadamard变换
  if/*true*/ ( !m_pcCfg->getUseHADME() && slice->getSliceType( ) != I_SLICE )
  {
    dLambda *= 0.95;//x = 0.57*a * 2^(t/3) *0.95
  }

#if X0038_LAMBDA_FROM_QP_CAPABILITY
  double lambdaModifier;//记作m
  if/*true*/( eSliceType != I_SLICE || intraLambdaModifiers.empty())
  {
    lambdaModifier = m_pcCfg->getLambdaModifier( temporalId );
  }
  else/*false*/
  {
    lambdaModifier = intraLambdaModifiers[ (temporalId < intraLambdaModifiers.size()) ? temporalId : (intraLambdaModifiers.size()-1) ];
  }
  dLambda *= lambdaModifier;//x = 0.57*a * 2^(t/3) * 0.95 * m
#endif

  iQP = Clip3( -slice->getSPS()->getQpBDOffset( CHANNEL_TYPE_LUMA ), MAX_QP, (int) floor( dQP + 0.5 ) );

  if/*true*/( m_pcCfg->getDepQuantEnabledFlag() )
  {//依赖量化的轻微lambda调整(由于量化器的斜率不同) x = 0.57*a * 2^(t/3) * 0.95 * m * 2^(0.25/3)
    dLambda *= pow( 2.0, 0.25/3.0 ); // slight lambda adjustment for dependent quantization (due to different slope of quantizer)
  }

  // NOTE: the lambda modifiers that are sometimes applied later might be best always applied in here.
  return dLambda;//x = 0.57*a * 2^(t/3) * 0.95 * m * 2^(0.25/3)
}
#endif

二、设置色度lambda(C)

在cfg中读取CbQpOffset和 CrQpOffset,与QP(Y)值进行运算,可得到一个色度QP暂时值QP(t)

QP(t)=QP(Y)+CbQpOffset

QP(t)=QP(Y)+CrQpOffset

根据QP(t)值在ROM中寻得用于后续计算的色度QP真实值QP©,如下表所示。

色度QP暂时值QP(t) 色度QP真实值QP( C)
30≤QP(t)≤33 QP( C) = QP(t) - 1
35≤QP(t)≤36 QP( C) = QP(t) - 2
37≤QP(t)≤38 QP( C) = QP(t) - 3
39≤QP(t)≤40 QP( C) = QP(t) - 4
41≤QP(t)≤42 QP( C) = QP(t) – 5
43≤QP(t)≤69 QP( C)= QP(t) – 6

设一个临时失真权重w,与GOP相关。

w = 2 ^ [(QP(Y)- QP( C)+ 0.1)/3.0] GOPsize<8

w = 2 ^ [(QP(Y)- QP( C)+ 0.2)/3.0] GOPsize≥8

最终得出色度lambda值:

lambda ( C)=lambda(Y) / w

色度部分在代码中对应函数为setUpLambda。

void
EncSlice::setUpLambda( Slice* slice, const double dLambda, int iQP)
{
  // store lambda 保存lambda数值
  m_pcRdCost ->setLambda( dLambda, slice->getSPS()->getBitDepths() );

  // for RDO
  // in RdCost there is only one lambda because the luma and chroma bits are not separated, instead we weight the distortion of chroma.
  double dLambdas[MAX_NUM_COMPONENT] = { dLambda };
  for( uint32_t compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ )//遍历2个色度通道的lambda,计算得出色度lambda的值(亮度lambda/权重)
  {
    const ComponentID compID = ComponentID( compIdx );
    int chromaQPOffset       = slice->getPPS()->getQpOffset( compID ) + slice->getSliceChromaQpDelta( compID );
    int qpc                  = ( iQP + chromaQPOffset < 0 ) ? iQP : getScaledChromaQP( iQP + chromaQPOffset, m_pcCfg->getChromaFormatIdc() );
    double tmpWeight         = pow( 2.0, ( iQP - qpc ) / 3.0 );  // takes into account of the chroma qp mapping and chroma qp Offset 考虑色度qp映射和色度qp偏移
    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 ENABLE_WPP_PARALLELISM
    for( int jId = 1; jId < ( m_pcLib->getNumWppThreads() + m_pcLib->getNumWppExtraLines() ); jId++ )
    {
      m_pcLib->getRdCost( slice->getPic()->scheduler.getWppDataId( jId ) )->setDistortionWeight( compID, tmpWeight );
    }
#endif
    dLambdas[compIdx] = dLambda / tmpWeight;//赋值色度lambda
  }

#if RDOQ_CHROMA_LAMBDA
  // for RDOQ
  m_pcTrQuant->setLambdas( dLambdas );//赋值全部lambda
#else
  m_pcTrQuant->setLambda( dLambda );
#endif

  // for SAO
  slice->setLambdas( dLambdas );//赋值全部lambda
}

三、QP与lambda的关系

由以上关系,QP和lambda的关系整理可知:

lambda(Y)≈ 0.54 * 2^(QP(Y)/3)

lambda( C)≈ 0.53 * 2^(QP( C)/3)

初步得出结论,一般确定情况下,lambda仅与QP相关

你可能感兴趣的:(H.266/VVC视频编码)