幀間預測基本知識
對幀間預測有所了解的同學應該都知道,幀間預測操作一般都是由兩個最基本也是最核心的操作組成:運動估計(ME)和運動補償(MC)。
運動估計:負責找到目前幀和參考幀之間的比對運動矢量(MV)
運動補償:對MV所指向的參考幀中的比對參考塊,進行插值濾波等操作,得到最終的預測參考塊(即用于和目前編碼塊作差得到殘差系數等)
HM中,ME和MC便是在predInterSearch函數中完成的。
備注:想複習補充幀間預測相關知識的同學,可以參見大神的部落格:https://blog.csdn.net/NB_vol_1/article/details/55272434
運動補償為什麼要插值
衆所周知,現在的視訊編碼标準MV都支援分數像素,而參考幀中的原始參考塊是整數像素級的,為了支援分數像素MV,是以需要對整數像素塊進行插值操作,得到分數像素精度的預測參考塊。進而進行後續的求殘差、變換量化等操作。
函數基本流程
函數原型
Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv DEBUG_STRING_FN_DECLARE(sDebug), Bool bUseRes, Bool bUseMRG )
參數說明:
pcCU:目前評估的CU模式
pcOrgYuv:CU原始資料
pcPredYuv:預測資料
pcResiYuv:殘差資料
pcRecoYuv:重建資料
bUseRes:預設為false,false–函數開頭處會清空pcResiYuv。樓主看了下,程式中該變量恒為false,不是很明白該變量的用途。
bUseMRG:預設為false。true–對目前劃分模式進行Merge評估,即找到最優的Merge候選項,不需要ME,false–進行ME + MC,計算代價
流程
P幀
對于CU下的每一個PU,周遊參考清單0中的各幀作為參考幀,進行如下操作:
-
AMVP
獲得最優MVP,并将其作為ME的搜尋起點,對應的函數是xEstimateMvPredAMVP
-
ME
以AMVP得到的MVP作為搜尋起點,搜尋最優MV,對應的函數是xMotionEstimation
周遊完各參考幀後,便得到了最優的幀間預測參數:參考幀和對應的MV
- 設定最優的預測模式、參考幀、MV、MVD
- 若劃分尺寸不是2N x 2N,則補充進行Merge模式評估,将最優的Merge模式代價和ME代價進行比較,更新最優模式。注:Merge省略了ME過程,直接使用時空域相鄰候選項的MV。
B幀
B幀與P幀主要的差別就是:B幀會選用兩個參考幀,對應兩個MV。對應到預測流程上的差別基本如下:
- 會周遊兩個參考清單中的各幀作為參考幀,進行AMVP和ME操作,記錄單向預測的預測性能
- 進行雙向預測,即從參考清單0和參考清單1中各選擇一個參考幀,進行預測。由于需要兩個MV,是以在對參考清單X(X取0,1)進行ME之後,需要補充進行MC操作(得到預測參考塊X),然後再對清單1-X進行ME操作,進而得到最優的兩個MV。
- 将雙向預測的性能與單向預測進行比較,得到最終的預測方式并記錄詳細的預測資訊
Merge評估
bUseMRG為true時,隻進行Merge模式評估,即隻進行步驟4。
關鍵變量
最核心的變量是bTestNormalMC。bTestNormalMC由傳入的形參bUseMRG決定,bUseMRG為true,bTestNormalMC才會被設為false,進而進行Merge模式評估。
作用:true–進行正常的ME和MC操作,false–進行Merge模式評估,省去了ME操作。
其他的重要變量都在源代碼中以注釋的形式進行了說明。注:注釋中還包含了樓主的一些疑問,還希望有大佬能指點迷津或者一起交流探讨。
源代碼分析
備注:此代碼段是從HM16.20源代碼TEncSearch.cpp中完整拷貝過來的,裡面加入了個人的閱讀注釋。樓主對代碼的注釋可能會存在了解偏差,望讀者可以指出來,一起交流探讨~畢竟樓主對于HM源代碼研究不多,不少地方也存在疑惑。希望有大佬同仁一起學習交流,共同進步!
//! search of the best candidate for inter prediction
#if AMP_MRG
Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv DEBUG_STRING_FN_DECLARE(sDebug), Bool bUseRes, Bool bUseMRG )
#else
Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv, Bool bUseRes )
#endif
{
for(UInt i=0; i<NUM_REF_PIC_LIST_01; i++)
{
m_acYuvPred[i].clear();
}
m_cYuvPredTemp.clear();
pcPredYuv->clear();
if ( !bUseRes )
{
pcResiYuv->clear(); // false--清空殘差塊
}
pcRecoYuv->clear();
TComMv cMvSrchRngLT;
TComMv cMvSrchRngRB;
TComMv cMvZero;
TComMv TempMv; //kolya
TComMv cMv[2]; // 存儲單向預測的MV(分别對應前向和後向)
TComMv cMvBi[2]; // 存儲雙向預測的兩個MV
TComMv cMvTemp[2][33];
Int iNumPart = pcCU->getNumPartitions(); // 擷取目前PU劃分模式下子塊的個數(例如2N x 2N 對應 1, 2N x N 對應 2, N x N 對應 4)
Int iNumPredDir = pcCU->getSlice()->isInterP() ? 1 : 2; // P幀預測方向數為1,B幀為2
TComMv cMvPred[2][33];
TComMv cMvPredBi[2][33];
Int aaiMvpIdxBi[2][33];
Int aaiMvpIdx[2][33]; // *** 存儲 AMVP 得到的最優 MVP的 清單索引
Int aaiMvpNum[2][33]; // *** 存儲 AMVP 得到的最優 MVP的 清單長度
AMVPInfo aacAMVPInfo[2][33];
Int iRefIdx[2]={0,0}; //If un-initialized, may cause SEGV in bi-directional prediction iterative stage.
Int iRefIdxBi[2];
UInt uiPartAddr;
Int iRoiWidth, iRoiHeight; // 寬高
UInt uiMbBits[3] = {1, 1, 0}; // *** 對應的是PU劃分模式所需要消耗的編碼比特數
UInt uiLastMode = 0; // *** 0--前向預測,1--後向預測,2--雙向預測
Int iRefStart, iRefEnd; // 參考幀的範圍
PartSize ePartSize = pcCU->getPartitionSize( 0 ); // *** 擷取CU的劃分尺寸
Int bestBiPRefIdxL1 = 0;
Int bestBiPMvpL1 = 0;
Distortion biPDistTemp = std::numeric_limits<Distortion>::max();
TComMvField cMvFieldNeighbours[MRG_MAX_NUM_CANDS << 1]; // double length for mv of both lists
UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS]; // *** MRG_MAX_NUM_CANDS 宏值為5,說明AMVP是從5個候選項中進行選擇
Int numValidMergeCand = 0 ;
for ( Int iPartIdx = 0; iPartIdx < iNumPart; iPartIdx++ )
{
Distortion uiCost[2] = { std::numeric_limits<Distortion>::max(), std::numeric_limits<Distortion>::max() };
Distortion uiCostBi = std::numeric_limits<Distortion>::max();
Distortion uiCostTemp;
UInt uiBits[3]; // 對應的是3種預測模式:0-前向預測,1-後向預測,2-雙向預測
UInt uiBitsTemp;
Distortion bestBiPDist = std::numeric_limits<Distortion>::max();
Distortion uiCostTempL0[MAX_NUM_REF]; // *** MAX_NUM_REF 該宏的值為16,說明HM中參考幀緩沖最多為16
for (Int iNumRef=0; iNumRef < MAX_NUM_REF; iNumRef++)
{
uiCostTempL0[iNumRef] = std::numeric_limits<Distortion>::max();
}
UInt uiBitsTempL0[MAX_NUM_REF];
TComMv mvValidList1;
Int refIdxValidList1 = 0;
UInt bitsValidList1 = MAX_UINT;
Distortion costValidList1 = std::numeric_limits<Distortion>::max();
xGetBlkBits( ePartSize, pcCU->getSlice()->isInterP(), iPartIdx, uiLastMode, uiMbBits); // 計算目前PU劃分模式所需要消耗的編碼比特數
pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iRoiWidth, iRoiHeight ); // 計算目前PU塊的位址 uiPartAddr, 寬度 iRoiWidth, 高度 iRoiHeight
#if AMP_MRG
Bool bTestNormalMC = true; // false--隻對Merge模式進行評估選擇
if ( bUseMRG && pcCU->getWidth( 0 ) > 8 && iNumPart == 2 ) // *** bUseMRG為true時,才可能會将bTestNormalMC設為false,關鍵代碼段
{
bTestNormalMC = false; // *** bTestNormalMC 設為 false,會跳過對PU的多參考幀運動估計搜尋過程,隻對Merge模式進行評估選擇,注意 bTestNormalMC為true時,也會對Merge模式進行評估
}
if (bTestNormalMC)
{
#endif
// Uni-directional prediction
for ( Int iRefList = 0; iRefList < iNumPredDir; iRefList++ ) // ***
{
RefPicList eRefPicList = ( iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
for ( Int iRefIdxTemp = 0; iRefIdxTemp < pcCU->getSlice()->getNumRefIdx(eRefPicList); iRefIdxTemp++ )
{
uiBitsTemp = uiMbBits[iRefList];
if ( pcCU->getSlice()->getNumRefIdx(eRefPicList) > 1 )
{
uiBitsTemp += iRefIdxTemp+1;
if ( iRefIdxTemp == pcCU->getSlice()->getNumRefIdx(eRefPicList)-1 )
{
uiBitsTemp--;
}
}
xEstimateMvPredAMVP( pcCU, pcOrgYuv, iPartIdx, eRefPicList, iRefIdxTemp, cMvPred[iRefList][iRefIdxTemp], false, &biPDistTemp); // 對目前參考幀(iRefIdxTemp) 進行AMVP, 獲得最優MVP
aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->getMVPIdx(eRefPicList, uiPartAddr);
aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->getMVPNum(eRefPicList, uiPartAddr);
if(pcCU->getSlice()->getMvdL1ZeroFlag() && iRefList==1 && biPDistTemp < bestBiPDist) // *** getMvdL1ZeroFlag()為true, bestBiPDist,bestBiPMvpL1,bestBiPRefIdxL1才有效
{
bestBiPDist = biPDistTemp;
bestBiPMvpL1 = aaiMvpIdx[iRefList][iRefIdxTemp];
bestBiPRefIdxL1 = iRefIdxTemp;
}
uiBitsTemp += m_auiMVPIdxCost[aaiMvpIdx[iRefList][iRefIdxTemp]][AMVP_MAX_NUM_CANDS]; // *** AMVP_MAX_NUM_CANDS為2,說明AMVP最終清單長度為2
if ( m_pcEncCfg->getFastMEForGenBLowDelayEnabled() && iRefList == 1 ) // *** list 1
{
if ( pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp ) >= 0 ) // 目前參考幀同時出現在List0 和 List1中,直接複制List0 中的計算資訊
{
cMvTemp[1][iRefIdxTemp] = cMvTemp[0][pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp )];
uiCostTemp = uiCostTempL0[pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp )];
/*first subtract the bit-rate part of the cost of the other list*/
uiCostTemp -= m_pcRdCost->getCost( uiBitsTempL0[pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp )] );
/*correct the bit-rate part of the current ref*/
m_pcRdCost->setPredictor ( cMvPred[iRefList][iRefIdxTemp] );
uiBitsTemp += m_pcRdCost->getBitsOfVectorWithPredictor( cMvTemp[1][iRefIdxTemp].getHor(), cMvTemp[1][iRefIdxTemp].getVer() );
/*calculate the correct cost*/
uiCostTemp += m_pcRdCost->getCost( uiBitsTemp );
}
else // *** 采用List1中的參考幀進行運動估計
{
xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
}
}
else
{
xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp );
}
xCopyAMVPInfo(pcCU->getCUMvField(eRefPicList)->getAMVPInfo(), &aacAMVPInfo[iRefList][iRefIdxTemp]); // must always be done ( also when AMVP_MODE = AM_NONE )
xCheckBestMVP(pcCU, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp);
if ( iRefList == 0 )
{
uiCostTempL0[iRefIdxTemp] = uiCostTemp; // *** 存儲List0中各個參考幀的 預測Cost和 Bits資訊
uiBitsTempL0[iRefIdxTemp] = uiBitsTemp;
}
if ( uiCostTemp < uiCost[iRefList] ) // *** 根據Cost大小,更新各個清單的最優Cost, Bits, Mv, 參考幀Idx資訊
{
uiCost[iRefList] = uiCostTemp;
uiBits[iRefList] = uiBitsTemp; // storing for bi-prediction
// set motion
cMv[iRefList] = cMvTemp[iRefList][iRefIdxTemp];
iRefIdx[iRefList] = iRefIdxTemp;
}
if ( iRefList == 1 && uiCostTemp < costValidList1 && pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp ) < 0 ) // *** 對于B幀,根據Cost大小,更新List1的最優Cost, Bits, Mv, 參考幀Idx資訊
{
costValidList1 = uiCostTemp;
bitsValidList1 = uiBitsTemp;
// set motion
mvValidList1 = cMvTemp[iRefList][iRefIdxTemp];
refIdxValidList1 = iRefIdxTemp;
}
}
}
// Bi-predictive Motion estimation
if ( (pcCU->getSlice()->isInterB()) && (pcCU->isBipredRestriction(iPartIdx) == false) ) // *** PU尺寸為 8x8,且劃分方式不是 2Nx2N時,isBipredRestriction為true
{ // *** 即當子塊尺寸小于 8x8時,不進行循環體内的操作(即雙向預測)
cMvBi[0] = cMv[0]; cMvBi[1] = cMv[1]; // *** 複制單向預測的預測結果
iRefIdxBi[0] = iRefIdx[0]; iRefIdxBi[1] = iRefIdx[1];
::memcpy(cMvPredBi, cMvPred, sizeof(cMvPred));
::memcpy(aaiMvpIdxBi, aaiMvpIdx, sizeof(aaiMvpIdx));
UInt uiMotBits[2];
if(pcCU->getSlice()->getMvdL1ZeroFlag()) // *** getMvdL1ZeroFlag()是與GPB相關的标志位,但具體什麼作用還沒搞明白。。。???
{
xCopyAMVPInfo(&aacAMVPInfo[1][bestBiPRefIdxL1], pcCU->getCUMvField(REF_PIC_LIST_1)->getAMVPInfo());
pcCU->setMVPIdxSubParts( bestBiPMvpL1, REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
aaiMvpIdxBi[1][bestBiPRefIdxL1] = bestBiPMvpL1;
cMvPredBi[1][bestBiPRefIdxL1] = pcCU->getCUMvField(REF_PIC_LIST_1)->getAMVPInfo()->m_acMvCand[bestBiPMvpL1];
cMvBi[1] = cMvPredBi[1][bestBiPRefIdxL1];
iRefIdxBi[1] = bestBiPRefIdxL1;
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllMv( cMvBi[1], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllRefIdx( iRefIdxBi[1], ePartSize, uiPartAddr, 0, iPartIdx );
TComYuv* pcYuvPred = &m_acYuvPred[REF_PIC_LIST_1];
motionCompensation( pcCU, pcYuvPred, REF_PIC_LIST_1, iPartIdx ); // *** 對于B Slice,需要對後向參考的預測塊進行運動補償,再重新進行運動估計。
uiMotBits[0] = uiBits[0] - uiMbBits[0];
uiMotBits[1] = uiMbBits[1];
if ( pcCU->getSlice()->getNumRefIdx(REF_PIC_LIST_1) > 1 )
{
uiMotBits[1] += bestBiPRefIdxL1+1;
if ( bestBiPRefIdxL1 == pcCU->getSlice()->getNumRefIdx(REF_PIC_LIST_1)-1 )
{
uiMotBits[1]--;
}
}
uiMotBits[1] += m_auiMVPIdxCost[aaiMvpIdxBi[1][bestBiPRefIdxL1]][AMVP_MAX_NUM_CANDS];
uiBits[2] = uiMbBits[2] + uiMotBits[0] + uiMotBits[1];
cMvTemp[1][bestBiPRefIdxL1] = cMvBi[1];
}
else
{
uiMotBits[0] = uiBits[0] - uiMbBits[0];
uiMotBits[1] = uiBits[1] - uiMbBits[1];
uiBits[2] = uiMbBits[2] + uiMotBits[0] + uiMotBits[1];
}
// 4-times iteration (default)
Int iNumIter = 4; // *** 為什麼是4???
// fast encoder setting: only one iteration
if ( m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE1 || m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE2 || pcCU->getSlice()->getMvdL1ZeroFlag() )
{
iNumIter = 1; // 設為1,減少搜尋次數,進而提高編碼速度
}
for ( Int iIter = 0; iIter < iNumIter; iIter++ )
{
Int iRefList = iIter % 2; // 預設按照List0--List1--List0--List1的順序進行搜尋
if ( m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE1 || m_pcEncCfg->getFastInterSearchMode()==FASTINTERSEARCH_MODE2 )
{
if( uiCost[0] <= uiCost[1] )
{
iRefList = 1; // *** 為什麼選擇Cost更大的list ???為了提高該List對應的預測性能?
}
else
{
iRefList = 0;
}
}
else if ( iIter == 0 )
{
iRefList = 0;
}
if ( iIter == 0 && !pcCU->getSlice()->getMvdL1ZeroFlag())
{
pcCU->getCUMvField(RefPicList(1-iRefList))->setAllMv( cMv[1-iRefList], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(RefPicList(1-iRefList))->setAllRefIdx( iRefIdx[1-iRefList], ePartSize, uiPartAddr, 0, iPartIdx );
TComYuv* pcYuvPred = &m_acYuvPred[1-iRefList];
motionCompensation ( pcCU, pcYuvPred, RefPicList(1-iRefList), iPartIdx ); // *** 上面getMvdL1ZeroFlag為true時進行了運動補償,此處對于getMvdL1ZeroFlag為false的情況補充進行了運動補償,不太懂為什麼
}
RefPicList eRefPicList = ( iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
if(pcCU->getSlice()->getMvdL1ZeroFlag()) // *** 關鍵代碼,表明getMvdL1ZeroFlag為true對應的是 GPB
{
iRefList = 0; // *** 參考清單強制設為0,說明此時對應的是廣義B幀技術???
eRefPicList = REF_PIC_LIST_0;
}
Bool bChanged = false;
iRefStart = 0;
iRefEnd = pcCU->getSlice()->getNumRefIdx(eRefPicList)-1;
for ( Int iRefIdxTemp = iRefStart; iRefIdxTemp <= iRefEnd; iRefIdxTemp++ )
{
uiBitsTemp = uiMbBits[2] + uiMotBits[1-iRefList];
if ( pcCU->getSlice()->getNumRefIdx(eRefPicList) > 1 )
{
uiBitsTemp += iRefIdxTemp+1;
if ( iRefIdxTemp == pcCU->getSlice()->getNumRefIdx(eRefPicList)-1 )
{
uiBitsTemp--;
}
}
uiBitsTemp += m_auiMVPIdxCost[aaiMvpIdxBi[iRefList][iRefIdxTemp]][AMVP_MAX_NUM_CANDS];
// call ME
xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, true );
xCopyAMVPInfo(&aacAMVPInfo[iRefList][iRefIdxTemp], pcCU->getCUMvField(eRefPicList)->getAMVPInfo());
xCheckBestMVP(pcCU, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp);
if ( uiCostTemp < uiCostBi ) // *** 代價變小,說明搜尋到了更優的Mv,需要更新相關資訊
{
bChanged = true;
cMvBi[iRefList] = cMvTemp[iRefList][iRefIdxTemp];
iRefIdxBi[iRefList] = iRefIdxTemp;
uiCostBi = uiCostTemp;
uiMotBits[iRefList] = uiBitsTemp - uiMbBits[2] - uiMotBits[1-iRefList];
uiBits[2] = uiBitsTemp;
if(iNumIter!=1)
{
// Set motion
pcCU->getCUMvField( eRefPicList )->setAllMv( cMvBi[iRefList], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField( eRefPicList )->setAllRefIdx( iRefIdxBi[iRefList], ePartSize, uiPartAddr, 0, iPartIdx );
TComYuv* pcYuvPred = &m_acYuvPred[iRefList];
motionCompensation( pcCU, pcYuvPred, eRefPicList, iPartIdx ); // *** ME之後若結果更優,則需要補充進行運動補償操作,獲得預測參考塊
}
}
} // for loop-iRefIdxTemp
if ( !bChanged ) // *** 未搜尋到更優的Mv
{
if ( uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1] ) // *** 此時雙向預測的結果已優于單向預測的結果,是以可以提前結束循環
{
xCopyAMVPInfo(&aacAMVPInfo[0][iRefIdxBi[0]], pcCU->getCUMvField(REF_PIC_LIST_0)->getAMVPInfo());
xCheckBestMVP(pcCU, REF_PIC_LIST_0, cMvBi[0], cMvPredBi[0][iRefIdxBi[0]], aaiMvpIdxBi[0][iRefIdxBi[0]], uiBits[2], uiCostBi);
if(!pcCU->getSlice()->getMvdL1ZeroFlag()) // *** 正常B幀(關鍵代碼,此行可以說明getMvdL1ZeroFlag為false 對應的是正常B幀,因為過程體内是對參考清單1進行的操作)
{
xCopyAMVPInfo(&aacAMVPInfo[1][iRefIdxBi[1]], pcCU->getCUMvField(REF_PIC_LIST_1)->getAMVPInfo());
xCheckBestMVP(pcCU, REF_PIC_LIST_1, cMvBi[1], cMvPredBi[1][iRefIdxBi[1]], aaiMvpIdxBi[1][iRefIdxBi[1]], uiBits[2], uiCostBi);
}
}
break;
}
} // for loop-iter
} // if (B_SLICE)
#if AMP_MRG
} //end if bTestNormalMC
#endif
// Clear Motion Field
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvField( TComMvField(), ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvField( TComMvField(), ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd ( cMvZero, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd ( cMvZero, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->setMVPIdxSubParts( -1, REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( -1, REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPIdxSubParts( -1, REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( -1, REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
UInt uiMEBits = 0;
// Set Motion Field_
cMv[1] = mvValidList1;
iRefIdx[1] = refIdxValidList1;
uiBits[1] = bitsValidList1;
uiCost[1] = costValidList1;
#if AMP_MRG
if (bTestNormalMC) // *** 設定最優的預測模式、參考幀id、Mv、Mvd
{
#endif
if ( uiCostBi <= uiCost[0] && uiCostBi <= uiCost[1]) // 雙向預測模式
{
uiLastMode = 2; // ***
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMv( cMvBi[0], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllRefIdx( iRefIdxBi[0], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMv( cMvBi[1], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllRefIdx( iRefIdxBi[1], ePartSize, uiPartAddr, 0, iPartIdx );
TempMv = cMvBi[0] - cMvPredBi[0][iRefIdxBi[0]];
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd ( TempMv, ePartSize, uiPartAddr, 0, iPartIdx );
TempMv = cMvBi[1] - cMvPredBi[1][iRefIdxBi[1]];
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd ( TempMv, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->setInterDirSubParts( 3, uiPartAddr, iPartIdx, pcCU->getDepth(0) );
pcCU->setMVPIdxSubParts( aaiMvpIdxBi[0][iRefIdxBi[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( aaiMvpNum[0][iRefIdxBi[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPIdxSubParts( aaiMvpIdxBi[1][iRefIdxBi[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( aaiMvpNum[1][iRefIdxBi[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
uiMEBits = uiBits[2]; // ***
}
else if ( uiCost[0] <= uiCost[1] ) // *** 前向預測
{
uiLastMode = 0; // ***
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMv( cMv[0], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllRefIdx( iRefIdx[0], ePartSize, uiPartAddr, 0, iPartIdx );
TempMv = cMv[0] - cMvPred[0][iRefIdx[0]];
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd ( TempMv, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->setInterDirSubParts( 1, uiPartAddr, iPartIdx, pcCU->getDepth(0) ); // ***
pcCU->setMVPIdxSubParts( aaiMvpIdx[0][iRefIdx[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( aaiMvpNum[0][iRefIdx[0]], REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
uiMEBits = uiBits[0]; // ***
}
else // *** 後向預測
{
uiLastMode = 1; // ***
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMv( cMv[1], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllRefIdx( iRefIdx[1], ePartSize, uiPartAddr, 0, iPartIdx );
TempMv = cMv[1] - cMvPred[1][iRefIdx[1]];
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd ( TempMv, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->setInterDirSubParts( 2, uiPartAddr, iPartIdx, pcCU->getDepth(0) ); // ***
pcCU->setMVPIdxSubParts( aaiMvpIdx[1][iRefIdx[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( aaiMvpNum[1][iRefIdx[1]], REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
uiMEBits = uiBits[1]; // ***
}
#if AMP_MRG
} // end if bTestNormalMC
#endif
if ( pcCU->getPartitionSize( uiPartAddr ) != SIZE_2Nx2N ) // *** 如果分割類型不是2N x 2N,進行Merge模式評估
{
UInt uiMRGInterDir = 0;
TComMvField cMRGMvField[2];
UInt uiMRGIndex = 0;
UInt uiMEInterDir = 0;
TComMvField cMEMvField[2];
m_pcRdCost->selectMotionLambda( true, 0, pcCU->getCUTransquantBypass(uiPartAddr) );
#if AMP_MRG
// calculate ME cost
Distortion uiMEError = std::numeric_limits<Distortion>::max();
Distortion uiMECost = std::numeric_limits<Distortion>::max();
if (bTestNormalMC)
{
xGetInterPredictionError( pcCU, pcOrgYuv, iPartIdx, uiMEError, m_pcEncCfg->getUseHADME() );
uiMECost = uiMEError + m_pcRdCost->getCost( uiMEBits );
}
#else
// calculate ME cost
Distortion uiMEError = std::numeric_limits<Distortion>::max();
xGetInterPredictionError( pcCU, pcOrgYuv, iPartIdx, uiMEError, m_pcEncCfg->getUseHADME() );
Distortion uiMECost = uiMEError + m_pcRdCost->getCost( uiMEBits );
#endif
// save ME result.
uiMEInterDir = pcCU->getInterDir( uiPartAddr );
TComDataCU::getMvField( pcCU, uiPartAddr, REF_PIC_LIST_0, cMEMvField[0] );
TComDataCU::getMvField( pcCU, uiPartAddr, REF_PIC_LIST_1, cMEMvField[1] );
// find Merge result
Distortion uiMRGCost = std::numeric_limits<Distortion>::max();
xMergeEstimation( pcCU, pcOrgYuv, iPartIdx, uiMRGInterDir, cMRGMvField, uiMRGIndex, uiMRGCost, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand); // Merge模式評估
if ( uiMRGCost < uiMECost ) // Merge模式更優
{
// set Merge result
pcCU->setMergeFlagSubParts ( true, uiPartAddr, iPartIdx, pcCU->getDepth( uiPartAddr ) );
pcCU->setMergeIndexSubParts( uiMRGIndex, uiPartAddr, iPartIdx, pcCU->getDepth( uiPartAddr ) );
pcCU->setInterDirSubParts ( uiMRGInterDir, uiPartAddr, iPartIdx, pcCU->getDepth( uiPartAddr ) );
pcCU->getCUMvField( REF_PIC_LIST_0 )->setAllMvField( cMRGMvField[0], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllMvField( cMRGMvField[1], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_0)->setAllMvd ( cMvZero, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField(REF_PIC_LIST_1)->setAllMvd ( cMvZero, ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->setMVPIdxSubParts( -1, REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( -1, REF_PIC_LIST_0, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPIdxSubParts( -1, REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
pcCU->setMVPNumSubParts( -1, REF_PIC_LIST_1, uiPartAddr, iPartIdx, pcCU->getDepth(uiPartAddr));
}
else // ME + MC模式更優
{
// set ME result
pcCU->setMergeFlagSubParts( false, uiPartAddr, iPartIdx, pcCU->getDepth( uiPartAddr ) );
pcCU->setInterDirSubParts ( uiMEInterDir, uiPartAddr, iPartIdx, pcCU->getDepth( uiPartAddr ) );
pcCU->getCUMvField( REF_PIC_LIST_0 )->setAllMvField( cMEMvField[0], ePartSize, uiPartAddr, 0, iPartIdx );
pcCU->getCUMvField( REF_PIC_LIST_1 )->setAllMvField( cMEMvField[1], ePartSize, uiPartAddr, 0, iPartIdx );
}
}
#if MCTS_ENC_CHECK
if (m_pcEncCfg->getTMCTSSEITileConstraint() && (!checkTMctsMvp(pcCU, iPartIdx)))
{
pcCU->setTMctsMvpIsValid(false);
return;
}
#endif
// MC
motionCompensation ( pcCU, pcPredYuv, REF_PIC_LIST_X, iPartIdx ); // ***得到最終的預測資訊後,再進行MC,擷取最終的預測參考塊,進而進行後續的變換量化熵編碼操作
} // end of for ( Int iPartIdx = 0; iPartIdx < iNumPart; iPartIdx++ )
setWpScalingDistParam( pcCU, -1, REF_PIC_LIST_X );
return;
}