VVC具有67种角度模式,如果分别对每个PU的预测模式进行编码,则对于67种模式需要7比特来编码,因此VVC中也采用了构建最可能模式列表(most probable mode ,MPM)的方法。在图像和视频编码中,相邻块通常具有较强的相关性,因此相邻块的帧内预测模式相同或者相似的概率较大,因此MPM列表基于左相邻PU和上相邻PU的帧内预测模式构建的,在VVC中,其MPM列表的长度为6。在帧内编码中,MPM列表的主要在以下两个过程
- 进行多参考行模式的SATD粗选中(mrl_idx = 1、2),仅使用MPM列表中的6种帧内预测模式
- 在对MIP模式粗选结束后,遍历MPM列表中的预测模式是否在率失真候选列表中
MPM列表是基于左边相邻块和上方相邻块的帧内模式构造的,如上图所示,构造方法如下:
- 当左边块和上边块不可参考时,其帧内模式默认设置为Planar模式
- 如果左边块和上边块都是非角度模式时
- MPM list -> {Planar,DC,V,H,V--4,V+4}
- 如果左边块和上边块其中一个是角度模式,另一个不是角度模式时
- 设Max是左边块和上边块较大的模式
- MPM list ->{Planar, Max, Max − 1, Max + 1, Max − 2, Max + 2}
- 如果左边块和上边块都是角度模式且它们不同时
- 设Max是左边块和上边块较大的模式
- 设Min是左边块和上边块较小的模式
- 如果Max - Min 的等于 1
- 则 MPM list -> {Planar, Left, Above, Min – 1, Max + 1, Min – 2}
- 如果如果Max - Min 的大于等于62
- 则MPM list ->{Planar, Left, Above, Min + 1, Max – 1, Min + 2}
- 如果如果Max - Min 的等于2
- 则MPM list -> {Planar, Left, Above, Min + 1, Min – 1, Max + 1}
- 否则,MPM list ->{Planar, Left, Above, Min – 1, –Min + 1, Max – 1}
- 如果左边块和上边块时相同的角度模式时
- MPM list –>{Planar, Left, Left − 1, Left + 1, Left − 2, Left + 2}}
具体构建步骤如下:
1、获得左下和右上相邻参考像素,分别记为A和B,如下图所示
2、获取相邻像素A和B所在PU的帧内预测模式,获取方法如下:
- 如果以下条件之一成立,则相邻PU的帧内预测模式设置为Planar模式
- 其相邻PU不可用
- 相邻PU的编码模式不是帧内编码模式
- 相邻PU是MIP模式
- 相邻PU和当前PU不是位于同一个CTU
- 否则,获取其相邻PU的帧内预测模式
3、将相邻像素A、B所处PU的预测模式分别记为A、B,则MPM列表构建如下:
(1)A = B且A > B
MPM[0] | Planar |
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 > DC且B > DC
记MinAB = Min(A,B), MaxAB = max(A,B)
MPM[0] | Planar |
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] | Planar |
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] | Planar |
MPM[1] | DC |
MPM[2] | VER |
MPM[3] | HOR |
MPM[4] | VER-4 |
MPM[5] | VER+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;
}
}