HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析

在前两篇文章中,首先介绍了HEVC标准和编码流程,然后介绍了在HEVC中采用的全新的R-λ模型,本文将基于前面的内容和相应代码对码率控制算法进行详细的分析。

下面基于JCTVC-K0103提案详细介绍一下HEVC中基于R-λ模型的码率控制方法。同时基于HM-10对码率控制部分的代码做一个简要分析,相比于JM,HM中更多的使用了面向对象技术,结构更加清楚明了,码率控制相关代码的基本调用层次如下,纵向上即层层调用的关系,横向上是对几个比较重要的函数的内部调用情况列了出来。

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第1张图片

跟以前的方法类似,码率控制方法还是分为两大步骤:比特分配以及调整编码参数来达到目标码率,在第二步中才会用到R-λ模型。

下面先看比特分配。分为三个级别,GOP层、图片层和基本编码单元层。
首先计算每幅图片的目标比特数,f为帧率,Rtar为目标码率

设已编码图片的数量为Ncoded,这些图片用掉的比特数为Rcoded,当前GOP中的图片数量为NGOP ,SW是平滑比特分配的滑动窗口的大小,用于使得比特消耗变化和编码图片的质量更加平缓,在这里设为40,则GOP级别的比特分配

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第2张图片

我们希望能在SW帧之后达到目标码率,如果SW帧图片可以正好做到每一帧消耗TAvgPic 比特,则上式可以改写为


式子的第一部分代表目标码率,第二部分则代表buffer的状态
对应代码如下

main
TAppEncTop::encode
TEncTop::encode
TEncRateCtrl::initRCGOP
TEncRCGOP::create
TEncRCGOP:: xEstGOPTargetBits

事先定义有 const Int g_RCSmoothWindowSize = 40;
Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int GOPSize )
{
  Int realInfluencePicture = min( g_RCSmoothWindowSize, encRCSeq->getFramesLeft() );

  Int averageTargetBitsPerPic = (Int)( encRCSeq->getTargetBits() / encRCSeq->getTotalFrames() );
  Int currentTargetBitsPerPic = (Int)( ( encRCSeq->getBitsLeft() - averageTargetBitsPerPic * (encRCSeq->getFramesLeft() - realInfluencePicture) ) / realInfluencePicture );
  Int targetBits = currentTargetBitsPerPic * GOPSize;

  if ( targetBits < 200 )
  {
    targetBits = 200;   // at least allocate 200 bits for one GOP
  }

  return targetBits;
}

然后是 图片级别的比特分配
设当前GOP已经用掉的比特数为CodedGOP ,ω 是每幅图片的比特分配权重,则当前图片的目标比特率为

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRateCtrl::initRCPic
TEncRCPic::create
TEncRCPic::xEstPicTargetBits
Int TEncRCPic::xEstPicTargetBits( TEncRCSeq* encRCSeq, TEncRCGOP* encRCGOP )
{
  Int targetBits        = 0;
  Int GOPbitsLeft       = encRCGOP->getBitsLeft();

  Int i;
  Int currPicPosition = encRCGOP->getNumPic()-encRCGOP->getPicLeft();
  Int currPicRatio    = encRCSeq->getBitRatio( currPicPosition );
  Int totalPicRatio   = 0;
  for ( i=currPicPosition; i<encRCGOP->getNumPic(); i++ )
  {
    totalPicRatio += encRCSeq->getBitRatio( i );
  }

  targetBits  = Int( GOPbitsLeft * currPicRatio / totalPicRatio );

  if ( targetBits < 100 )
  {
    targetBits = 100;   // at least allocate 100 bits for one picture
  }

  if ( m_encRCSeq->getFramesLeft() > 16 )
  {
    targetBits = Int( g_RCWeightPicRargetBitInBuffer * targetBits + g_RCWeightPicTargetBitInGOP * m_encRCGOP->getTargetBitInGOP( currPicPosition ) );
  }

  return targetBits;
}

同时有
if ( targetBits < estHeaderBits + 100 )
  {
    targetBits = estHeaderBits + 100;   // at least allocate 100 bits for picture data
  }

上式可以根据不同图片的权重分配剩余的比特,ω 的值如下

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第3张图片

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第4张图片

在实际的应用中,所有图片均使用相同的ω是一种选择(即Equal allocation),这样设置会导致每幅图片消耗的比特差别不大。图片之间分级分配比特是另一种不错的选择(Hierarchical allocation),因为图片之间分级的分配比特可以对编码性能带来不小的提升。K0103的码率控制算法支持均匀分配比特和分级分配比特。

