HEVC码率控制浅析——HM代码阅读之一

HM的码率控制提案主要参考如下三篇:K0103,M0036,M0257。本文及后续文章将基于HM12.0进行讨论,且首先仅讨论K0103对应的代码,之后再陆续补充M0036,M0257对应的代码分析,这么做可能会使得剧情不会显得那么地跳跃,分析起来能够更好地被接受。

 

按照我的个人习惯,还是先分析HM中码率控制部分(以后简称RC)的总体框架吧。

跟RC有关的头文件和源文件为工程TLibEncoder中的TEncRateCtrl.h和TEncRateCtrl.cpp,其余的地方都是调用这两个文件中定义的函数或者变量。

 

在最顶层,TEncTop这个类定义了成员变量TEncRateCtrl m_cRateCtrl,类TEncGOP,TEncSlice以及TEncCu也分别定义了成员变量TEncRateCtrl *m_pcRateCtrl,但是请注意,这三个类的m_pcRateCtrl实际上都是指向TEncTop这个类的m_cRateCtrl,即它们本质上是同一个。这个从这三个类的成员函数init中对各成员变量的初始化可以看出来。

 

(1)初始化:

首先,在TEncTop::create()中,对整个序列都要用到的相关参数进行初始化

#if RATE_CONTROL_LAMBDA_DOMAIN

  if ( m_RCEnableRateControl )

  {

    m_cRateCtrl.init( m_framesToBeEncoded, m_RCTargetBitrate, m_iFrameRate, m_iGOPSize, m_iSourceWidth, m_iSourceHeight,

                      g_uiMaxCUWidth, g_uiMaxCUHeight, m_RCKeepHierarchicalBit, m_RCUseLCUSeparateModel, m_GOPList );

  }

#else

  m_cRateCtrl.create(getIntraPeriod(), getGOPSize(), getFrameRate(), getTargetBitrate(), getQP(), getNumLCUInUnit(), getSourceWidth(), getSourceHeight(), g_uiMaxCUWidth, g_uiMaxCUHeight);

#endif


其次,在TEncTop::encode()中,对整个GOP需要用到的相关参数进行初始化

#if RATE_CONTROL_LAMBDA_DOMAIN

  if ( m_RCEnableRateControl )

  {

    m_cRateCtrl.initRCGOP( m_iNumPicRcvd );

  }

#endif

 

接着,在TEncGOP::compressGOP()中,对每一幅picture需要用到的相关参数进行初始化

#if RATE_CONTROL_LAMBDA_DOMAIN

    Double lambda            = 0.0;

    Int actualHeadBits       = 0;

    Int actualTotalBits      = 0;

    Int estimatedBits        = 0;

    Int tmpBitsBeforeWriting = 0;

    if ( m_pcCfg->getUseRateCtrl() )

    {

      Int frameLevel = m_pcRateCtrl->getRCSeq()->getGOPID2Level( iGOPid );

      if ( pcPic->getSlice(0)->getSliceType() == I_SLICE )

      {

        frameLevel = 0;

      }

      m_pcRateCtrl->initRCPic( frameLevel ); //!< picture level 初始化

      estimatedBits = m_pcRateCtrl->getRCPic()->getTargetBits();

      

      Int sliceQP = m_pcCfg->getInitialQP();

      if ( ( pcSlice->getPOC() == 0 && m_pcCfg->getInitialQP() > 0 ) || ( frameLevel == 0 && m_pcCfg->getForceIntraQP() ) ) // QP is specified

      {

        Int    NumberBFrames = ( m_pcCfg->getGOPSize() - 1 );

        Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)NumberBFrames );

        Double dQPFactor     = 0.57*dLambda_scale;

        Int    SHIFT_QP      = 12;

        Int    bitdepth_luma_qp_scale = 0;

        Double qp_temp = (Double) sliceQP + bitdepth_luma_qp_scale - SHIFT_QP;

        lambda = dQPFactor*pow( 2.0, qp_temp/3.0 );

      }

      else if ( frameLevel == 0 )   // intra case, but use the model

      {

#if RATE_CONTROL_INTRA

        m_pcSliceEncoder->calCostSliceI(pcPic);

#endif

        if ( m_pcCfg->getIntraPeriod() != 1 )   // do not refine allocated bits for all intra case

        {

          Int bits = m_pcRateCtrl->getRCSeq()->getLeftAverageBits();

#if RATE_CONTROL_INTRA

          bits = m_pcRateCtrl->getRCPic()->getRefineBitsForIntra( bits );

#else

          bits = m_pcRateCtrl->getRCSeq()->getRefineBitsForIntra( bits );

#endif

          if ( bits < 200 )

          {

            bits = 200;

          }

          m_pcRateCtrl->getRCPic()->setTargetBits( bits );

        }

        

        list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();

#if RATE_CONTROL_INTRA

        m_pcRateCtrl->getRCPic()->getLCUInitTargetBits();

        lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());

