H.266/VVC VTM 11-merge候補リストの構築
68292 ワード
VVVCには、MMVD、DMVR、GPMなど、mergeモードに関連するフレーム間技術が多く組み込まれており、mergeの候補リスト生成方式もいくつか修正されている.ここでは、VTMにおけるmerge候補リストの生成に関連するいくつかのコードを記録する.
1、候補運動モード決定
プルトニウム決定候補モードのプロセスは、決定フレーム内予測モードと少し似ている.まず粗選を行い,各候補に1つのcostを得,その後costに基づいて各候補を並べ替える.次にcostを小さい順から大きい順にインストールし,rdo検出を一定回数行い,このモードが最適モードであるか否かを確認する.決定プロセスは、関数EncCu::xCheckRDCostMerge 2 Nx 2 N()において、MMVDおよびCIIPの決定を含む.
2、mergeコンテキスト
MvFieldタイプ変数mvFieldNeighboursなどの符号化された現在の予測ユニットに隣接するブロックの情報を格納するためにMergeCtxクラスが維持され、隣接するブロックの動きベクトルと参照フレームが格納される.2つのメンバー関数setMmvdMergeCandiInfoおよびPredictionUnitは、それぞれMMVDモード動き情報および従来のmergeモード動き情報リストを予測ユニットに抽出するために使用される.
3、mergeコンテキスト構築
mergeモードのコンテキストは関数PU::getInterMergeCandidates()mvFieldNeighboursを取得し、getInterMMVDMergeCandidates()mmvdBaseMvを取得するが、実際にはmmvdBaseMvはmvFieldNeighboursによって導出されるため、主にmvFieldNeighboursの生成過程が記録される.関数構造は比較的明確で、有効なブロックを空間隣接ブロック、時間隣接ブロック、履歴ブロック動き情報、および動き情報の平均値の順にコンテキストに追加し、数量制限に達した場合は早めに終了し、不足した場合は0動きベクトルを追加する.
4、affineコンテキスト構築
従来のmergeモードと同様に、vtmはaffineMergetを構築するためのAffineMergectxクラスのコンテキストを確立した.クラスには同様に動き情報が含まれ、Affineタイプ情報とsubTmvp用の通常のmergeコンテキストが含まれています.関数PU::getaffineMergeCand()を使用して、affine mergeコンテキストを順番に生成します.
1、候補運動モード決定
プルトニウム決定候補モードのプロセスは、決定フレーム内予測モードと少し似ている.まず粗選を行い,各候補に1つのcostを得,その後costに基づいて各候補を並べ替える.次にcostを小さい順から大きい順にインストールし,rdo検出を一定回数行い,このモードが最適モードであるか否かを確認する.決定プロセスは、関数EncCu::xCheckRDCostMerge 2 Nx 2 N()において、MMVDおよびCIIPの決定を含む.
2、mergeコンテキスト
MvFieldタイプ変数mvFieldNeighboursなどの符号化された現在の予測ユニットに隣接するブロックの情報を格納するためにMergeCtxクラスが維持され、隣接するブロックの動きベクトルと参照フレームが格納される.2つのメンバー関数setMmvdMergeCandiInfoおよびPredictionUnitは、それぞれMMVDモード動き情報および従来のmergeモード動き情報リストを予測ユニットに抽出するために使用される.
class MergeCtx
{
public:
MergeCtx() : numValidMergeCand( 0 ), hasMergedCandList( false ) { for( unsigned i = 0; i < MRG_MAX_NUM_CANDS; i++ ) mrgTypeNeighbours[i] = MRG_TYPE_DEFAULT_N; }
~MergeCtx() {}
public:
MvField mvFieldNeighbours [ MRG_MAX_NUM_CANDS << 1 ]; // double length for mv of both lists
uint8_t BcwIdx [ MRG_MAX_NUM_CANDS ];
unsigned char interDirNeighbours[ MRG_MAX_NUM_CANDS ];
MergeType mrgTypeNeighbours [ MRG_MAX_NUM_CANDS ];
int numValidMergeCand;
bool hasMergedCandList;
MotionBuf subPuMvpMiBuf;
MotionBuf subPuMvpExtMiBuf;
MvField mmvdBaseMv[MMVD_BASE_MV_NUM][2];
void setMmvdMergeCandiInfo(PredictionUnit& pu, int candIdx);
bool mmvdUseAltHpelIf [ MMVD_BASE_MV_NUM ];
bool useAltHpelIf [ MRG_MAX_NUM_CANDS ];
void setMergeInfo( PredictionUnit& pu, int candIdx );
};
3、mergeコンテキスト構築
mergeモードのコンテキストは関数PU::getInterMergeCandidates()mvFieldNeighboursを取得し、getInterMMVDMergeCandidates()mmvdBaseMvを取得するが、実際にはmmvdBaseMvはmvFieldNeighboursによって導出されるため、主にmvFieldNeighboursの生成過程が記録される.関数構造は比較的明確で、有効なブロックを空間隣接ブロック、時間隣接ブロック、履歴ブロック動き情報、および動き情報の平均値の順にコンテキストに追加し、数量制限に達した場合は早めに終了し、不足した場合は0動きベクトルを追加する.
void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
int mmvdList,
const int& mrgCandIdx )
{
const CodingStructure &cs = *pu.cs;
const Slice &slice = *pu.cs->slice;
const uint32_t maxNumMergeCand = slice.getPicHeader()->getMaxNumMergeCand();
for (uint32_t ui = 0; ui < maxNumMergeCand; ++ui)
{
mrgCtx.BcwIdx[ui] = BCW_DEFAULT;
mrgCtx.interDirNeighbours[ui] = 0;
mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;
mrgCtx.mvFieldNeighbours[(ui << 1) ].refIdx = NOT_VALID;
mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;
mrgCtx.useAltHpelIf[ui] = false;
}
mrgCtx.numValidMergeCand = maxNumMergeCand;
// compute the location of the current PU
int cnt = 0;
const Position posLT = pu.Y().topLeft();
const Position posRT = pu.Y().topRight();
const Position posLB = pu.Y().bottomLeft();
MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;
// above
const PredictionUnit *puAbove = cs.getPURestricted(posRT.offset(0, -1), pu, pu.chType);
bool isAvailableB1 = puAbove && isDiffMER(pu, *puAbove) && pu.cu != puAbove->cu && CU::isInter(*puAbove->cu);
if (isAvailableB1)
{
miAbove = puAbove->getMotionInfo(posRT.offset(0, -1));
// get Inter Dir
mrgCtx.interDirNeighbours[cnt] = miAbove.interDir;
mrgCtx.useAltHpelIf[cnt] = miAbove.useAltHpelIf;
// get Mv from Above
mrgCtx.BcwIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->BcwIdx : BCW_DEFAULT;
mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miAbove.mv[0], miAbove.refIdx[0]);
if (slice.isInterB())
{
mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miAbove.mv[1], miAbove.refIdx[1]);
}
if (mrgCandIdx == cnt)
{
return;
}
cnt++;
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
//left
const PredictionUnit* puLeft = cs.getPURestricted(posLB.offset(-1, 0), pu, pu.chType);
const bool isAvailableA1 = puLeft && isDiffMER(pu, *puLeft) && pu.cu != puLeft->cu && CU::isInter(*puLeft->cu);
if (isAvailableA1)
{
miLeft = puLeft->getMotionInfo(posLB.offset(-1, 0));
if (!isAvailableB1 || (miAbove != miLeft))
{
if (!isAvailableB1 || (miAbove != miLeft))
{
...
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
// above right
const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
...
//left bottom
const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
...
// early termination
// above left
if ( cnt < 4 )
{
const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
...
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
if (slice.getPicHeader()->getEnableTMVPFlag() && (pu.lumaSize().width + pu.lumaSize().height > 12))
{
//>> MTK colocated-RightBottom
// offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to
Position posRB = pu.Y().bottomRight().offset( -3, -3 );
const PreCalcValues& pcv = *cs.pcv;
Position posC0;
Position posC1 = pu.Y().center();
bool C0Avail = false;
#if JVET_O1143_MV_ACROSS_SUBPIC_BOUNDARY
bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight);
SubPic curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos());
if (curSubPic.getTreatedAsPicFlag())
{
boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() &&
(posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom());
}
if (boundaryCond)
#else
if (((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight))
#endif
{
int posYInCtu = posRB.y & pcv.maxCUHeightMask;
if (posYInCtu + 4 < pcv.maxCUHeight)
{
posC0 = posRB.offset(4, 4);
C0Avail = true;
}
}
Mv cColMv;
int iRefIdx = 0;
int dir = 0;
unsigned uiArrayAddr = cnt;
bool bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, iRefIdx, false ) )
|| getColocatedMVP( pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx, false );
if (bExistMV)
{
dir |= 1;
mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);
}
if (slice.isInterB())
{
bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, iRefIdx, false ) )
|| getColocatedMVP( pu, REF_PIC_LIST_1, posC1, cColMv, iRefIdx, false );
if (bExistMV)
{
dir |= 2;
mrgCtx.mvFieldNeighbours[2 * uiArrayAddr + 1].setMvField(cColMv, iRefIdx);
}
}
if( dir != 0 )
{
bool addTMvp = true;
if( addTMvp )
{
mrgCtx.interDirNeighbours[uiArrayAddr] = dir;
mrgCtx.BcwIdx[uiArrayAddr] = BCW_DEFAULT;
mrgCtx.useAltHpelIf[uiArrayAddr] = false;
if (mrgCandIdx == cnt)
{
return;
}
cnt++;
}
}
}
// early termination
if (cnt == maxNumMergeCand)
{
return;
}
int maxNumMergeCandMin1 = maxNumMergeCand - 1;
if (cnt != maxNumMergeCandMin1)
{
bool isGt4x4 = true;
bool bFound = addMergeHMVPCand(cs, mrgCtx, mrgCandIdx, maxNumMergeCandMin1, cnt
, isAvailableA1, miLeft, isAvailableB1, miAbove
, CU::isIBC(*pu.cu)
, isGt4x4
);
if (bFound)
{
return;
}
}
// pairwise-average candidates
{
if (cnt > 1 && cnt < maxNumMergeCand)
{
mrgCtx.mvFieldNeighbours[cnt * 2].setMvField( Mv( 0, 0 ), NOT_VALID );
mrgCtx.mvFieldNeighbours[cnt * 2 + 1].setMvField( Mv( 0, 0 ), NOT_VALID );
// calculate average MV for L0 and L1 seperately
unsigned char interDir = 0;
mrgCtx.useAltHpelIf[cnt] = (mrgCtx.useAltHpelIf[0] == mrgCtx.useAltHpelIf[1]) ? mrgCtx.useAltHpelIf[0] : false;
for( int refListId = 0; refListId < (slice.isInterB() ? 2 : 1); refListId++ )
{
const short refIdxI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].refIdx;
const short refIdxJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].refIdx;
// both MVs are invalid, skip
if( (refIdxI == NOT_VALID) && (refIdxJ == NOT_VALID) )
{
continue;
}
interDir += 1 << refListId;
// both MVs are valid, average these two MVs
if( (refIdxI != NOT_VALID) && (refIdxJ != NOT_VALID) )
{
const Mv& MvI = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
const Mv& MvJ = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
// average two MVs
Mv avgMv = MvI;
avgMv += MvJ;
roundAffineMv(avgMv.hor, avgMv.ver, 1);
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( avgMv, refIdxI );
}
// only one MV is valid, take the only one MV
else if( refIdxI != NOT_VALID )
{
Mv singleMv = mrgCtx.mvFieldNeighbours[0 * 2 + refListId].mv;
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxI );
}
else if( refIdxJ != NOT_VALID )
{
Mv singleMv = mrgCtx.mvFieldNeighbours[1 * 2 + refListId].mv;
mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxJ );
}
}
mrgCtx.interDirNeighbours[cnt] = interDir;
if( interDir > 0 )
{
cnt++;
}
}
// early termination
if( cnt == maxNumMergeCand )
{
return;
}
}
uint32_t uiArrayAddr = cnt;
int iNumRefIdx = slice.isInterB() ? std::min(slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1)) : slice.getNumRefIdx(REF_PIC_LIST_0);
int r = 0;
int refcnt = 0;
while (uiArrayAddr < maxNumMergeCand)
{
mrgCtx.interDirNeighbours [uiArrayAddr ] = 1;
mrgCtx.BcwIdx [uiArrayAddr ] = BCW_DEFAULT;
mrgCtx.mvFieldNeighbours [uiArrayAddr << 1].setMvField(Mv(0, 0), r);
mrgCtx.useAltHpelIf[uiArrayAddr] = false;
if (slice.isInterB())
{
mrgCtx.interDirNeighbours [ uiArrayAddr ] = 3;
mrgCtx.mvFieldNeighbours [(uiArrayAddr << 1) + 1].setMvField(Mv(0, 0), r);
}
if ( mrgCtx.interDirNeighbours[uiArrayAddr] == 1 && pu.cs->slice->getRefPic(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[uiArrayAddr << 1].refIdx)->getPOC() == pu.cs->slice->getPOC())
{
mrgCtx.mrgTypeNeighbours[uiArrayAddr] = MRG_TYPE_IBC;
}
uiArrayAddr++;
if (refcnt == iNumRefIdx - 1)
{
r = 0;
}
else
{
++r;
++refcnt;
}
}
mrgCtx.numValidMergeCand = uiArrayAddr;
}
4、affineコンテキスト構築
従来のmergeモードと同様に、vtmはaffineMergetを構築するためのAffineMergectxクラスのコンテキストを確立した.クラスには同様に動き情報が含まれ、Affineタイプ情報とsubTmvp用の通常のmergeコンテキストが含まれています.関数PU::getaffineMergeCand()を使用して、affine mergeコンテキストを順番に生成します.
class AffineMergeCtx
{
public:
AffineMergeCtx() : numValidMergeCand( 0 ) { for ( unsigned i = 0; i < AFFINE_MRG_MAX_NUM_CANDS; i++ ) affineType[i] = AFFINEMODEL_4PARAM; }
~AffineMergeCtx() {}
public:
MvField mvFieldNeighbours[AFFINE_MRG_MAX_NUM_CANDS << 1][3]; // double length for mv of both lists
unsigned char interDirNeighbours[AFFINE_MRG_MAX_NUM_CANDS];
EAffineModel affineType[AFFINE_MRG_MAX_NUM_CANDS];
uint8_t BcwIdx[AFFINE_MRG_MAX_NUM_CANDS];
int numValidMergeCand;
int maxNumMergeCand;
MergeCtx *mrgCtx;
MergeType mergeType[AFFINE_MRG_MAX_NUM_CANDS];
};