Main
TAppEncTop::encode
TAppEncTop::xCreateLib
TEncTop::create
TEncRateCtrl::init

Int* bitsRatio;
  bitsRatio = new Int[ GOPSize ];
  for ( Int i=0; i<GOPSize; i++ )
  {
    bitsRatio[i] = 10;
    if ( !GOPList[i].m_refPic )
    {
      bitsRatio[i] = 2;
    }
  }
if ( keepHierBits )
  {
    Double bpp = (Double)( targetBitrate / (Double)( frameRate*picWidth*picHeight ) );
    if ( GOPSize == 4 && isLowdelay )
    {
      if ( bpp > 0.2 )
      {
        bitsRatio[0] = 2;
        bitsRatio[1] = 3;
        bitsRatio[2] = 2;
        bitsRatio[3] = 6;
      }
      else if( bpp > 0.1 )
      {
        bitsRatio[0] = 2;
        bitsRatio[1] = 3;
        bitsRatio[2] = 2;
        bitsRatio[3] = 10;
      }
      else if ( bpp > 0.05 )
      {
        bitsRatio[0] = 2;
        bitsRatio[1] = 3;
        bitsRatio[2] = 2;
        bitsRatio[3] = 12;
      }
      else
      {
        bitsRatio[0] = 2;
        bitsRatio[1] = 3;
        bitsRatio[2] = 2;
        bitsRatio[3] = 14;
      }
    }
    else if ( GOPSize == 8 && !isLowdelay )
    {
      if ( bpp > 0.2 )
      {
        bitsRatio[0] = 15;
        bitsRatio[1] = 5;
        bitsRatio[2] = 4;
        bitsRatio[3] = 1;
        bitsRatio[4] = 1;
        bitsRatio[5] = 4;
        bitsRatio[6] = 1;
        bitsRatio[7] = 1;
      }
      else if ( bpp > 0.1 )
      {
        bitsRatio[0] = 20;
        bitsRatio[1] = 6;
        bitsRatio[2] = 4;
        bitsRatio[3] = 1;
        bitsRatio[4] = 1;
        bitsRatio[5] = 4;
        bitsRatio[6] = 1;
        bitsRatio[7] = 1;
      }
      else if ( bpp > 0.05 )
      {
        bitsRatio[0] = 25;
        bitsRatio[1] = 7;
        bitsRatio[2] = 4;
        bitsRatio[3] = 1;
        bitsRatio[4] = 1;
        bitsRatio[5] = 4;
        bitsRatio[6] = 1;
        bitsRatio[7] = 1;
      }
      else
      {
        bitsRatio[0] = 30;
        bitsRatio[1] = 8;
        bitsRatio[2] = 4;
        bitsRatio[3] = 1;
        bitsRatio[4] = 1;
        bitsRatio[5] = 4;
        bitsRatio[6] = 1;
        bitsRatio[7] = 1;
      }
    }
    else
    {
      printf( "\n hierarchical bit allocation is not support for the specified coding structure currently." );
    }
  }

对于帧内编码图像,当QP和λ未指定时,分配给帧内编码图像的比特数(TCurrPic)修正如下

需要注意的是,该修正值只在更新Rcoded 时使用(整个序列消耗的比特数),而不会用于更新CodedGOP (当前GOP消耗的比特数),这是因为帧内编码帧消耗的比特数往往很高,甚至高于给GOP分配的比特数,用未经修正的TCurrPic值更新CodedGOP

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRCSeq::getRefineBitsForIntra

Int TEncRCSeq::getRefineBitsForIntra( Int orgBits )
{
  Double bpp = ( (Double)orgBits ) / m_picHeight / m_picHeight;
  if ( bpp > 0.2 )
  {
    return orgBits * 5;
  }
  if ( bpp > 0.1 )
  {
    return orgBits * 7;
  }
  return orgBits * 10;
}

LCU层的比特分配
在该提案中认为一个基本单元包含一个LCU,其目标比特数由下式决定

Bitheader 是所有头信息比特数的估计值,由同一层之前的已编码图片的实际头信息比特数估计得来。

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRateCtrl::initRCPic
TEncRCPic::create(先xEstPicTargetBits再xEstPicHeaderBits)
TEncRCPic:: xEstPicHeaderBits