#else

        lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture );

#endif

        sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );

      }

      else    // normal case

      {

        list<TEncRCPic*> listPreviousPicture = m_pcRateCtrl->getPicList();

#if RATE_CONTROL_INTRA

        lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture, pcSlice->getSliceType());

#else

        lambda  = m_pcRateCtrl->getRCPic()->estimatePicLambda( listPreviousPicture );

#endif

        sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda, listPreviousPicture );

      }

      

      sliceQP = Clip3( -pcSlice->getSPS()->getQpBDOffsetY(), MAX_QP, sliceQP );

      m_pcRateCtrl->getRCPic()->setPicEstQP( sliceQP );

      

      m_pcSliceEncoder->resetQP( pcPic, sliceQP, lambda );

    }

#endif


最后,在TEncSlice::compressSlice()中,对每一个LCU需要用到的相关参数进行初始化:

#if RATE_CONTROL_LAMBDA_DOMAIN

      Double oldLambda = m_pcRdCost->getLambda();

      if ( m_pcCfg->getUseRateCtrl() )

      {

        Int estQP        = pcSlice->getSliceQp();

        Double estLambda = -1.0;

        Double bpp       = -1.0;



#if M0036_RC_IMPROVEMENT

        if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )

#else

        if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE || !m_pcCfg->getLCULevelRC() )

#endif

        {

          estQP = pcSlice->getSliceQp();

        }

        else

        {

#if RATE_CONTROL_INTRA

          bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());

          if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)

          {

            estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);

          }

          else

          {

            estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );

            estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );

          }

#else

          bpp       = m_pcRateCtrl->getRCPic()->getLCUTargetBpp();

          estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );

          estQP     = m_pcRateCtrl->getRCPic()->getLCUEstQP    ( estLambda, pcSlice->getSliceQp() );

#endif



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



          m_pcRdCost->setLambda(estLambda);

#if M0036_RC_IMPROVEMENT

#if RDOQ_CHROMA_LAMBDA

          // set lambda for RDOQ

          Double weight=m_pcRdCost->getChromaWeight();

          m_pcTrQuant->setLambda( estLambda, estLambda / weight );

#else

          m_pcTrQuant->setLambda( estLambda );

#endif

#endif

        }



        m_pcRateCtrl->setRCQP( estQP );

        pcCU->getSlice()->setSliceQpBase( estQP );

      }

#endif


(2)参数值更新:

首先,在TEncSlice::compressSlice中,每编码完一个LCU,进行一次更新:

#if TICKET_1090_FIX

