getIntraMPMs()为命名空间PU下的函数,用于获取帧内预测MPM,一共三类:传统预测MPM,多参考行预测MPM,ISP预测MPM,代码和相关注释如下
int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
{
const int numMPMs = NUM_MOST_PROBABLE_MODES; // 6个MPM
const int extendRefLine = (channelType == CHANNEL_TYPE_LUMA) ? pu.multiRefIdx : 0; // 多行预测参考行索引
#if JVET_M0102_INTRA_SUBPARTITIONS
const ISPType ispType = isLuma( channelType ) ? ISPType( pu.cu->ispMode ) : NOT_INTRA_SUBPARTITIONS; // isp划分类型
const bool isHorSplit = ispType == HOR_INTRA_SUBPARTITIONS; // 是否为水平ISP
#endif
{
int numCand = -1; // 记录有效相邻单元模式
int leftIntraDir = PLANAR_IDX, aboveIntraDir = PLANAR_IDX; // 左PU和上PU帧内预测模式,如果pu不存在,默认为Planar模式
const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));
const Position posRT = area.topRight(); // 右上坐标
const Position posLB = area.bottomLeft(); // 左下
// Get intra direction of left PU
const PredictionUnit *puLeft = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, channelType);
if (puLeft && CU::isIntra(*puLeft->cu))
{
leftIntraDir = puLeft->intraDir[channelType];
}
// Get intra direction of above 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 = puAbove->intraDir[channelType];
}
CHECK(2 >= numMPMs, "Invalid number of most probable modes");
const int offset = (int)NUM_LUMA_MODE - 6; ///> 61
const int mod = offset + 3; ///> 64
if (extendRefLine) //#### 多行预测MPM获取 ####
{
int modeIdx = 0; // 左、上pu不同角度模式个数
int angularMode[2] = { 0, 0 }; // 记录相应角度模式
if (leftIntraDir > DC_IDX)
{
angularMode[modeIdx++] = leftIntraDir;
}
if (aboveIntraDir > DC_IDX && aboveIntraDir != leftIntraDir)
{
angularMode[modeIdx++] = aboveIntraDir;
}
if (modeIdx == 0) // 如果相邻块没有角度模式,填充默认:垂直、水平、对角线(34)、负对角线(2,66),26
{
mpm[0] = VER_IDX;
mpm[1] = HOR_IDX;
mpm[2] = 2;
mpm[3] = DIA_IDX;
mpm[4] = VDIA_IDX;
mpm[5] = 26;
}
else if (modeIdx == 1) // 如果有一个角度模式A
{
mpm[0] = angularMode[0]; // A
mpm[1] = ((angularMode[0] + offset) % mod) + 2; // A-1 (2->65)
mpm[2] = ((angularMode[0] - 1) % mod) + 2; // A+1 (65->2,66->3)
mpm[3] = ((angularMode[0] + offset - 1) % mod) + 2; // A-2 (2->64,3->65)
mpm[4] = (angularMode[0] % mod) + 2; // A+2 (64->2, 65->3,66->4)
mpm[5] = ((angularMode[0] + offset - 2) % mod) + 2; // A-3 (2->63,3->64,4->65)
}
else // 如果两个模式都为角度,且各不相同: A 和 B
{
mpm[0] = angularMode[0]; // A
mpm[1] = angularMode[1]; // B
int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1; // 角度模式索引:大
int minCandModeIdx = 1 - maxCandModeIdx; // 角度模式索引:小
if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1) //> 如果两个角度模式向差1
{
mpm[2] = ((angularMode[minCandModeIdx] + offset) % mod) + 2; // 小-1
mpm[3] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2; // 大+1
mpm[4] = ((angularMode[minCandModeIdx] + offset - 1) % mod) + 2; // 小-2
mpm[5] = ( angularMode[maxCandModeIdx] % mod) + 2; // 大+2
}
else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62) //> 两个角度模式相差大于等于62
{
mpm[2] = ((angularMode[minCandModeIdx] - 1) % mod) + 2; // 小+1
mpm[3] = ((angularMode[maxCandModeIdx] + offset) % mod) + 2; // 大-1
mpm[4] = ((angularMode[minCandModeIdx]) % mod) + 2; // 小+2
mpm[5] = ((angularMode[maxCandModeIdx] + offset - 1) % mod) + 2; // 大-2
}
else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2) //> 两个角度模式相差2
{
mpm[2] = ((angularMode[minCandModeIdx] - 1) % mod) + 2; // 小+1
mpm[3] = ((angularMode[minCandModeIdx] + offset) % mod) + 2; // 小-1
mpm[4] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2; // 大+1
mpm[5] = ((angularMode[minCandModeIdx] + offset - 1) % mod) + 2; // 大-1
}
else // 其余情况(A±1,B±1)
{
mpm[2] = ((angularMode[minCandModeIdx] + offset) % mod) + 2;
mpm[3] = ((angularMode[minCandModeIdx] - 1) % mod) + 2;
mpm[4] = ((angularMode[maxCandModeIdx] + offset) % mod) + 2;
mpm[5] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2;
}
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS
else if( ispType != NOT_INTRA_SUBPARTITIONS ) //#### ISP MPM列表 #### 原则:DC模式不进行ISP
{
//default case 首先快速建立默认模式列表(如果,左、上模式都不是角度模式,MPM最终为默认填充的列表)
mpm[0] = PLANAR_IDX; // Planar
if( isHorSplit ) //> 水平划分默认列表顺序:Planar, 水平, 25, 10 ,65, 垂直
{
mpm[1] = HOR_IDX;
mpm[2] = 25;
mpm[3] = 10;
mpm[4] = 65;
mpm[5] = VER_IDX;
}
else //> 垂直划分默认列表顺序:Planar, 垂直, 43, 60, 3, 水平
{
mpm[1] = VER_IDX;
mpm[2] = 43;
mpm[3] = 60;
mpm[4] = 3;
mpm[5] = HOR_IDX;
}
int canonicalMode = mpm[1];
if( leftIntraDir == aboveIntraDir ) //L=A 相邻块只有一个模式
{
numCand = 1;
if( leftIntraDir > DC_IDX ) // 该模式为角度模式 AL(优先AL上下文模式)
{
mpm[0] = leftIntraDir; // AL
mpm[1] = ( ( leftIntraDir + offset ) % mod ) + 2; // AL-1
mpm[2] = ( ( leftIntraDir - 1 ) % mod ) + 2; // AL+1
if( ( isHorSplit && leftIntraDir < DIA_IDX ) || ( !isHorSplit && leftIntraDir >= DIA_IDX ) )
// 水平划分相邻模式也是水平模式,或垂直划分相邻模式是垂直模式
{
mpm[3] = ( ( leftIntraDir + offset - 1 ) % mod ) + 2; // AL-2
mpm[4] = ( leftIntraDir % mod ) + 2; // AL+2
mpm[5] = ( ( leftIntraDir + offset - 2 ) % mod ) + 2; // AL-3
}
else // 其他情况
{
if( isHorSplit ) // 水平划分:水平和5
{
mpm[3] = HOR_IDX;
mpm[4] = 5;
}
else // 垂直划分:垂直和63
{
mpm[3] = VER_IDX;
mpm[4] = VDIA_IDX - 3;
}
mpm[5] = PLANAR_IDX; // Planar
}
}
}
else //L!=A 相邻块模式不同
{
numCand = 2;
if( ( leftIntraDir > DC_IDX ) && ( aboveIntraDir > DC_IDX ) ) // ***左、上都是角度模式***
{
// 依据左、上模式与相关ISP划分方式的模式(水平划分对瓶水平模式、垂直划分对应垂直模式)距离,距离近的排在前面
int distLeftToCanonicalMode = abs( leftIntraDir - canonicalMode );
int distAboveToCanonicalMode = abs( aboveIntraDir - canonicalMode );
mpm[0] = aboveIntraDir;
mpm[1] = leftIntraDir;
if( distLeftToCanonicalMode <= distAboveToCanonicalMode )
{
mpm[0] = leftIntraDir;
mpm[1] = aboveIntraDir;
}
// --- 后面的模式,根据左上模式的数值大小,进行上下偏移,和多行预测类似---
int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;
int minCandModeIdx = 1 - maxCandModeIdx;
if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1 )
{
mpm[2] = ( ( mpm[minCandModeIdx] + offset ) % mod ) + 2;
mpm[3] = ( ( mpm[maxCandModeIdx] - 1 ) % mod ) + 2;
mpm[4] = ( ( mpm[minCandModeIdx] + offset - 1 ) % mod ) + 2;
mpm[5] = ( mpm[maxCandModeIdx] % mod ) + 2;
}
else if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62 )
{
mpm[2] = ( ( mpm[minCandModeIdx] - 1 ) % mod ) + 2;
mpm[3] = ( ( mpm[maxCandModeIdx] + offset ) % mod ) + 2;
mpm[4] = ( ( mpm[minCandModeIdx] ) % mod ) + 2;
mpm[5] = ( ( mpm[maxCandModeIdx] + offset - 1 ) % mod ) + 2;
}
else if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2 )
{
mpm[2] = ( ( mpm[minCandModeIdx] - 1 ) % mod ) + 2;
mpm[3] = ( ( mpm[minCandModeIdx] + offset ) % mod ) + 2;
mpm[4] = ( ( mpm[maxCandModeIdx] - 1 ) % mod ) + 2;
mpm[5] = ( ( mpm[minCandModeIdx] + offset - 1 ) % mod ) + 2;
}
else
{
mpm[2] = ( ( mpm[minCandModeIdx] + offset ) % mod ) + 2;
mpm[3] = ( ( mpm[minCandModeIdx] - 1 ) % mod ) + 2;
mpm[4] = ( ( mpm[maxCandModeIdx] + offset ) % mod ) + 2;
mpm[5] = ( ( mpm[maxCandModeIdx] - 1 ) % mod ) + 2;
}
}
else if( leftIntraDir + aboveIntraDir > 2 ) // *** 如果左、上只有一个是角度模式**dir** (Planar, dir, dir±1, dir±2)
{
//mpm[0] = PLANAR_IDX;
int angMode = leftIntraDir > DC_IDX ? leftIntraDir : aboveIntraDir;
mpm[1] = angMode;
mpm[2] = ( ( angMode + offset ) % mod ) + 2;
mpm[3] = ( ( angMode - 1 ) % mod ) + 2;
mpm[4] = ( ( angMode + offset - 1 ) % mod ) + 2;
mpm[5] = ( ( angMode ) % mod ) + 2;
}
}
}
#endif
else // #### 传统帧内MPM(参考行0)####
{
// 填充默认列表,如果左上模式相同,且不为角度模式,MPM为默认填充列表
mpm[0] = leftIntraDir;
mpm[1] = (mpm[0] == PLANAR_IDX) ? DC_IDX : PLANAR_IDX;
mpm[2] = VER_IDX;
mpm[3] = HOR_IDX;
mpm[4] = VER_IDX - 4;
mpm[5] = VER_IDX + 4;
if (leftIntraDir == aboveIntraDir)
{
numCand = 1;
if (leftIntraDir > DC_IDX) // 左、上相同,为角度,MPM顺序:LA,Planar,DC,LA-1,LA+1,LA-2
{
mpm[0] = leftIntraDir;
mpm[1] = PLANAR_IDX;
mpm[2] = DC_IDX;
mpm[3] = ((leftIntraDir + offset) % mod) + 2;
mpm[4] = ((leftIntraDir - 1) % mod) + 2;
mpm[5] = ((leftIntraDir + offset - 1) % mod) + 2;
}
}
else //L!=A 左、上不相同,
// MPM顺序:前两个为左,上模式。后面如果L、A中有非角度模式,则填充另外一个;如果没有,则填充Planar,DC,然后填充角度偏移模式
{
numCand = 2;
mpm[0] = leftIntraDir;
mpm[1] = aboveIntraDir;
bool maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;
if ((leftIntraDir > DC_IDX) && (aboveIntraDir > DC_IDX))
{
mpm[2] = PLANAR_IDX;
mpm[3] = DC_IDX;
if ((mpm[maxCandModeIdx] - mpm[!maxCandModeIdx] < 63) && (mpm[maxCandModeIdx] - mpm[!maxCandModeIdx] > 1))
{
mpm[4] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
}
else
{
mpm[4] = ((mpm[maxCandModeIdx] + offset - 1) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx]) % mod) + 2;
}
}
else if (leftIntraDir + aboveIntraDir >= 2)
{
mpm[2] = (mpm[!maxCandModeIdx] == PLANAR_IDX) ? DC_IDX : PLANAR_IDX;
mpm[3] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
mpm[4] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx] + offset - 1) % 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;
}
}