您的位置:首页 > 运维架构

HEVC码率控制(二):从compressGOP()到compressSlice()

2016-12-16 22:26 337 查看

compressGOP()函数要点:

根据iGOPid逐次递增,对一个GOP内的picture依次编码

根据picture level对当前picture进行码率分配

实际中一个Pic就是一个Slice,因此关于slice的循环只执行一次(默认)

根据已经编过的picture,来得到当前picture的lambda

得到当前picture的lambda后,对该picture下每一个CTU进行码率分配

一个picture编完,码流写好了,根据实际编码所用的比特,更新对应level下的lambda参数

compressGOP()
{
...
for(Int iGOPid=0;iGOPid<m_iGopSize;iGOPid++) //GOP内picture循环
{
m_pcSliceEncoder->initEncSlice();
//当前Slice编码参数QP、lambda的默认设置,会在码率控制中进一步被修改
...
if(m_pcCfg->getUseRateCtrl())
{
m_pcRateCtrl->initRCPic(frameLevel);//根据frame level进行比特分配
...
lambda=m_pcRateCtrl->getRCPic()->estimatePicLambda();//确定当前picture的lambda
sliceQP = m_pcRateCtrl->getRCPic()->estimatePicQP( lambda,.. );//QP由lambda计算
...
}
for(UInt nextCtuTsAddr = 0; nextCtuTsAddr < numberOfCtusInFrame; ) //这个循环只执行了一次
{
...
m_pcSliceEncoder->compressSlice();
...
}
...
//编完一帧,开始熵编码,写码流等等
...
if(m_pcCfg->getUseRateCtrl())
{
...
updataAfterPicture(); //编完一帧,更新当前picture level对应的lambda参数值(alpha和beta)
...
}

}//iGOPid loop

}


estimatePicLambda()函数中,在得到lambda之后,对一个picture内所有LCU进行比特分配:

Double TEncRCPic::estimatePicLambda( )
{
Double alpha         = m_encRCSeq->getPicPara( m_frameLevel ).m_alpha;
Double beta          = m_encRCSeq->getPicPara( m_frameLevel ).m_beta;
//alpha和beta从数组中对应level处取,这两个值在编完前一个相同level的帧后,被设置
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);
}
else
{
estLambda = alpha * pow( bpp, beta );
}
Double lastLevelLambda = -1.0;
Double lastPicLambda   = -1.0;
Double lastValidLambda = -1.0;
//对estLambda进行Clip操作

m_estPicLambda = estLambda; //TEncRCPic::m_estPicLambda

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


m_bitWeight即使每一个CTU对应的比特分配权重

跟新picture level 的alpha和beta函数updateAfterPicture():

void TEncRCPic::updateAfterPicture()
{
//计算inputLambda
//计算calLambda
alpha += m_encRCSeq->getAlphaUpdate() * ( log( inputLambda ) - log( calLambda ) ) * alpha;
Double lnbpp = log( picActualBpp );
lnbpp = Clip3( -5.0, -0.1, lnbpp );
//按照码率控制算法,跟新alpha和beta
TRCParameter rcPara;
rcPara.m_alpha = alpha;
rcPara.m_beta  = beta;
m_encRCSeq->setPicPara( m_frameLevel, rcPara );
//将更新后的alpha和beta保存在数组中
}


compressSlice()函数

按照CTU的先后顺序循环遍历编码

编完一个CTU之后,要更新参数

void TEncSlice::compressSlice()
{
...
xDetermineStartAndBoundingCtuTsAddr ( startCtuTsAddr, boundingCtuTsAddr, pcPic );
...
for( UInt ctuTsAddr = startCtuTsAddr; ctuTsAddr < boundingCtuTsAddr; ++ctuTsAddr )
//对一个Slice内所有CTU开始循环编码
{
...
if ( m_pcCfg->getUseRateCtrl() )
{
Int estQP        = pcSlice->getSliceQp();
Double estLambda = -1.0;
Double bpp       = -1.0;

8f8d
//根据LCU的m_bitWeight这个成员计算当前LCU的比特,再除以LCU内Pixel总数,得到bpp。而m_bitWeight这个成员是得到Pic的lambda之后被赋值。
estLambda = m_pcRateCtrl->getRCPic()->getLCUEstLambdaAndQP(bpp, pcSlice->getSliceQp(), &estQP);
...
m_pcRdCost->setLambda(estLambda, pcSlice->getSPS()->getBitDepths());
}
m_pcCuEncoder->compressCtu(pCtu);
...
if(m_pcCfg->getUseRateCtrl())
{
...
updateAfterCTU();
...
}
//将CTU的编码结果累加到Picture的统计量之中
m_uiPicTotalBits+=pCtu->getTotalBits();
m_dPicRdcost+=PCtu->getTotalCost();
m_uiPicDist+=PCtu->getTotalDistortion;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: