大神传送门
https://blog.csdn.net/cpp12341234/article/details/45867631
https://blog.csdn.net/HEVC_CJL/article/details/11128745
//compressslice()中的关于RC的部分
Double oldLambda = m_pcRdCost->getLambda();
if ( m_pcCfg->getUseRateCtrl() )
{
Int estQP = pcSlice->getSliceQp();//定义各种变量并且初始化
Double estLambda = -1.0;
Double bpp = -1.0;
if ( ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE && m_pcCfg->getForceIntraQP() ) || !m_pcCfg->getLCULevelRC() )
{//!< 如果当前slice为I slice或者不进行LCU level RC,则LCU直接使用当前slice的QP
estQP = pcSlice->getSliceQp();
}
else
{
bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());//获得当前的LCU的目标Bpp
if ( rpcPic->getSlice( 0 )->getSliceType() == I_SLICE)//若为I帧
{
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
//根据Bpp、slice的QP和估计QP来对lambda进行估计
}
else//若不是I帧
{
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );//根据Bpp对lambda进行估计
estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
//然后根据估计lambda和slice的QP对LCU的QP进行估计
}
estQP = Clip3( -pcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, estQP );//最终对QP进行确定
m_pcRdCost->setLambda(estLambda);//对估计的lambda进行设置
#if RDOQ_CHROMA_LAMBDA
// set lambda for RDOQ
const Double chromaLambda = estLambda / m_pcRdCost->getChromaWeight();
const Double lambdaArray[MAX_NUM_COMPONENT] = { estLambda, chromaLambda, chromaLambda };
m_pcTrQuant->setLambdas( lambdaArray );
#else
m_pcTrQuant->setLambda( estLambda );
#endif
}
m_pcRateCtrl->setRCQP( estQP );
#if ADAPTIVE_QP_SELECTION
pcCU->getSlice()->setSliceQpBase( estQP );//对编码时使用的QP进行设置
#endif
}
①其中调用了 bpp = m_pcRateCtrl->getRCPic()->getLCUTargetBpp(pcSlice->getSliceType());//获得当前的LCU的目标Bpp
Double TEncRCPic::getLCUTargetBpp(SliceType eSliceType)
{
Int LCUIdx = getLCUCoded();
// Int getLCUCoded() { return m_numberOfLCU - m_LCULeft; }返回已经编码的LCU的数目
Double bpp = -1.0;//定义变量并且初始化
Int avgBits = 0;
if (eSliceType == I_SLICE)//如果当前帧为I帧
{
Int noOfLCUsLeft = m_numberOfLCU - LCUIdx + 1;//剩余LCU的个数=LCU的总个数-已经编码的LCU的个数+1
Int bitrateWindow = min(4,noOfLCUsLeft);//比特率窗,最小为4,不能小于GOP的size
Double MAD = getLCU(LCUIdx).m_costIntra;
// TRCLCU& getLCU( Int LCUIdx ){ return m_LCUs[LCUIdx]; }返回TRCLCU类型的结构体,具体见上图,编号为LCUIdx的LCU块的比重值为MAD
if (m_remainingCostIntra > 0.1 )//如果剩余帧内LCU的比重>0.1
{
Double weightedBitsLeft = (m_bitsLeft*bitrateWindow+(m_bitsLeft-getLCU(LCUIdx).m_targetBitsLeft)*noOfLCUsLeft)/(Double)bitrateWindow;
//剩余比特
//*******解释见下方******
avgBits = Int( MAD*weightedBitsLeft/m_remainingCostIntra );
}
else//如果剩余帧内消耗比重<=0.1
{
avgBits = Int( m_bitsLeft / m_LCULeft );//平均每个LCU的比特数=剩余比特/剩余LCU个数
}
m_remainingCostIntra -= MAD;//m_remainingCostIntra =m_remainingCostIntra -MAD 更新剩余LCU所占的比重
}
else//如果当前帧不为I帧
{
Double totalWeight = 0;
for ( Int i=LCUIdx; i<m_numberOfLCU; i++ )//从当前的LCU向后进行遍历
{
totalWeight += m_LCUs[i].m_bitWeight;//计算出包括当前LCU在内的剩余LCU所需要的比特和
}
Int realInfluenceLCU = min( g_RCLCUSmoothWindowSize, getLCULeft() );//真正有用的(影响大的)LCU数目=平滑窗口和剩余LCU数目的最小值
avgBits = (Int)( m_LCUs[LCUIdx].m_bitWeight - ( totalWeight - m_bitsLeft ) / realInfluenceLCU + 0.5 );
//平均每个LCU的比特数=当前LCU的需要比特-(包括当前LCU在内的剩余LCU所需要的比特和-实际剩余的比特)/有用的LCU数目+0.5
}
if ( avgBits < 1 )//平均每个LCU至少分配1个比特
{
avgBits = 1;
}
bpp = ( Double )avgBits/( Double )m_LCUs[ LCUIdx ].m_numberOfPixel;//计算当前LCU的Bpp值
m_LCUs[ LCUIdx ].m_targetBits = avgBits;//平均比特即作为当前LCU的目标比特
return bpp;//返回Bpp的值
}
②其中 estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP); //如果是I帧,根据Bpp、slice的QP和估计QP来对lambda进行估计
Double TEncRCPic::getLCUEstLambdaAndQP(Double bpp, Int clipPicQP, Int *estQP)
{
Int LCUIdx = getLCUCoded();//获得已经编码完的帧数
Double alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;//获得alpha、beta
Double beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
Double costPerPixel = getLCU(LCUIdx).m_costIntra/(Double)getLCU(LCUIdx).m_numberOfPixel;//每个像素在帧内的比重=当前LCU在帧内的比重/总像素数
costPerPixel = pow(costPerPixel, BETA1);//对比重进行修正
Double estLambda = calculateLambdaIntra(alpha, beta, costPerPixel, bpp);//通过计算帧内lambda的函数求出估计lambda
//Double TEncRCPic::calculateLambdaIntra(Double alpha, Double beta, Double MADPerPixel, Double bitsPerPixel)
//{
// return ( (alpha/256.0) * pow( MADPerPixel/bitsPerPixel, beta ) );
//}
Int clipNeighbourQP = g_RCInvalidQPValue;//const Int g_RCInvalidQPValue = -999;定义变量存储邻域QP
for (Int i=LCUIdx-1; i>=0; i--)
{
if ((getLCU(i)).m_QP > g_RCInvalidQPValue)//从当前LCU向前遍历,若QP为有效值
{
clipNeighbourQP = getLCU(i).m_QP;//将该邻域QP赋给变量并跳出循环
break;
}
}
Int minQP = clipPicQP - 2;//clipPicQP为slice的QP值
Int maxQP = clipPicQP + 2;
if ( clipNeighbourQP > g_RCInvalidQPValue )//若邻域QP有效,对Max和minQP进行修正
{
maxQP = min(clipNeighbourQP + 1, maxQP);
minQP = max(clipNeighbourQP - 1, minQP);
}
Double maxLambda=exp(((Double)(maxQP+0.49)-13.7122)/4.2005);//通过maxQP和minQP分别计算Maxlambda和minlambda
Double minLambda=exp(((Double)(minQP-0.49)-13.7122)/4.2005);
estLambda = Clip3(minLambda, maxLambda, estLambda);//选出最适合的作为当前LCU的估计lambda
*estQP = Int( 4.2005 * log(estLambda) + 13.7122 + 0.5 );//根据得到的估计lambda对传进来的估计QP进行修正
*estQP = Clip3(minQP, maxQP, *estQP);
return estLambda;//返回估计的lambda值
}
③estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambda( bpp );//如果不是I帧,根据Bpp对lambda进行估计
Double TEncRCPic::getLCUEstLambda( Double bpp )
{
Int LCUIdx = getLCUCoded();//获得已经编码的帧数
Double alpha;//新建变量
Double beta;
if ( m_encRCSeq->getUseLCUSeparateModel() )//如果每个LCU的alpha和beta互相独立
{
alpha = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_alpha;//获得当前LCU的alpha和beta参数
beta = m_encRCSeq->getLCUPara( m_frameLevel, LCUIdx ).m_beta;
}
else
{
alpha = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;//否则使用图片级的alpha和beta参数
beta = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
}
Double estLambda = alpha * pow( bpp, beta );//新建估计lambda变量,并且计算赋值
//for Lambda clip, picture level clip
Double clipPicLambda = m_estPicLambda;//得到图片的估计lambda
//for Lambda clip, LCU level clip
Double clipNeighbourLambda = -1.0;//新建邻域lambda变量,并初始化
for ( Int i=LCUIdx - 1; i>=0; i-- )//从当前LCU向前进行遍历
{
if ( m_LCUs[i].m_lambda > 0 )//若lambda>0,则有效lambda的值就赋给邻域lambda变量
{
clipNeighbourLambda = m_LCUs[i].m_lambda;
break;
}
}
if ( clipNeighbourLambda > 0.0 )//若邻域lambda>0
{
estLambda = Clip3( clipNeighbourLambda * pow( 2.0, -1.0/3.0 ), clipNeighbourLambda * pow( 2.0, 1.0/3.0 ), estLambda );//对估计lambda进行修正
}
if ( clipPicLambda > 0.0 )//若图片层估计lambda>0
{
estLambda = Clip3( clipPicLambda * pow( 2.0, -2.0/3.0 ), clipPicLambda * pow( 2.0, 2.0/3.0 ), estLambda );//对估计lambda进行修正
}
else
{
estLambda = Clip3( 10.0, 1000.0, estLambda );//否则用常数进行修正
}
if ( estLambda < 0.1 )//估计的LCUlambda至少为0.1
{
estLambda = 0.1;
}
return estLambda;//返回估计的lambda
}
④ estQP = m_pcRateCtrl->getRCPic()->getLCUEstQP ( estLambda, pcSlice->getSliceQp() );
//若不是I帧,根据估计lambda和slice的QP对LCU的QP进行估计
Int TEncRCPic::getLCUEstQP( Double lambda, Int clipPicQP )
{
Int LCUIdx = getLCUCoded();//获得已经编码的帧数
Int estQP = Int( 4.2005 * log( lambda ) + 13.7122 + 0.5 );//根据传入的LCU的估计lambda对QP进行估计
//for Lambda clip, LCU level clip
Int clipNeighbourQP = g_RCInvalidQPValue;
//const Int g_RCInvalidQPValue = -999;新建邻域QP变量并且初始化
for ( Int i=LCUIdx - 1; i>=0; i-- )//从当前LCU向前遍历
{
if ( (getLCU(i)).m_QP > g_RCInvalidQPValue )
{
clipNeighbourQP = getLCU(i).m_QP;//若为有效的QP则赋给邻域QP
break;
}
}
if ( clipNeighbourQP > g_RCInvalidQPValue )//若邻域QP有效
{
estQP = Clip3( clipNeighbourQP - 1, clipNeighbourQP + 1, estQP );//对预测QP进行修正
}
estQP = Clip3( clipPicQP - 2, clipPicQP + 2, estQP );//根据图片层的QP再次进行修正
return estQP;//返回预测的QP值
}
本博文仅作小白学习记录使用,所有文章出处均在开头链接处可见,所有疑惑点均作了标注。欢迎各位大佬交流批评指正,侵删。