H.265/HEVC码率控制总体框架与代码解读(中)
2017-09-29 10:24
465 查看
上:介绍码率控制的初始化部分
中:介绍码率控制的具体实现
下:介绍码率控制的应用以及码率控制的一些研究方向
2.码率控制的比特分配部分
从初始化部分可以看出,码率控制从GOP,frame,LCU逐级分配比特数,在frame与LCU这两块中还要根据比特数计算出对应的QP,从而运用于实际编码之中。码率控制相较于整个HEVC框架而言是简单的,初始化的函数部分基本就占据了TEncRateCtrl.cpp的一半篇幅,剩下的部分大致分为三块:比特分配、QP计算以及数据更新。接下来就从这三块进行解读。
2.1比特分配
2.1.1
GOP比特分配
首先是GOP的比特分配,在1.2节中提到的xEstGOPTargetBits(
) 函数指明了对应的分配方法。代码如下
Int TEncRCGOP::xEstGOPTargetBits( TEncRCSeq* encRCSeq, Int GOPSize ) { Int realInfluencePicture = min( g_RCSmoothWindowSize, encRCSeq->getFramesLeft() ); //这里用到了滑动窗口g_RCSmoothWindowSize的概念,realInfluencePicture取滑动窗口40与编码剩余比特数的较小值,以精确计算GOP应得的targetBits,具体算法见JCTVC-0103中的(7),图附在代码块之后 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; }
2.1.2 frame的比特分配
根据2.1.1中GOP分配的比特数,可以得到GOP内各帧的比特数,代码如下
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 ); //对应1.1节的各帧权重 Int totalPicRatio = 0; for ( i=currPicPosition; i<encRCGOP->getNumPic(); i++ ) { totalPicRatio += encRCSeq->getBitRatio( i ); } targetBits = Int( ((Double)GOPbitsLeft) * currPicRatio / totalPicRatio ); //2.1.1中给GOP分配的比特数乘上当前帧所占的权重,得到对应比特数,即JCTVC-0103(9),图附在代码块之后 if ( targetBits < 100 ) { targetBits = 100; // at least allocate 100 bits for one picture } if ( m_encRCSeq->getFramesLeft() > 16 ) //对targetBits进行微调,对应的算法暂未找到 { targetBits = Int( g_RCWeightPicRargetBitInBuffer * targetBits + g_RCWeightPicTargetBitInGOP * m_encRCGOP->getTargetBitInGOP( currPicPosition ) ); } return targetBits; }
2.1.3 LCU的比特分配
得到各帧的比特数后,可以对帧内各个LCU进行比特分配,代码如下
Double TEncRCPic::getLCUTargetBpp(SliceType eSliceType) { Int LCUIdx = getLCUCoded();//当前LCU的序号 Double bpp = -1.0; Int avgBits = 0; if (eSliceType == I_SLICE) { Int noOfLCUsLeft = m_numberOfLCU - LCUIdx + 1; Int bitrateWindow = min(4,noOfLCUsLeft); //类似于之前的滑动窗口 Double MAD = getLCU(LCUIdx).m_costIntra; //计算出每个LCU对应的MAD值 if (m_remainingCostIntra > 0.1 ) //m_remainingCostIntra为当前帧的总MAD { Double weightedBitsLeft = (m_bitsLeft*bitrateWindow+(m_bitsLeft-getLCU(LCUIdx).m_targetBitsLeft)*noOfLCUsLeft)/(Double)bitrateWindow; avgBits = Int( MAD*weightedBitsLeft/m_remainingCostIntra ); } else { avgBits = Int( m_bitsLeft / m_LCULeft ); } m_remainingCostIntra -= MAD; //分配完一个LCU比特后,更新剩余的m_remainingCostIntra } else //非I帧 { Double totalWeight = 0; for ( Int i=LCUIdx; i<m_numberOfLCU; i++ ) { totalWeight += m_LCUs[i].m_bitWeight; } Int realInfluenceLCU = min( g_RCLCUSmoothWindowSize, getLCULeft() ); avgBits = (Int)( m_LCUs[LCUIdx].m_bitWeight - ( totalWeight - m_bitsLeft ) / realInfluenceLCU + 0.5 ); } if ( avgBits < 1 ) { avgBits = 1; } bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel; //求出的比特数转化为bpp(bits per pixel)计算 m_LCUs[ LCUIdx ].m_targetBits = avgBits; return bpp; }
2.2 QP计算
计算出GOP,frame,LCU的比特之后,为了进行编码,需要传递QP值给编码器。计算出来的QP会直接影响编码器给每一帧(LCU)分配的比特数,因此QP计算变得尤为重要,不同于以往的H.264中的R-Q模型,H.265中采取R-lambda,lambda-QP两步计算QP值,以提高精确性。
2.2.1 frame的QP计算
Double TEncRCPic::estimatePicLambda( list<TEncRCPic*>& listPreviousPictures, SliceType eSliceType) { 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; if (eSliceType == I_SLICE) { estLambda = calculateLambdaIntra(alpha, beta, pow(m_totalCostIntra/(Double)m_numberOfPixel, BETA1), bpp); //针对I帧有一个修正的比特数 } else { estLambda = alpha * pow( bpp, beta ); //即JCTVC-K0103 (10),用alpha,beta计算lambda,图附于代码块后 } 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 ) //对当前帧的lambda进行限定,与前一帧、前一个层的lambda差值不能超过这个范围,JCTVC-K0103 的3.2部分 { 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; Double totalWeight = 0.0; // initial BU bit allocation weight for ( Int i=0; i<m_numberOfLCU; i++ ) { Double alphaLCU, betaLCU; if ( m_encRCSeq->getUseLCUSeparateModel() ) { alphaLCU = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_alpha; betaLCU = m_encRCSeq->getLCUPara( m_frameLevel, i ).m_beta; } else { alphaLCU = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha; betaLCU = m_encRCSeq->getPicPara( m_frameLevel ).m_beta; } m_LCUs[i].m_bitWeight = m_LCUs[i].m_numberOfPixel * pow( estLambda/alphaLCU, 1.0/betaLCU ); if ( m_LCUs[i].m_bitWeight < 0.01 ) { m_LCUs[i].m_bitWeight = 0.01; } totalWeight += m_LCUs[i].m_bitWeight; } for ( Int i=0; i<m_numberOfLCU; i++ ) { Double BUTargetBits = m_targetBits * m_LCUs[i].m_bitWeight / totalWeight; m_LCUs[i].m_bitWeight = BUTargetBits; } return estLambda; } Int TEncRCPic::estimatePicQP( Double lambda, list<TEncRCPic*>& listPreviousPictures ) { Int QP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 ); //经典的lambda-QP模型 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 ) //于lambda相同,对QP进行限定 { 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; }
2.2.2 LCU的QP计算
LCU的QP计算与frame的QP计算步骤类似,在此不进行赘述。
2.3 参数更新
在计算完QP后,对LCU及frame进行参数更新
2.3.1 frame的参数更新
Void TEncRCSeq::updateAfterPic ( Int bits ) { m_bitsLeft -= bits; //更新GOP中剩余比特数 m_framesLeft--; } Void TEncRCPic::updateAfterPicture( Int actualHeaderBits, Int actualTotalBits, Double averageQP, Double averageLambda, SliceType eSliceType) { m_picActualHeaderBits = actualHeaderBits; m_picActualBits = actualTotalBits; if ( averageQP > 0.0 ) { m_picQP = Int( averageQP + 0.5 ); } else { m_picQP = g_RCInvalidQPValue; } m_picLambda = averageLambda; Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha; Double beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta; if (eSliceType == I_SLICE) { updateAlphaBetaIntra(&alpha, &beta); } else { // 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 ) { alpha *= ( 1.0 - m_encRCSeq->getAlphaUpdate() / 2.0 ); beta *= ( 1.0 - m_encRCSeq->getBetaUpdate() / 2.0 ); alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha ); beta = Clip3( g_RCBetaMinValue, g_RCBetaMaxValue, 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; //alpha、beta的更新依据JCTVC-K0103的(11-13),附图如下 Double lnbpp = log( picActualBpp ); lnbpp = Clip3( -5.0, -0.1, lnbpp ); beta += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp; alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha ); beta = Clip3( g_RCBetaMinValue, g_RCBetaMaxValue, beta ); } TRCParameter rcPara; rcPara.m_alpha = alpha; rcPara.m_beta = beta; m_encRCSeq->setPicPara( m_frameLevel, rcPara ); if ( m_frameLevel == 1 ) { Double currLambda = Clip3( 0.1, 10000.0, m_picLambda ); Double updateLastLambda = g_RCWeightHistoryLambda * m_encRCSeq->getLastLambda() + g_RCWeightCurrentLambda * currLambda; m_encRCSeq->setLastLambda( updateLastLambda ); } }
2.3.2 LCU的参数更新
Void TEncRCPic::updateAfterCTU( 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; //frame中剩余比特数的更新 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( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha ); beta = Clip3( g_RCBetaMinValue, g_RCBetaMaxValue, 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; //与frame类似,进行alpha、beta的更新 Double lnbpp = log( bpp ); lnbpp = Clip3( -5.0, -0.1, lnbpp ); beta += m_encRCSeq->getBetaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * lnbpp; alpha = Clip3( g_RCAlphaMinValue, g_RCAlphaMaxValue, alpha ); beta = Clip3( g_RCBetaMinValue, g_RCBetaMaxValue, beta ); TRCParameter rcPara; rcPara.m_alpha = alpha; rcPara.m_beta = beta; m_encRCSeq->setLCUPara( m_frameLevel, LCUIdx, rcPara ); }
至此,整个码率控制具体代码部分解读完毕。
相关文章推荐
- H.265/HEVC码率控制总体框架与代码解读(上)
- HEVC码率控制浅析——HM代码阅读之四
- HEVC码率控制浅析——HM代码阅读之二
- HEVC码率控制浅析——HM代码阅读之四
- HEVC码率控制浅析——HM代码阅读之二
- HEVC码率控制算法研究与HM相应代码分析(二)——新的码率控制模型
- HEVC码率控制浅析——HM代码阅读之一
- HEVC码率控制算法研究与HM相应代码分析(三)——算法及代码分析
- HEVC码率控制浅析——HM代码阅读之一
- HEVC码率控制浅析——HM代码阅读之三
- HEVC码率控制浅析——HM代码阅读之一
- HEVC码率控制浅析——HM代码阅读之三
- HEVC码率控制浅析——HM代码阅读之四
- x265代码阅读:码率控制(一)
- 识货的拿走:Android游戏框架解读之总体结构
- android AChartEnginee解说之源代码框架解读
- x265代码阅读:码率控制(二)
- HEVC码率控制: Unified R-Q Model
- HEVC的码率控制的相关提案
- 解读mpeg2编码码率控制