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下載地址在這裡。
各位看官,如果您覺得本人的部落格對您有所幫助,可以掃描如下二維碼進行打賞,打賞多少您隨意~