Int TEncRCPic::xEstPicHeaderBits( list<TEncRCPic*>& listPreviousPictures, Int frameLevel )
{
  Int numPreviousPics   = 0;
  Int totalPreviousBits = 0;

  list<TEncRCPic*>::iterator it;
  for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ )
  {
    if ( (*it)->getFrameLevel() == frameLevel )
    {
      totalPreviousBits += (*it)->getPicActualHeaderBits();
      numPreviousPics++;
    }
  }

  Int estHeaderBits = 0;
  if ( numPreviousPics > 0 )
  {
    estHeaderBits = totalPreviousBits / numPreviousPics;
  }

  return estHeaderBits;
}

ω 则是每个LCU的权重,根据根据之前编码的,在同一级别图片中处于同一位置的基本单元的预测误差(MAD)进行计算,如下

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第5张图片

以上就是比特分配的过程。

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncSlice::compressSlice
TEncRCPic::getLCUTargetBpp

Double TEncRCPic::getLCUTargetBpp()
{
  Int   LCUIdx    = getLCUCoded();
  Double bpp      = -1.0;
  Int avgBits     = 0;
  Double totalMAD = -1.0;
  Double MAD      = -1.0;

  if ( m_lastPicture == NULL )
  {
    avgBits = Int( m_bitsLeft / m_LCULeft );
  }
  else
  {
    MAD = m_lastPicture->getLCU(LCUIdx).m_MAD;
    totalMAD = m_lastPicture->getTotalMAD();
    for ( Int i=0; i<LCUIdx; i++ )
    {
      totalMAD -= m_lastPicture->getLCU(i).m_MAD;
    }

    if ( totalMAD > 0.1 )
    {
      avgBits = Int( m_bitsLeft * MAD / totalMAD );
    }
    else
    {
      avgBits = Int( m_bitsLeft / m_LCULeft );
    }
  }

#if L0033_RC_BUGFIX
  if ( avgBits < 1 )
  {
    avgBits = 1;
  }
#else
  if ( avgBits < 5 )
  {
    avgBits = 5;
  }
#endif

  bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel;
  m_LCUs[ LCUIdx ].m_targetBits = avgBits;

  return bpp;
}

然后是第二步,即如何达到分配的目标比特数
首先将前面的R-λ模型变为如下形式

使用上式依据一幅图片或者一个LCU的目标码率尺推导得到当前图片或者当前LCU编码所需要使用的λ。现在唯一的问题是,在不同编码序列的情况下,模型可能会拥有完全不相同的α和β值。此外,即使对于同一序列,处于不同级别的图片也可能拥有完全不相同的α和β值。例如,当GOP大小为4时,图片共分为三个级别,这三个级别的图片的α和β值可能是不同的。另外,不同的基本编码单元也可能拥有不同的α和β值,在此,我们假设在同一级别图片中对应位置的基本编码单元的α和β值相同。
需要注意的是α和β值的初始值设置并不是很严重的问题,因为在编码过程中,α和β值会根据序列逐渐更新,并最终适应序列特性。
设α和β值的初始值分别为3.2003 和-1.367。

Main
TAppEncTop::encode
TAppEncTop::xCreateLib
TEncTop::create
TEncRateCtrl::init
TEncRCSeq::initPicPara

Void TEncRCSeq::initPicPara( TRCParameter* picPara )
{
  assert( m_picPara != NULL );

  if ( picPara == NULL )
  {
    for ( Int i=0; i<m_numberOfLevel; i++ )
    {
      m_picPara[i].m_alpha = 3.2003;
      m_picPara[i].m_beta  = -1.367;
    }
  }
  else
  {
    for ( Int i=0; i<m_numberOfLevel; i++ )
    {
      m_picPara[i] = picPara[i];
    }
  }
}

以及
Main
TAppEncTop::encode
TAppEncTop::xCreateLib
TEncTop::create
TEncRateCtrl::init
TEncRCSeq::initLCUPara

Void TEncRCSeq::initLCUPara( TRCParameter** LCUPara )
{
  if ( m_LCUPara == NULL )
  {
    return;
  }
  if ( LCUPara == NULL )
  {
    for ( Int i=0; i<m_numberOfLevel; i++ )
    {
      for ( Int j=0; j<m_numberOfLCU; j++)
      {
        m_LCUPara[i][j].m_alpha = 3.2003;
        m_LCUPara[i][j].m_beta  = -1.367;
      }
    }
  }
  else
  {
    for ( Int i=0; i<m_numberOfLevel; i++ )
    {
      for ( Int j=0; j<m_numberOfLCU; j++)
      {
        m_LCUPara[i][j] = LCUPara[i][j];
      }
    }
  }
}


