VTM10.0程式碼學習8:xReconInter()
此係列是為了記錄自己學習VTM10.0的過程和鍛鍊表達能力,主要是從解碼端進行入手。由於本人水平有限,出現的錯誤懇請大家指正,歡迎與大家一起交流進步。
上一篇博文(VTM10.0程式碼學習7)的第1大節提到xReconInter(),這個函式是用來重構幀間模式或IBC模式下的CU塊,本篇博文就來細說它。
1. xReconInter()
if( cu.geoFlag )//如果當前CU開啟GPM
{
m_pcInterPred->motionCompensationGeo( cu, m_geoMrgCtx );//在開啟GPM下進行運動補償
PU::spanGeoMotionInfo( *cu.firstPU, m_geoMrgCtx, cu.firstPU->geoSplitDir, cu.firstPU->geoMergeIdx0, cu.firstPU->geoMergeIdx1 );//向cs中加入當前PU對應的MotionInfo
}
else
{
m_pcIntraPred->geneIntrainterPred(cu);//進行CIIP中的幀內預測部分
// inter prediction
const bool luma = cu.Y().valid();//為1表示當前CU的亮度分量存在
const bool chroma = isChromaEnabled(cu.chromaFormat) && cu.Cb().valid();//為1表示當前CU的色度分量存在
//進行運動補償,獲取幀間預測值
if (luma && (chroma || !isChromaEnabled(cu.chromaFormat)))
{
m_pcInterPred->motionCompensation(cu);
}
else//在IBC的情況下會進入else
{
m_pcInterPred->motionCompensation(cu, REF_PIC_LIST_0, luma, chroma);
}
}
這段是用來進行運動補償,獲取當前CU的幀間預測值。
第一個if分支:如果判斷條件為真,表示在開啟GPM的情況下進行運動補償,這裡不再展開;判斷條件為假進入非GPM情況下的運動補償。
geneIntrainterPred():用來進行CIIP中的幀內預測部分
luma:為1表示當前CU的亮度分量存在
chroma:為1表示當前CU的色度分量存在
第二個if分支:如果當前CU亮度不存在,或在視訊源不是單通道的情況下色度不存在,表示當前CU是IBC預測。此時指明是list 0的單向預測,並將luma和chroma變數傳入函式motionCompensation()。如果判斷條件為真(IBC預測或幀間預測),就直接呼叫函式motionCompensation()進行運動補償,此時luma和chroma預設為1。有關函式motionCompensation()的講解,參考第2大節
if (cu.Y().valid())
{
bool isIbcSmallBlk = CU::isIBC(cu) && (cu.lwidth() * cu.lheight() <= 16);
CU::saveMotionInHMVP( cu, isIbcSmallBlk );//更新HMVP候選列表
}
如果當前CU不是亮度畫素點數小於等於16的開啟IBC預測的CU,就更新HMVP候選列表
if (cu.firstPU->ciipFlag)//當前CU開啟CIIP模式
{
if (cu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())//如果開啟LMCS
{
cu.cs->getPredBuf(*cu.firstPU).Y().rspSignal(m_pcReshape->getFwdLUT());//進行LMCS中的luma mapping
}
m_pcIntraPred->geneWeightedPred(COMPONENT_Y, cu.cs->getPredBuf(*cu.firstPU).Y(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Y, 0));//進行亮度分量的CIIP的幀內幀間預測加權
if (isChromaEnabled(cu.chromaFormat) && cu.chromaSize().width > 2)
{
m_pcIntraPred->geneWeightedPred(COMPONENT_Cb, cu.cs->getPredBuf(*cu.firstPU).Cb(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cb, 0));
m_pcIntraPred->geneWeightedPred(COMPONENT_Cr, cu.cs->getPredBuf(*cu.firstPU).Cr(), *cu.firstPU, m_pcIntraPred->getPredictorPtr2(COMPONENT_Cr, 0));
}
}
這段用來進行CIIP的幀內幀間預測加權
如果開啟LMCS,還要對幀間預測值進行luma mapping。如果對這裡進行luma mapping有疑惑的話,推薦看一下JVET-S2002 3.7.3小節的開頭以及圖54。
xDecodeInterTexture(cu);//獲得當前CU的畫素殘差值
具體參考1.1小節
//下面進行獲取畫素重建值
if (cu.rootCbf)//如果碼流中存在當前CU有關殘差的語法元素
{
if (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
{
if (!cu.firstPU->ciipFlag && !CU::isIBC(cu))
cs.getPredBuf(cu).get(COMPONENT_Y).rspSignal(m_pcReshape->getFwdLUT());//進行LMCS中的luma mapping
}
cs.getResiBuf( cu ).reconstruct( cs.getPredBuf( cu ), cs.getResiBuf( cu ), cs.slice->clpRngs() );//將畫素預測值與畫素殘差值相加存在畫素殘差值中
cs.getRecoBuf( cu ).copyFrom ( cs.getResiBuf( cu ) );//將畫素殘差值拷貝給畫素重建值
}
else
{//如果碼流中不存在當前CU有關殘差的語法元素
cs.getRecoBuf(cu).copyClip(cs.getPredBuf(cu), cs.slice->clpRngs());//將畫素預測值拷貝給畫素重建值
if (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag() && !cu.firstPU->ciipFlag && !CU::isIBC(cu))
{//如果需要,進行LMCS中的luma mapping
cs.getRecoBuf(cu).get(COMPONENT_Y).rspSignal(m_pcReshape->getFwdLUT());
}
}
if分支:如果判斷條件為真則存在殘差,將畫素預測值與畫素殘差值相加存在畫素殘差值中,再將畫素殘差值拷貝給畫素重建值;如果判斷條件為假,則直接將畫素預測值拷貝給畫素重建值。如果需要,還會對亮度分量進行luma mapping
cs.setDecomp(cu);//設定cs中的m_isDecomp
這段用來設定cs中的m_isDecomp,m_isDecomp表示當前幀每個畫素點是否已重構
1.1 xDecodeInterTexture()
這個函式用來獲得當前CU的畫素殘差值
const uint32_t uiNumVaildComp = getNumberValidComponents(cu.chromaFormat);
if (cu.colorTransform)//當開啟ACT的情況
{
}
else
{
//分Y、Cb、Cr分量進行
for (uint32_t ch = 0; ch < uiNumVaildComp; ch++)
{
const ComponentID compID = ComponentID(ch);
//遍歷當前CU內所有TU
for( auto& currTU : CU::traverseTUs( cu ) )
{
CodingStructure &cs = *cu.cs;
const Slice &slice = *cs.slice;
if (slice.getLmcsEnabledFlag() && slice.getPicHeader()->getLmcsChromaResidualScaleFlag() && (compID == COMPONENT_Y) && (currTU.cbf[COMPONENT_Cb] || currTU.cbf[COMPONENT_Cr]))
{//如果需要,設定LMCS中chroma scaling的縮放係數
const CompArea &areaY = currTU.blocks[COMPONENT_Y];
int adj = m_pcReshape->calculateChromaAdjVpduNei(currTU, areaY);
currTU.setChromaAdj(adj);
}
xDecodeInterTU( currTU, compID );//獲取當前TU對應分量的畫素殘差值
}
}
}
if分支:如果判斷條件為真,表示在開啟ACT的情況下獲取畫素殘差值,這裡不再展開;判斷條件為假,在非ACT情況下獲取畫素殘差值
在else分支中,分三個分量遍歷當前CU內所有TU,呼叫xDecodeInterTU()函式獲取當前TU對應分量的畫素殘差值。這個函式中的內容與幀內預測情況下獲取畫素殘差值方式一致,不再贅述。如果需要,還要設定LMCS中chroma scaling的縮放係數,在xDecodeInterTU()裡會用到
2. motionCompensation()
for( auto &pu : CU::traversePUs( cu ) )
{
PelUnitBuf predBuf = cu.cs->getPredBuf( pu );//獲取在cs中儲存的當前PU的畫素預測值地址
pu.mvRefine = true;
motionCompensation(pu, predBuf, eRefPicList, luma, chroma);//進行運動補償,獲取當前PU的幀間預測值
pu.mvRefine = false;
}
這段用來遍歷當前CU內所有PU,進行運動補償(雖然CU內只有一個PU)
predBuf:當前PU的畫素預測值地址
mvRefine:為1表示當前PU允許開啟DMVR
motionCompensation():進行運動補償,獲取當前PU的幀間預測值。此函式是過載的,雖然函式名相同實則不同。稱此函式為PU級motionCompensation(),具體參考2.1小節
2.1 PU級motionCompensation()
由於這個函式是在編碼端和解碼端都會呼叫的函式,為了方便理解,下面省略只在編碼端以及只在IBC預測下才會遇到的情況
int refIdx0 = pu.refIdx[REF_PIC_LIST_0];//參考幀在reference list 0的Index
int refIdx1 = pu.refIdx[REF_PIC_LIST_1];//參考幀在reference list 1的Index
//有關WP的引數
const WPScalingParam *wp0 = pu.cs->slice->getWpScaling(REF_PIC_LIST_0, refIdx0);
const WPScalingParam *wp1 = pu.cs->slice->getWpScaling(REF_PIC_LIST_1, refIdx1);
bool bioApplied = false;//為1表示開啟BDOF
//下面判斷是否開啟BDOF
if (pu.cs->sps->getBDOFEnabledFlag() && (!pu.cs->picHeader->getDisBdofFlag()))
{
}
bool refIsScaled = ( refIdx0 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_0, refIdx0 )->isRefScaled( pu.cs->pps ) ) ||
( refIdx1 < 0 ? false : pu.cu->slice->getRefPic( REF_PIC_LIST_1, refIdx1 )->isRefScaled( pu.cs->pps ) );//參考幀是否進行過縮放
bioApplied = refIsScaled ? false : bioApplied;
bool dmvrApplied = false;//為1表示開啟DMVR
dmvrApplied = (pu.mvRefine) && PU::checkDMVRCondition(pu);
if ((pu.lumaSize().width > MAX_BDOF_APPLICATION_REGION || pu.lumaSize().height > MAX_BDOF_APPLICATION_REGION) && pu.mergeType != MRG_TYPE_SUBPU_ATMVP && (bioApplied && !dmvrApplied))
{//如果開啟BDOF後PU過大,需要劃分成SubPU進行運動補償
xSubPuBio(pu, predBuf, eRefPicList, predBufWOBIO);
}
else
{
if (pu.mergeType != MRG_TYPE_DEFAULT_N && pu.mergeType != MRG_TYPE_IBC)
{//如果當前PU的MVP由ATMVP得到,則劃分成SubPU進行運動補償
xSubPuMC(pu, predBuf, eRefPicList, luma, chroma);
}
else if (xCheckIdenticalMotion(pu))
{//如果list0和list1的預測一樣,採用單向預測
xPredInterUni(pu, REF_PIC_LIST_0, predBuf, false, false, luma, chroma);
}
else
{
xPredInterBi(pu, predBuf, luma, chroma, predBufWOBIO);//進行雙向預測,如果list0或list1的預測不可用則變成單向預測
}
}
refIdx0:參考幀在reference list 0的Index
refIdx1:參考幀在reference list 1的Index
wp0和wp1:都是有關WP的引數
bioApplied:為1表示開啟BDOF,後面的if分支用來判斷是否開啟BDOF,由於過長就不貼出來了
refIsScaled:為1表示參考幀進行過縮放,則不能開啟BDOF
dmvrApplied:為1表示開啟DMVR,後面會進行判斷
第二個if分支:如果判斷條件為真,表示開啟BDOF後PU過大,需要劃分成SubPU進行運動補償,呼叫函式xSubPuBio();判斷條件為假,進入其餘情況
第三個if分支:如果判斷條件為真,表示當前PU的MVP由ATMVP得到,則劃分成SubPU進行運動補償,呼叫函式xSubPuMC();如果elseif的判斷條件為真,表示list0和list1的預測一樣,採用單向預測呼叫函式xPredInterUni(),這個稍後會講;進入else,呼叫函式xPredInterBi(),具體參考第3大節
3. xPredInterBi()
這個函式進行雙向預測,如果list0或list1的預測不可用則變成單向預測。前面的設定和2.1小節類似,這裡不再贅述
for (uint32_t refList = 0; refList < NUM_REF_PIC_LIST_01; refList++)
{
if( pu.refIdx[refList] < 0)
{
continue;
}
RefPicList eRefPicList = (refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0);
PelUnitBuf pcMbBuf = ( pu.chromaFormat == CHROMA_400 ?
PelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[refList][0], pcYuvPred.Y())) :
PelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[refList][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[refList][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[refList][2], pcYuvPred.Cr())) );
if (pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0)
{//如果兩個方向都可以進行預測
if (dmvrApplied)
{//如果開啟DMVR,跳過這裡的預測,後面會呼叫xProcessDMVR
continue;
}
xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
}
else
{
if( ( (pps.getUseWP() && slice.getSliceType() == P_SLICE) || (pps.getWPBiPred() && slice.getSliceType() == B_SLICE) ) )
{//如果使用WP
xPredInterUni(pu, eRefPicList, pcMbBuf, true, bioApplied, luma, chroma);
}
else
{
xPredInterUni(pu, eRefPicList, pcMbBuf, pu.cu->geoFlag, bioApplied, luma, chroma);
}
}
}
for迴圈:分別對list0和list1進行單向預測
第一個if分支:如果refIdx[refList]小於0,表示當前list的預測不可用,跳過當前list的單向預測
eRefPicList:表示當前list是list0還是list1
pcMbBuf:存放當前list的單向預測值
第二個if分支:分不同情況呼叫函式xPredInterUni()進行單向預測,只是傳入引數的不同。在開啟DMVR的情況下,跳過這裡的預測,後面會呼叫函式xProcessDMVR()。有關xPredInterUni()的講解,參考3.1小節
//list 0的單向預測值
CPelUnitBuf srcPred0 = ( pu.chromaFormat == CHROMA_400 ?
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[0][0], pcYuvPred.Y())) :
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[0][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[0][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[0][2], pcYuvPred.Cr())) );
//list 1的單向預測值
CPelUnitBuf srcPred1 = ( pu.chromaFormat == CHROMA_400 ?
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[1][0], pcYuvPred.Y())) :
CPelUnitBuf(pu.chromaFormat, PelBuf(m_acYuvPred[1][0], pcYuvPred.Y()), PelBuf(m_acYuvPred[1][1], pcYuvPred.Cb()), PelBuf(m_acYuvPred[1][2], pcYuvPred.Cr())) );
const bool lumaOnly = luma && !chroma;//為1表示只進行了亮度分量的預測
const bool chromaOnly = !luma && chroma;//為1表示只進行了色度分量的預測
srcPred0:list 0的單向預測值
srcPred1:list 1的單向預測值
lumaOnly:為1表示只進行了亮度分量的預測
chromaOnly:為1表示只進行了色度分量的預測
if( !pu.cu->geoFlag && (!dmvrApplied) && (!bioApplied) && pps.getWPBiPred() && slice.getSliceType() == B_SLICE && pu.cu->BcwIdx == BCW_DEFAULT)
{
xWeightedPredictionBi( pu, srcPred0, srcPred1, pcYuvPred, m_maxCompIDToPred, lumaOnly, chromaOnly );//進行雙向WP,當list0或list1的預測無效時會變成單向WP
}
else if( !pu.cu->geoFlag && pps.getUseWP() && slice.getSliceType() == P_SLICE )
{
xWeightedPredictionUni( pu, srcPred0, REF_PIC_LIST_0, pcYuvPred, -1, m_maxCompIDToPred, lumaOnly, chromaOnly );//進行單向WP
}
else
{//如果不進行WP
if (dmvrApplied)
{//如果開啟DMVR
xProcessDMVR(pu, pcYuvPred, slice.clpRngs(), bioApplied);//進行DMVR
}
else
{//一般情況下進行list0和list1的預測值平均,當list0或list1的預測值無效時就變為直接拷貝。裡面包括BDOF和BCW的實現
xWeightedAverage( pu, srcPred0, srcPred1, pcYuvPred, slice.getSPS()->getBitDepths(), slice.clpRngs(), bioApplied, lumaOnly, chromaOnly, yuvPredTmp );
}
}
第一個if分支:如果判斷條件為真,表示進行雙向WP,呼叫函式xWeightedPredictionBi();如果elseif的判斷條件為真,表示進行單向WP,呼叫函式xWeightedPredictionUni();如果進入else,表示不進行WP
第二個if分支:如果判斷條件為真,表示開啟DMVR,呼叫函式xProcessDMVR();判斷條件為假,呼叫函式xWeightedAverage()
xWeightedAverage():一般情況下進行list0和list1的預測值平均,當list0或list1的預測值無效時就變為直接拷貝。裡面包括BDOF和BCW的實現。具體參考3.2小節
3.1 xPredInterUni()
先來看一下函式的兩個形參:
-
bioApplied:為1表示開啟BDOF
-
bi:這個暫且不清楚具體意義,只能知道以下情況時為1
- 開啟WP
- 開啟GPM
- list0和list1的預測值皆有效
int iRefIdx = pu.refIdx[eRefPicList];//參考幀在參考幀列表中的Index
bool isIBC = false;
if (CU::isIBC(*pu.cu))
{
isIBC = true;
}
isIBC:為1表示進行IBC預測
Mv mv[3];
//獲取MV
if( pu.cu->affine )
{
mv[0] = pu.mvAffi[eRefPicList][0];
mv[1] = pu.mvAffi[eRefPicList][1];
mv[2] = pu.mvAffi[eRefPicList][2];
}
else
{
mv[0] = pu.mv[eRefPicList];
}
//進行MV裁剪
if( !pu.cu->affine )
{
if( !isIBC && pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->isRefScaled( pu.cs->pps ) == false )
{
if( !pu.cs->pps->getWrapAroundEnabledFlag() )
{
clipMv( mv[0], pu.cu->lumaPos(), pu.cu->lumaSize(), sps, *pu.cs->pps );
}
}
}
mv:儲存著當前PU的MV,如果需要,還要進行裁剪
for( uint32_t comp = COMPONENT_Y; comp < pcYuvPred.bufs.size() && comp <= m_maxCompIDToPred; comp++ )
{
const ComponentID compID = ComponentID( comp );
if (compID == COMPONENT_Y && !luma)
{
continue;
}
if (compID != COMPONENT_Y && !chroma)
{
continue;
}
if ( pu.cu->affine )//如果開啟affine模式
{
m_iRefListIdx = eRefPicList;
bool genChromaMv = (!luma && chroma && compID == COMPONENT_Cb);//為1表示要生成色度的子塊MV
//在開啟affine的情況下,獲取相應分量的單向預測值(包括PROF的實現)
xPredAffineBlk( compID, pu, pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->unscaledPic, mv, pcYuvPred, bi, pu.cu->slice->clpRng( compID ), genChromaMv, pu.cu->slice->getScalingRatio( eRefPicList, iRefIdx ));
}
else
{
if (isIBC)//如果開啟IBC預測
{
xPredInterBlk(compID, pu, pu.cu->slice->getPic(), mv[0], pcYuvPred, bi, pu.cu->slice->clpRng(compID),
bioApplied, isIBC);
}
else
{
//獲取相應分量的單向預測值
xPredInterBlk( compID, pu, pu.cu->slice->getRefPic( eRefPicList, iRefIdx )->unscaledPic, mv[0], pcYuvPred, bi, pu.cu->slice->clpRng( compID ), bioApplied, isIBC, pu.cu->slice->getScalingRatio( eRefPicList, iRefIdx ) );
}
}
}
for迴圈:分Y、Cb、Cr獲取單向預測值
第一個if分支和第二個if分支:如果對應分量不需要獲取預測值,則跳過當前迴圈
第三個if分支:如果判斷條件為真,表示開啟affine,呼叫函式xPredAffineBlk()獲取相應分量的單向預測值。函式裡面包括PROF的實現,這裡不再展開。判斷條件為假,表示進入非affine的情況,呼叫函式xPredInterBlk()獲取相應分量的單向預測值。開啟IBC與不開啟IBC時傳入函式的實參有區別,函式具體參考第4大節
3.2 xWeightedAverage()
const int iRefIdx0 = pu.refIdx[0];
const int iRefIdx1 = pu.refIdx[1];
if( iRefIdx0 >= 0 && iRefIdx1 >= 0 )
{
}
else if( iRefIdx0 >= 0 && iRefIdx1 < 0 )
{
if( pu.cu->geoFlag )
{
pcYuvDst.copyFrom( pcYuvSrc0 );
}
else
{
pcYuvDst.copyClip( pcYuvSrc0, clpRngs, lumaOnly, chromaOnly );
}
}
else if( iRefIdx0 < 0 && iRefIdx1 >= 0 )
{
if( pu.cu->geoFlag )
{
pcYuvDst.copyFrom( pcYuvSrc1 );
}
else
{
pcYuvDst.copyClip( pcYuvSrc1, clpRngs, lumaOnly, chromaOnly );
}
}
iRefIdx0和iRefIdx1:參考幀在參考幀列表中的Index,如果值為-1表示不存在相應方向的幀間預測
if分支:如果判斷條件為真,表示list0和list1的預測值都有效,具體參考3.2.1小節;判斷條件為假,表示list0和list1只有其中一個的預測值有效,直接將list0或list1的單向預測值拷貝給當前PU對應的畫素預測值。如果不開啟GPM的話,單向預測值需要經過裁剪
3.2.1 list0和list1的預測值都有效
if( pu.cu->BcwIdx != BCW_DEFAULT && !pu.ciipFlag )
{//如果開啟BCW
pcYuvDst.addWeightedAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, pu.cu->BcwIdx, chromaOnly, lumaOnly);
return;
}
if (bioApplied)//如果開啟BDOF
{
}
if (!bioApplied && (lumaOnly || chromaOnly))
{
pcYuvDst.addAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, chromaOnly, lumaOnly);
}
else
{
pcYuvDst.addAvg(pcYuvSrc0, pcYuvSrc1, clpRngs, bioApplied);//如果開啟BDOF只用對色度進行list0和list1的預測值平均
}
第一個if分支:如果判斷條件為真,表示開啟BCW,對list0和list1的單向預測值加權並跳過之後的語句
第二個if分支:如果判斷條件為真,表示開啟BDOF,裡面呼叫函式applyBiOptFlow()進行BDOF,這裡不再展開
第三個if分支:這裡大致的意思是,如果開啟BDOF只用對色度進行list0和list1的預測值平均,亮度已經在第二個if分支中處理過了
4. xPredInterBlk()
為了方便講解和理解,這裡省略參考幀縮放、DMVR、BDOF的情況
const ChromaFormat chFmt = pu.chromaFormat;
const bool rndRes = !bi;
bool useAltHpelIf = pu.cu->imv == IMV_HPEL;//為1表示MVD的傳輸精度是半畫素
int shiftHor = MV_FRACTIONAL_BITS_INTERNAL + ::getComponentScaleX(compID, chFmt);
int shiftVer = MV_FRACTIONAL_BITS_INTERNAL + ::getComponentScaleY(compID, chFmt);
shiftHor:對MV的水平分量要縮小(2的shiftHor次)倍。一方面是因為儲存的是1/16畫素精度,獲取預測畫素值用的是整畫素精度;另一方面是因為儲存的是亮度MV,獲取色度MV還要根據色度取樣格式進行縮小。
shiftVer:與shiftHor類似,處理的是MV的垂直分量
bool wrapRef = false;//為1表示是horizontal wrap-around運動補償
Mv mv(_mv);
if( !isIBC && refPic->isWrapAroundEnabled( pu.cs->pps ) )
{
wrapRef = wrapClipMv( mv, pu.blocks[0].pos(), pu.blocks[0].size(), pu.cs->sps, pu.cs->pps );
}
wrapRef:為1表示是horizontal wrap-around運動補償
int xFrac = mv.hor & ((1 << shiftHor) - 1);//MV的水平方向的小數部分
int yFrac = mv.ver & ((1 << shiftVer) - 1);//MV的垂直方向的小數部分
if (isIBC)
{
xFrac = yFrac = 0;
}
PelBuf & dstBuf = dstPic.bufs[compID];
unsigned width = dstBuf.width;
unsigned height = dstBuf.height;
xFrac:MV的水平方向的小數部分
yFrac:MV的垂直方向的小數部分
CPelBuf refBuf;//儲存著參考幀中相應的參考塊
{
Position offset = pu.blocks[compID].pos().offset(mv.getHor() >> shiftHor, mv.getVer() >> shiftVer);//參考塊在參考幀中的位置
refBuf = refPic->getRecoBuf(CompArea(compID, chFmt, offset, pu.blocks[compID].size()), wrapRef);//獲取參考幀中相應的參考塊
}
refBuf:儲存著參考幀中相應的參考塊
getRecoBuf():獲取參考幀中相應的參考塊
if (yFrac == 0)
{
m_if.filterHor(compID, (Pel *) refBuf.buf, refBuf.stride, dstBuf.buf, dstBuf.stride, backupWidth, backupHeight,
xFrac, rndRes, chFmt, clpRng, bilinearMC, bilinearMC, useAltHpelIf);
}
else if (xFrac == 0)
{
m_if.filterVer(compID, (Pel *) refBuf.buf, refBuf.stride, dstBuf.buf, dstBuf.stride, backupWidth, backupHeight,
yFrac, true, rndRes, chFmt, clpRng, bilinearMC, bilinearMC, useAltHpelIf);
}
else
{
PelBuf tmpBuf = PelBuf(m_filteredBlockTmp[0][compID], pu.blocks[compID]);
int vFilterSize = isLuma(compID) ? NTAPS_LUMA : NTAPS_CHROMA;
if (bilinearMC)
{
vFilterSize = NTAPS_BILINEAR;
}
m_if.filterHor(compID, (Pel *) refBuf.buf - ((vFilterSize >> 1) - 1) * refBuf.stride, refBuf.stride, tmpBuf.buf,
tmpBuf.stride, backupWidth, backupHeight + vFilterSize - 1, xFrac, false, chFmt, clpRng,
bilinearMC, bilinearMC, useAltHpelIf);
m_if.filterVer(compID, (Pel *) tmpBuf.buf + ((vFilterSize >> 1) - 1) * tmpBuf.stride, tmpBuf.stride, dstBuf.buf,
dstBuf.stride, backupWidth, backupHeight, yFrac, false, rndRes, chFmt, clpRng, bilinearMC,
bilinearMC, useAltHpelIf);
}
這段可以理解成,根據MV的小數部分對參考塊進行濾波,獲取單向預測值。
相關文章
- GET程式碼學習
- 提升碼農90%學習效率的8大學習工具
- 全部HTML程式碼學習HTML
- Laravel核心程式碼學習 — 路由Laravel路由
- 學習VIORB程式碼記錄ORB
- 深度學習程式碼積累深度學習
- Laravel核心程式碼學習 -- 路由Laravel路由
- C++學習程式碼--sizeofC++
- ROS_DWA--程式碼學習ROS
- 程式碼8
- 3/8學習程序
- java學習程序8Java
- Python學習筆記—程式碼Python筆記
- 對比學習 ——simsiam 程式碼解析。
- Laravel核心程式碼學習--HTTP核心LaravelHTTP
- 如何學習以太坊的程式碼
- Java學習之程式碼優化Java優化
- Laravel核心程式碼學習 -- Database QueryBuilderLaravelDatabaseUI
- 【深度學習】研究Fast rcnn程式碼深度學習ASTCNN
- 前端學習程式碼目錄存放前端
- surfer 8 scripter 學習筆記(1)指令碼物件模型筆記指令碼物件模型
- 李沐動手學習深度學習 錨框部分程式碼解析深度學習
- 五分鐘學習 Java 8 的流程式設計Java程式設計
- Laravel核心程式碼學習 — 觀察者模式Laravel模式
- Laravel核心程式碼學習 — 事件系統Laravel事件
- 動軟程式碼生成器學習
- Laravel核心程式碼學習 -- 事件系統Laravel事件
- 如何學習用Java編寫程式碼?Java
- Java學習之程式碼最佳化Java
- Laravel核心程式碼學習 -- 響應 ResponseLaravel
- Laravel核心程式碼學習 -- 請求RequestLaravel
- Laravel核心程式碼學習 -- 控制器Laravel
- FPGA學習中的程式碼閱讀FPGA
- Dagger2 生成程式碼學習
- PHP程式碼審計學習總結PHP
- 深度學習高頻手撕程式碼深度學習
- k8s學習K8S
- JAVA8-stream學習Java