VVCには67の角度モードがあります。各PUの予測モードを個別にコーディングする場合、67のモードをコーディングするには7ビットが必要です。したがって、VVCでは最も可能性の高いモードリスト(MPM)の作成方法も使用されます。画像とビデオのコーディングでは、通常、隣接するブロックは強い相関関係があるため、隣接するブロックのイントラ予測モードは、同じまたは類似する可能性が高くなります。したがって、MPMリストは、左側の隣接するPUと上部のフレームに基づいています。隣接PUVVCでは、MPMリストの長さは6です。フレーム内コーディングでは、MPMリストは主に次の2つのプロセスにあります
- マルチリファレンスラインモード(mrl_idx = 1、2)でSATDラフ選択を実行し、MPMリストの6つのイントラ予測モードのみを使用します
- MIPモードの大まかな選択が終わったら、MPMリストの予測モードがレート歪み候補リストにあるかどうかをトラバースします。
具体的な構築手順は次のとおりです。
1.次の図に示すように、それぞれAおよびBで示される、左下および右上の隣接する参照ピクセルを取得します。
2.隣接するピクセルAとBが配置されているPUのイントラ予測モードを取得します。取得方法は次のとおりです。
- 次の条件のいずれかが当てはまる場合、隣接するPUのイントラ予測モードは平面モードに設定されます
- その隣接するPUは利用できません
- 隣接するPUのコーディングモードはイントラコーディングモードではありません
- 隣接するPUはMIPモードです
- 隣接するPUと現在のPUが同じCTUにありません
- それ以外の場合は、隣接するPUのイントラ予測モードを取得します
3.隣接するピクセルAとBがそれぞれAとBとして配置されているPUの予測モードをマークし、MPMリストを次のように作成します。
(1)A = BANDA> B
MPM [0] | 平面 |
MPM [1] | A |
MPM [2] | 2 +((A + 61)%64) |
MPM [3] | 2 +((A − 1)%64) |
MPM [4] | 2 +((A + 60)%64) |
MPM [5] | 2 +(A%64) |
(2)A≠B、A> DCANDB> DC
记MinAB = Min(A、B)、MaxAB = max(A、B)
MPM [0] | 平面 |
MPM [1] | A |
MPM [2] | B |
①maxAB-minAB= 1の場合、
MPM [3] | 2 +((minAB + 61)%64) |
MPM [4] | 2 +((maxAB-1)%64) |
MPM [5] | 2 +((minAB + 60)%64) |
②maxAB-minAB> = 62の場合、
MPM [3] | 2 +((minAB − 1)%64) |
MPM [4] | 2 +((maxAB + 61)%64) |
MPM [5] | 2 +(minAB%64) |
③maxAB-minAB= 2の場合、
MPM [3] | 2 +((minAB − 1)%64) |
MPM [4] | 2 +((minAB + 61)%64) |
MPM [5] | 2 +((maxAB-1)%64) |
④それ以外の場合
MPM [3] | 2 +((minAB + 61)%64) |
MPM [4] | 2 +((minAB-1)%64) |
MPM [5] | 2 +((maxAB + 61)%64) |
(3)A≠B、A> DCまたはB> DC
MPM [0] | 平面 |
MPM [1] | maxAB |
MPM [2] | 2 +((maxAB + 61)%64) |
MPM [3] | 2 +((maxAB-1)%64) |
MPM [4] | 2 +((maxAB + 60)%64) |
MPM [5] | 2+(maxAB%64) |
(4)それ以外の場合。
MPM [0] | 平面 |
MPM [1] | DC |
MPM [2] | 見る |
MPM [3] | HOR |
MPM [4] | SEE-4 |
MPM [5] | 見る+4 |
具体的なコードの実装とコメントは次のとおりです。
int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
{
const int numMPMs = NUM_MOST_PROBABLE_MODES;
{
CHECK(channelType != CHANNEL_TYPE_LUMA, "Not harmonized yet");
int numCand = -1;
int leftIntraDir = PLANAR_IDX, aboveIntraDir = PLANAR_IDX;//将左相邻块和上相邻块预测模式设置为Planar模式
const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));
const Position posRT = area.topRight();//当前块的左上角
const Position posLB = area.bottomLeft();//当前块的右下角
// Get intra direction of left PU
// 获得左相邻PU
const PredictionUnit *puLeft = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, channelType);
if (puLeft && CU::isIntra(*puLeft->cu))
{
leftIntraDir = PU::getIntraDirLuma( *puLeft );//获得左相邻PU的预测模式
}
// Get intra direction of above PU
// 获得上相邻PU
const PredictionUnit *puAbove = pu.cs->getPURestricted(posRT.offset(0, -1), pu, channelType);
if (puAbove && CU::isIntra(*puAbove->cu) && CU::isSameCtu(*pu.cu, *puAbove->cu))
{
aboveIntraDir = PU::getIntraDirLuma( *puAbove );//获得左相邻PU的预测模式
}
CHECK(2 >= numMPMs, "Invalid number of most probable modes");
const int offset = (int)NUM_LUMA_MODE - 6;//61
const int mod = offset + 3;//64
{
mpm[0] = PLANAR_IDX;//Planar
mpm[1] = DC_IDX;//DC
mpm[2] = VER_IDX;//50
mpm[3] = HOR_IDX;//18
mpm[4] = VER_IDX - 4;//46
mpm[5] = VER_IDX + 4;//54
if (leftIntraDir == aboveIntraDir)
{
numCand = 1;
if (leftIntraDir > DC_IDX)
{
mpm[0] = PLANAR_IDX;
mpm[1] = leftIntraDir;
mpm[2] = ((leftIntraDir + offset) % mod) + 2;
mpm[3] = ((leftIntraDir - 1) % mod) + 2;
mpm[4] = ((leftIntraDir + offset - 1) % mod) + 2;
mpm[5] = ( leftIntraDir % mod) + 2;
}
}
else //L!=A
{
numCand = 2;
int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;
if ((leftIntraDir > DC_IDX) && (aboveIntraDir > DC_IDX))
{
mpm[0] = PLANAR_IDX;
mpm[1] = leftIntraDir;
mpm[2] = aboveIntraDir;
maxCandModeIdx = mpm[1] > mpm[2] ? 1 : 2;
int minCandModeIdx = mpm[1] > mpm[2] ? 2 : 1;
if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1)
{
mpm[3] = ((mpm[minCandModeIdx] + offset) % mod) + 2;
mpm[4] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
mpm[5] = ((mpm[minCandModeIdx] + offset - 1) % mod) + 2;
}
else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62)
{
mpm[3] = ((mpm[minCandModeIdx] - 1) % mod) + 2;
mpm[4] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
mpm[5] = ( mpm[minCandModeIdx] % mod) + 2;
}
else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2)
{
mpm[3] = ((mpm[minCandModeIdx] - 1) % mod) + 2;
mpm[4] = ((mpm[minCandModeIdx] + offset) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
}
else
{
mpm[3] = ((mpm[minCandModeIdx] + offset) % mod) + 2;
mpm[4] = ((mpm[minCandModeIdx] - 1) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
}
}
else if (leftIntraDir + aboveIntraDir >= 2)
{
mpm[0] = PLANAR_IDX;
mpm[1] = (leftIntraDir < aboveIntraDir) ? aboveIntraDir : leftIntraDir;
maxCandModeIdx = 1;
mpm[2] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
mpm[3] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
mpm[4] = ((mpm[maxCandModeIdx] + offset - 1) % mod) + 2;
mpm[5] = ( mpm[maxCandModeIdx] % mod) + 2;
}
}
}
for (int i = 0; i < numMPMs; i++)
{
CHECK(mpm[i] >= NUM_LUMA_MODE, "Invalid MPM");
}
CHECK(numCand == 0, "No candidates found");
return numCand;
}
}
getPURestricted関数は、主に場所に応じてPUを取得するためのものです
const PredictionUnit* CodingStructure::getPURestricted( const Position &pos, const PredictionUnit& curPu, const ChannelType _chType ) const
{
const PredictionUnit* pu = getPU( pos, _chType );
// exists same slice and tile pu precedes curPu in encoding order
// (thus, is either from parent CS in RD-search or its index is lower)
//存在 相同slice和Tile PU在编码顺序中先于当前PU
//(因此,在RD搜索中是来自父CS的,或者其索引较低)
const bool wavefrontsEnabled = curPu.cu->slice->getSPS()->getEntropyCodingSyncEnabledFlag();
int ctuSizeBit = floorLog2(curPu.cs->sps->getMaxCUWidth());
int xNbY = pos.x << getChannelTypeScaleX( _chType, curPu.chromaFormat );//相邻PU的左上角位置x
int xCurr = curPu.blocks[_chType].x << getChannelTypeScaleX( _chType, curPu.chromaFormat );//当前PU的左上角位置x
//判断相邻PU和当前PU是否在同一CTU中
bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;
if( pu && CU::isSameSliceAndTile( *pu->cu, *curPu.cu ) && ( pu->cs != curPu.cs || pu->idx <= curPu.idx ) && addCheck )
{
return pu;
}
else
{
return nullptr;
}
}