确定λ之后,使用下式得到QP值

 Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );


最后是参数更新步骤
在编码完一个LCU或者一幅图像之后,需要使用真正的bpp值(bppreal)和λ值(λreal)来更新α和β值。需要注意的是,在一幅图像中,每一个LCU都有他自己的λ值,而整幅图像的λ为所有LCU的λ的几何平均值

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRCPic::calAverageLambda

Double TEncRCPic::calAverageLambda()
{
  Double totalLambdas = 0.0;
  Int numTotalLCUs = 0;

  Int i;
  for ( i=0; i<m_numberOfLCU; i++ )
  {
    if ( m_LCUs[i].m_lambda > 0.01 )
    {
      totalLambdas += log( m_LCUs[i].m_lambda );
      numTotalLCUs++;
    }
  }

  Double avgLambda; 
  if( numTotalLCUs == 0 )
  {
    avgLambda = -1.0;
  }
  else
  {
    avgLambda = pow( 2.7183, totalLambdas / numTotalLCUs );
  }
  return avgLambda;
}

至于QP平均值的计算,就是用常见的算术平均值
main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRCPic:: calAverageQP

Double TEncRCPic::calAverageQP()
{
  Int totalQPs = 0;
  Int numTotalLCUs = 0;

  Int i;
  for ( i=0; i<m_numberOfLCU; i++ )
  {
    if ( m_LCUs[i].m_QP > 0 )
    {
      totalQPs += m_LCUs[i].m_QP;
      numTotalLCUs++;
    }
  }

  Double avgQP = 0.0;
  if ( numTotalLCUs == 0 )
  {
    avgQP = g_RCInvalidQPValue;
  }
  else
  {
    avgQP = ((Double)totalQPs) / ((Double)numTotalLCUs);
  }
  return avgQP;
}

更新过程按下式进行


Double estLambda = alpha * pow( bpp, beta );

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第6张图片

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRCPic::updateAfterLCU

Void TEncRCPic::updateAfterLCU( Int LCUIdx, Int bits, Int QP, Double lambda, Bool updateLCUParameter )
{
  m_LCUs[LCUIdx].m_actualBits = bits;
  m_LCUs[LCUIdx].m_QP         = QP;
  m_LCUs[LCUIdx].m_lambda     = lambda;

  m_LCULeft--;
  m_bitsLeft   -= bits;
  m_pixelsLeft -= m_LCUs[LCUIdx].m_numberOfPixel;

  if ( !updateLCUParameter )
  {
    return;
  }

  if ( !m_encRCSeq->getUseLCUSeparateModel() )
  {
    return;
  }

  Double alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;
  Double beta  = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;

  Int LCUActualBits   = m_LCUs[LCUIdx].m_actualBits;
  Int LCUTotalPixels  = m_LCUs[LCUIdx].m_numberOfPixel;
  Double bpp         = ( Double )LCUActualBits/( Double )LCUTotalPixels;
  Double calLambda   = alpha * pow( bpp, beta );
  Double inputLambda = m_LCUs[LCUIdx].m_lambda;

  if( inputLambda < 0.01 || calLambda < 0.01 || bpp < 0.0001 )
  {
    alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 );
    beta  *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 );

    alpha = Clip3( 0.05, 20.0, alpha );
    beta  = Clip3( -3.0, -0.1, beta  );

    TRCParameter rcPara;
    rcPara.m_alpha = alpha;
    rcPara.m_beta  = beta;
    m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara );

    return;
  }

  calLambda = Clip3( inputLambda / 10.0, inputLambda * 10.0, calLambda );
  alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha;
  double lnbpp = log( bpp );
  lnbpp = Clip3( -5.0, 1.0, lnbpp );
  beta  += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp;

  alpha = Clip3( 0.05, 20.0, alpha );
  beta  = Clip3( -3.0, -0.1, beta  );
  TRCParameter rcPara;
  rcPara.m_alpha = alpha;
  rcPara.m_beta  = beta;
  m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara );

}

以及
main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP
TEncRCPic:: updateAfterPicture

