H.266/VCコード学習6:VTM 4.0のlambdaとQPの関係


Lambdaはレート歪み最適化における重要なパラメータであり、その値は量子化パラメータQPと比較的固定的な関数関係を有する.次にVTM 4に深く入り込む.0コードでlambdaとQPの関係を探究する.
まずcfgでQP値を得ることができ、本書では輝度QPとしてQP(Y)と記す.cfgにおいて、CbQpOffsetおよびCrQpOffsetも得られ、その後、色度QPを計算し、QP(C)として記載することができる.輝度lambdaと色度lambdaは、以上の2つの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変換を使用する場合は、lambda(Y)の最終値として元のベースに0.95を乗じます.
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)値と演算して、1つの色度QP一時値QP(t)を得る
QP(t)=QP(Y)+CbQpOffset
または
QP(t)=QP(Y)+CrQpOffset
QP(t)の値に基づいて、後続の計算のための色度QPの真値QPをROMにおいて求める©,下表のとおりです.
色度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のみに関連すると初歩的に結論した.