H.265/HEVC率失真優化(RDO)及其HM程式碼註解
一、率失真優化(RDO)的目的
選擇一個最小失真的編碼模式可以帶來最好的視訊質量, 然而這往往需要很高的編碼位元率。如何在有限的編碼位元數下,選擇一個失真最小的模式是編碼中的關鍵問題。對於給定編碼單元,上述求極值問題可將其轉化為:在給定位元速率的情況下,儘可能降低失真D。這也即是 RDO 的目的,率失真代價函式表述如下:
{Para}opt=arg min{Para}(D+λ•R)
其中,R 和D 分別表示編碼所消耗的位元數位元速率以及失真程度,{Para}opt表示最佳的編碼引數集,包括模式選擇、運動估計以及QP等,λ 為拉格朗日乘子。
二、HEVC中率失真優化方法
HM 採用拉格朗日優化方法為每個編碼樹單元CTU確定除編碼引數QP之外的所有編碼引數,主要包括CU劃分模式、CU中PU和TU的劃分、PU預測等等。每個CTU採用分級方式確定不同層的編碼引數,步驟如下:
1. 首先,遍歷所有CU,按如下公式對CU(CU從64x64到8x8)進行劃分模式進行編碼;
min J J
2. 然後,在CTU中遍歷所有PU(PU是預測的基本單元)模式和TU(TU是變換的基本單元,TU遍歷的最小尺寸為4x4)的
組合,選擇率失真代價值最小的確定為最優模式;
3. 不論幀內還是幀間,都會存在PU的預測,對PU的預測模式也是遍歷所有的預測模式,分別計算每個模式對應的率失真代價值,選取最小的率失真代價值對應的預測模式為最優模式;
三、HEVC參考模型HM中初始QP和拉格朗日乘子初始化
在RDO中拉格朗日乘子作為率失真代價函式計算的關鍵引數,每幀的初始拉格朗日乘子會根據Slice的型別和在GOP中的位置,根據下式中每幀的初始QP確定對應的拉格朗日乘子:
模式選擇過程對應拉格朗日乘子:
λ
運動估計過程對應拉格朗日乘子:
λ(Motion)=pow(λ(Mode),1/2.0)
W為加權因子,I幀為0.57。根據當前Slice是否最為參考影象,Nb為GOP中B幀的個數(發現LDP配置中P幀在HM中也算作B幀),如果為非參考影象α為1,如果為非參考幀時:
α= 1.0 - Clip3(0.0,0.5,0.05*Nb)
HM在編碼GOP之前會對每個Slice的拉格朗日乘子作初始化,並在compressGOP函式中的initEncSlice實現初始化
m_pcSliceEncoder->initEncSlice ( pcPic, iPOCLast, pocCurr, iGOPid, pcSlice, isField );
如果開啟多QP優化,會對每個遍歷的QP初始lambda,我用的HM版本為HM 16.9,不過不同版本的對應函式應該沒有太大變化
// pre-compute lambda and QP values for all possible QP candidates
for ( Int iDQpIdx = 0; iDQpIdx < 2 * m_pcCfg->getDeltaQpRD() + 1; iDQpIdx++ )
{
// compute QP value
dQP = dOrigQP + ((iDQpIdx+1)>>1)*(iDQpIdx%2 ? -1 : 1);
// compute lambda value
Int NumberBFrames = ( m_pcCfg->getGOPSize() - 1 ); //計算GOP中B幀個數
Int SHIFT_QP = 12;
#if FULL_NBIT
Int bitdepth_luma_qp_scale = 6 * (rpcSlice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) - 8);
#else
Int bitdepth_luma_qp_scale = 0;
#endif
Double qp_temp = (Double) dQP + bitdepth_luma_qp_scale - SHIFT_QP;
#if FULL_NBIT
Double qp_temp_orig = (Double) dQP - SHIFT_QP;
#endif
// Case #1: I or P-slices (key-frame)
Double dQPFactor = m_pcCfg->getGOPEntry(iGOPid).m_QPFactor;
if ( eSliceType==I_SLICE )
{
if (m_pcCfg->getIntraQpFactor()>=0.0 && m_pcCfg->getGOPEntry(iGOPid).m_sliceType != I_SLICE)
{
dQPFactor=m_pcCfg->getIntraQpFactor(); //如果不是I幀,根據 cfg 配置,對GOP中不同Slice分配不同的拉格朗日乘子權重
}
else
{
//I幀根據GOP中非參考影象的個數分配拉格朗日乘子權重
Double dLambda_scale = 1.0 - Clip3( 0.0, 0.5, 0.05*(Double)(isField ? NumberBFrames/2 : NumberBFrames) );
dQPFactor=0.57*dLambda_scale;
}
}
dLambda = dQPFactor*pow( 2.0, qp_temp/3.0 );
if ( depth>0 ) // I幀的 depth 為0
{
#if FULL_NBIT
dLambda *= Clip3( 2.00, 4.00, (qp_temp_orig / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )
#else
dLambda *= Clip3( 2.00, 4.00, (qp_temp / 6.0) ); // (j == B_SLICE && p_cur_frm->layer != 0 )
#endif
}
// if hadamard is used in ME process
if ( !m_pcCfg->getUseHADME() && rpcSlice->getSliceType( ) != I_SLICE )
{
dLambda *= 0.95;
}
#if W0062_RECALCULATE_QP_TO_ALIGN_WITH_LAMBDA
Double lambdaRef = 0.57*pow(2.0, qp_temp/3.0);
// QP correction due to modified lambda
Double qpOffset = floor((3.0*log(dLambda/lambdaRef)/log(2.0)) +0.5);
dQP += qpOffset;
#endif
iQP = max( -rpcSlice->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), min( MAX_QP, (Int) floor( dQP + 0.5 ) ) );
//iDQpIdx為遍歷QP的索引
m_vdRdPicLambda[iDQpIdx] = dLambda;
m_vdRdPicQp [iDQpIdx] = dQP;
m_viRdPicQp [iDQpIdx] = iQP;
}
// 如果沒有多QP優化時,初始的QP和拉格朗日乘子選擇索引為0的PQ和lambda
// obtain dQP = 0 case
dLambda = m_vdRdPicLambda[0];
dQP = m_vdRdPicQp [0];
iQP = m_viRdPicQp [0];
順帶說一下,在編碼CU過程中會遞迴呼叫xCompressCU函式,並在完成遞迴編碼完子CU後都會比較率失真代價值
#if AMP_ENC_SPEEDUP
DEBUG_STRING_NEW(sChild)
if ( !(rpcBestCU->getTotalCost()!=MAX_DOUBLE && rpcBestCU->isInter(0)) )
{
xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), NUMBER_OF_PART_SIZES );
}
else
{
xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), rpcBestCU->getPartitionSize(0) );
}
DEBUG_STRING_APPEND(sTempDebug, sChild)
#else
xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth );
#endif
rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth ); // Keep best part data to current temporary data.
xCopyYuv2Tmp( pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth );
}
else
{
pcSubBestPartCU->copyToPic( uhNextDepth );
rpcTempCU->copyPartFrom( pcSubBestPartCU, uiPartUnitIdx, uhNextDepth );
}
}
m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);
if( !bBoundary )
{
m_pcEntropyCoder->resetBits();
m_pcEntropyCoder->encodeSplitFlag( rpcTempCU, 0, uiDepth, true );
rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
}
rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost( rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion() );
比較率失真代價函式,選擇最優編碼引數
xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false) ); // RD compare current larger prediction