Void TEncRCPic::updateAfterPicture( Int actualHeaderBits, Int actualTotalBits, Double averageQP, Double averageLambda, Double effectivePercentage )
{
  m_picActualHeaderBits = actualHeaderBits;
  m_picActualBits       = actualTotalBits;
  if ( averageQP > 0.0 )
  {
    m_picQP             = Int( averageQP + 0.5 );
  }
  else
  {
    m_picQP             = g_RCInvalidQPValue;
  }
  m_picLambda           = averageLambda;
  for ( Int i=0; i<m_numberOfLCU; i++ )
  {
    m_totalMAD += m_LCUs[i].m_MAD;
  }

  Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;
  Double beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;

  // update parameters
  Double picActualBits = ( Double )m_picActualBits;
  Double picActualBpp  = picActualBits/(Double)m_numberOfPixel;
  Double calLambda     = alpha * pow( picActualBpp, beta );
  Double inputLambda   = m_picLambda;

  if ( inputLambda < 0.01 || calLambda < 0.01 || picActualBpp < 0.0001 || effectivePercentage < 0.05 )
  {
    alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 );
    beta  *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 );

    alpha = Clip3( 0.05, 20.0, alpha );
    beta  = Clip3( -3.0, -0.1, beta  );
    TRCParameter rcPara;
    rcPara.m_alpha = alpha;
    rcPara.m_beta  = beta;
    m_encRCSeq->setPicPara( m_frameLevel, rcPara );

    return;
  }

  calLambda = Clip3( inputLambda / 10.0, inputLambda * 10.0, calLambda );
  alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha;
  double lnbpp = log( picActualBpp );
  lnbpp = Clip3( -5.0, 1.0, lnbpp );
  beta  += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp;

  alpha = Clip3( 0.05, 20.0, alpha );
  beta  = Clip3( -3.0, -0.1, beta  );

  TRCParameter rcPara;
  rcPara.m_alpha = alpha;
  rcPara.m_beta  = beta;

  m_encRCSeq->setPicPara( m_frameLevel, rcPara );
}

δα 和δβ 设为0.1 和 0.05

 Main
TAppEncTop::encode
TAppEncTop::xCreateLib
TEncTop::create
TEncRateCtrl::init
TEncRCSeq::create

  m_numberOfPixel   = m_picWidth * m_picHeight;
  m_targetBits      = (Int64)m_totalFrames * (Int64)m_targetRate / (Int64)m_frameRate;
  m_seqTargetBpp = (Double)m_targetRate / (Double)m_frameRate / (Double)m_numberOfPixel;
  if ( m_seqTargetBpp < 0.03 )
  {
    m_alphaUpdate = 0.01;
    m_betaUpdate  = 0.005;
  }
  else if ( m_seqTargetBpp < 0.08 )
  {
    m_alphaUpdate = 0.05;
    m_betaUpdate  = 0.025;
  }
  else
  {
    m_alphaUpdate = 0.1;
    m_betaUpdate  = 0.05;
  }

此外,在某些时候(如LCU使用了skip模式,或者一幅图片中有大量的skip模式的LCU)可能出现bpp过小的情况,此时用下式进行更新


当然,α和β也是有范围限定的。α 的值限定在 [0.05, 20] 而 β 的值限定在 [−3.0, −0.1].

  alpha = Clip3( 0.05, 20.0, alpha );
  beta  = Clip3( -3.0, -0.1, beta  );

当然,λ和QP值将会被限定在一个范围内
在图像层,有
HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第7张图片

main
TAppEncTop::encode
TEncTop::encode(先initRCGOP再compressGOP)
TEncGOP::compressGOP

Double TEncRCPic::estimatePicLambda( list<TEncRCPic*>& listPreviousPictures )
{
  Double alpha         = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;
  Double beta          = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
  Double bpp       = (Double)m_targetBits/(Double)m_numberOfPixel;
  Double estLambda = alpha * pow( bpp, beta );
  Double lastLevelLambda = -1.0;
  Double lastPicLambda   = -1.0;
  Double lastValidLambda = -1.0;
  list<TEncRCPic*>::iterator it;
  for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ )
  {
    if ( (*it)->getFrameLevel() == m_frameLevel )
    {
      lastLevelLambda = (*it)->getPicActualLambda();
    }
    lastPicLambda     = (*it)->getPicActualLambda();

    if ( lastPicLambda > 0.0 )
    {
      lastValidLambda = lastPicLambda;
    }
  }

  if ( lastLevelLambda > 0.0 )
  {
    lastLevelLambda = Clip3( 0.1, 10000.0, lastLevelLambda );
    estLambda = Clip3( lastLevelLambda * pow( 2.0, -3.0/3.0 ), lastLevelLambda * pow( 2.0, 3.0/3.0 ), estLambda );
  }

  if ( lastPicLambda > 0.0 )
  {
    lastPicLambda = Clip3( 0.1, 2000.0, lastPicLambda );
    estLambda = Clip3( lastPicLambda * pow( 2.0, -10.0/3.0 ), lastPicLambda * pow( 2.0, 10.0/3.0 ), estLambda );
  }
  else if ( lastValidLambda > 0.0 )
  {
    lastValidLambda = Clip3( 0.1, 2000.0, lastValidLambda );
    estLambda = Clip3( lastValidLambda * pow(2.0, -10.0/3.0), lastValidLambda * pow(2.0, 10.0/3.0), estLambda );
  }
  else
  {
    estLambda = Clip3( 0.1, 10000.0, estLambda );
  }

  if ( estLambda < 0.1 )
  {
    estLambda = 0.1;
  }

  m_estPicLambda = estLambda;
  return estLambda;
}

