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

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

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

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

下面先看比特分配。分为三个级别,GOP层、图片层和基本编码单元层。

首先计算每幅图片的目标比特数,f为帧率,Rtar为目标码率

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

我们希望能在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
  }

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

在实际的应用中,所有图片均使用相同的ω是一种选择(即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)进行计算,如下

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

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 );

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值将会被限定在一个范围内

在图像层,有

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层有

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下载地址在这里

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-08 01:16:24

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

kmp算法c++代码实现

1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 #define max 5000 5 6 int t[max];//目标串 7 int p[max];//模式串 8 int next[max];//前缀函数 9 int n,m;//n为目标串的数目,m为模式串的数目 10 void function_prefix(int *s,int *next) 11 { 12 next[1]=next[0]=0;

HEVC码率控制简介(R-lamda)

HEVC码率控制介绍 R-lamda模型提出到优化已有2年,从近几年的文章来看,大体归为类:一是帧内的码率的码率控制算法,一是模型参数更新,一是考虑感知 第一类:K0103码率控制算法主要是在P/B帧上提出的,I帧没有做,体现在I帧中LCU编码时用的还是帧层的QP,而帧层的QP是由配置文件(即*.cfg中QP: 32 # Quantization parameter(0-51)  这里的32我们可以更改一般设置为22.27.32.37),如果码率控制开启,实际上配置文件中#======== Qu

阅读笔记之《梦断代码》三

这本书比前两本都要长,但却是有一个主线“剧情”的.就是chandler团队的开发历程.书中的旁引例子也挺多,最大的例子还是这个“剧情”.OSAF团队在一开始被寄予厚望,但最后还是免不了遭受失败的命运.作者使用自己的经历现身说法,书中很多的道理我都曾在王老师的课上听过,很多经历也在我唯一的项目开发经历中遇到过,也算感同身受.王老师曾讲过,一个团队的第一个项目总是免不了失败的命运,我常常思考是为什么,读完这本书我突然醒悟,软件工程并不只是软件的工程,人类文明在软件文明之上,人类制作软件使用软件,软件

Python实现各种排序算法的代码示例总结

Python实现各种排序算法的代码示例总结 作者:Donald Knuth 字体:[增加 减小] 类型:转载 时间:2015-12-11我要评论 这篇文章主要介绍了Python实现各种排序算法的代码示例总结,其实Python是非常好的算法入门学习时的配套高级语言,需要的朋友可以参考下 在Python实践中,我们往往遇到排序问题,比如在对搜索结果打分的排序(没有排序就没有Google等搜索引擎的存在),当然,这样的例子数不胜数.<数据结构>也会花大量篇幅讲解排序.之前一段时间,由于需要,我复习了

每周一书-编写高质量代码:改善C程序代码的125个建议

首先说明,本周活动有效时间为2016年8月28日到2016年9月4日.本周为大家送出的书是由机械工业出版社出版,马伟编著的<编写高质量代码:改善C程序代码的125个建议>. 编辑推荐 10余年开发经验的资深C语言专家全面从C语法和C11标准两大方面深入探讨编写高质量C代码的技巧.禁忌和实践 C语言因为既具有高级语言特性,又具有汇编语言特性,所以它是近二十几年来使用较为广泛.生命力较强的编程语言.无论是操作系统.嵌入式系统.普通应用软件,还是移动智能设备开发,它都能够很好地胜任,是公认的强大的语

代码质量优先——《编写高质量代码:改善c程序代码的125个建议》

高质量的代码不但可以促进团队合作.减少bug处理.降低维护成本,对程序员自身的成长也是至关重要的.很难想象一个参考<如何编写无法维护的代码>写代码的程序员技术成长的上限有多么低.为了写出高质量的代码,我们需要听取过来人的改善代码质量的经验,<编写高质量代码:改善c程序代码的125个建议>就是一本能让人写出高质量代码的好书. 本书的第三章<程序控制语句应该保持简洁高效>首先用简练的语言介绍了流程控制结构的概念,然后提供了对if.else.for.do-while.swit

html 音乐 QQ播放器 外链 代码 播放器 外链 代码

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha QQ播放器 外链 代码 播放器 外链 代码 ======== 歌曲链接 QQ播放器 外链 代码 播放器 外链 代码 <EMBED src="音乐url" autostart="true" loop="true" width="80" height="20"> 在网易云音乐, 搜索一首

代码分析—“CA0052 没有选择要分析的目标”(VS2012)

情况: 1.未采用代码分析时程序正常编译 2.采用代码分析,会提示"没有选择分析目标"或"未加载制定版本的程序集"...的错误 分析: 是由于代码分析依赖程序集的强签名,包括版本 解决方案: 1.修改代码分析工具的配置项: FxCopCmd.exe.config里节点AssemblyReferenceResolveMode的Value值StrongName修改为StrongNameIgnoringVersion或None 2.修改当前分析的项目: .csproj增加

能盈利的代码,才是好代码——参股新三板

能盈利的代码,才是好代码. 代码 名称 参股公司 参股比例% 000008 神州高铁 拓控信息 100.00 002307 北新路桥 新疆鼎源   600064 南京高科 臣功制药   600282 南钢股份 钢宝股份   603598 引力传媒 九合互动   601901 方正证券 方正中期 90.62 002465 海格通信 摩诘创新 90.00 600096 云天化 纽米科技 88.67 000551 创元科技 苏州电瓷 86.42 000507 珠海港 港昇公司 85.35 600839