#if RATE_CONTROL_LAMBDA_DOMAIN

      if ( m_pcCfg->getUseRateCtrl() )

      {

#if !M0036_RC_IMPROVEMENT

        UInt SAD    = m_pcCuEncoder->getLCUPredictionSAD();

        Int height  = min( pcSlice->getSPS()->getMaxCUHeight(),pcSlice->getSPS()->getPicHeightInLumaSamples() - uiCUAddr / rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUHeight() );

        Int width   = min( pcSlice->getSPS()->getMaxCUWidth(),pcSlice->getSPS()->getPicWidthInLumaSamples() - uiCUAddr % rpcPic->getFrameWidthInCU() * pcSlice->getSPS()->getMaxCUWidth() );

        Double MAD = (Double)SAD / (Double)(height * width);

        MAD = MAD * MAD;

        ( m_pcRateCtrl->getRCPic()->getLCU(uiCUAddr) ).m_MAD = MAD; //!< 注意:此处的MAD已经进行过K0103中公式的两个处理了。

#endif



        Int actualQP        = g_RCInvalidQPValue;

        Double actualLambda = m_pcRdCost->getLambda();

        Int actualBits      = pcCU->getTotalBits();

        Int numberOfEffectivePixels    = 0;

        for ( Int idx = 0; idx < rpcPic->getNumPartInCU(); idx++ )

        {

          if ( pcCU->getPredictionMode( idx ) != MODE_NONE && ( !pcCU->isSkipped( idx ) ) ) //!< 不考虑skip模式

          {

            numberOfEffectivePixels = numberOfEffectivePixels + 16;

            break;

          }

        }



        if ( numberOfEffectivePixels == 0 )

        {

          actualQP = g_RCInvalidQPValue;

        }

        else

        {

          actualQP = pcCU->getQP( 0 );

        }

        m_pcRdCost->setLambda(oldLambda);



#if RATE_CONTROL_INTRA

        m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, 

          pcCU->getSlice()->getSliceType() == I_SLICE ? 0 : m_pcCfg->getLCULevelRC() );

#else

        m_pcRateCtrl->getRCPic()->updateAfterLCU( m_pcRateCtrl->getRCPic()->getLCUCoded(), actualBits, actualQP, actualLambda, m_pcCfg->getLCULevelRC() );

#endif

      }

#endif

#endif


接着,在TEncGOP::compressGOP中,一个slice编码完后,进行一次更新:

#if RATE_CONTROL_LAMBDA_DOMAIN

    if ( m_pcCfg->getUseRateCtrl() )

    {

#if !M0036_RC_IMPROVEMENT

      Double effectivePercentage = m_pcRateCtrl->getRCPic()->getEffectivePercentage();

#endif

      Double avgQP     = m_pcRateCtrl->getRCPic()->calAverageQP(); //!< arithmetic mean value for QP

      Double avgLambda = m_pcRateCtrl->getRCPic()->calAverageLambda(); //!< geometric mean value for lamda 

      if ( avgLambda < 0.0 )

      {

        avgLambda = lambda;

      }

#if M0036_RC_IMPROVEMENT

#if RATE_CONTROL_INTRA

      m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, pcSlice->getSliceType());

#else

      m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda );

#endif

#else

      m_pcRateCtrl->getRCPic()->updateAfterPicture( actualHeadBits, actualTotalBits, avgQP, avgLambda, effectivePercentage );

#endif

      m_pcRateCtrl->getRCPic()->addToPictureLsit( m_pcRateCtrl->getPicList() );

      

      m_pcRateCtrl->getRCSeq()->updateAfterPic( actualTotalBits );

      if ( pcSlice->getSliceType() != I_SLICE )

      {

        m_pcRateCtrl->getRCGOP()->updateAfterPicture( actualTotalBits );

      }

      else    // for intra picture, the estimated bits are used to update the current status in the GOP

      {

        m_pcRateCtrl->getRCGOP()->updateAfterPicture( estimatedBits );

      }

    }

#else

    if(m_pcCfg->getUseRateCtrl())

    {

      UInt  frameBits = m_vRVM_RP[m_vRVM_RP.size()-1];

      m_pcRateCtrl->updataRCFrameStatus((Int)frameBits, pcSlice->getSliceType());

    }

#endif


最后,在TEncTop::encode()中,对当前GOP的码控变量进行了销毁。

 

至此,总体框架大体分析完毕,最后,简单地提一下TEncRateCtrl.h中定义的类的关系:TEncRateCtrl属于RC顶层的控制,负责整个RC的流程管理;TEncRCSeq负责序列级的RC管理;TEncRCGOP负责GOP级的RC管理;TEncRCPic负责picture级的RC管理;TRCLCU负责LCU级的RC管理。


 

你可能感兴趣的:(代码)