Int TEncRCPic::estimatePicQP( Double lambda, list<TEncRCPic*>& listPreviousPictures )
{
  Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 ); 

  Int lastLevelQP = g_RCInvalidQPValue;
  Int lastPicQP   = g_RCInvalidQPValue;
  Int lastValidQP = g_RCInvalidQPValue;
  list<TEncRCPic*>::iterator it;
  for ( it = listPreviousPictures.begin(); it != listPreviousPictures.end(); it++ )
  {
    if ( (*it)->getFrameLevel() == m_frameLevel )
    {
      lastLevelQP = (*it)->getPicActualQP();
    }
    lastPicQP = (*it)->getPicActualQP();
    if ( lastPicQP > g_RCInvalidQPValue )
    {
      lastValidQP = lastPicQP;
    }
  }

  if ( lastLevelQP > g_RCInvalidQPValue )
  {
    QP = Clip3( lastLevelQP - 3, lastLevelQP + 3, QP );
  }

  if( lastPicQP > g_RCInvalidQPValue )
  {
    QP = Clip3( lastPicQP - 10, lastPicQP + 10, QP );
  }
  else if( lastValidQP > g_RCInvalidQPValue )
  {
    QP = Clip3( lastValidQP - 10, lastValidQP + 10, QP );
  }

  return QP;
}


在LCU层有

HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析_第8张图片

Double TEncRCPic::getLCUEstLambda( Double bpp )
{
  Int   LCUIdx = getLCUCoded();
  Double alpha;
  Double beta;
  if ( m_encRCSeq->getUseLCUSeparateModel() )
  {
    alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;
    beta  = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;
  }
  else
  {
    alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;
    beta  = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
  }

  Double estLambda = alpha * pow( bpp, beta );
  //for Lambda clip, picture level clip
  Double clipPicLambda = m_estPicLambda;

  //for Lambda clip, LCU level clip
  Double clipNeighbourLambda = -1.0;
  for ( int i=LCUIdx - 1; i>=0; i-- )
  {
    if ( m_LCUs[i].m_lambda > 0 )
    {
      clipNeighbourLambda = m_LCUs[i].m_lambda;
      break;
    }
  }

  if ( clipNeighbourLambda > 0.0 )
  {
    estLambda = Clip3( clipNeighbourLambda * pow( 2.0, -1.0/3.0 ), clipNeighbourLambda * pow( 2.0, 1.0/3.0 ), estLambda );
  }  

  if ( clipPicLambda > 0.0 )
  {
    estLambda = Clip3( clipPicLambda * pow( 2.0, -2.0/3.0 ), clipPicLambda * pow( 2.0, 2.0/3.0 ), estLambda );
  }
  else
  {
    estLambda = Clip3( 10.0, 1000.0, estLambda );
  }

  if ( estLambda < 0.1 )
  {
    estLambda = 0.1;
  }

  return estLambda;
}

Int TEncRCPic::getLCUEstQP( Double lambda, Int clipPicQP )
{
  Int LCUIdx = getLCUCoded();
  Int estQP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );

  //for Lambda clip, LCU level clip
  Int clipNeighbourQP = g_RCInvalidQPValue;
#if L0033_RC_BUGFIX
  for ( int i=LCUIdx - 1; i>=0; i-- )
#else
  for ( int i=LCUIdx; i>=0; i-- )
#endif
  {
    if ( (getLCU(i)).m_QP > g_RCInvalidQPValue )
    {
      clipNeighbourQP = getLCU(i).m_QP;
      break;
    }
  }

  if ( clipNeighbourQP > g_RCInvalidQPValue )
  {
    estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP );
  }

  estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP );

  return estQP;
}

以上就是本系列文章的所有内容,本系列文章完整版PDF下载地址 在这里。

你可能感兴趣的:(算法,编码,HEVC,码率控制)