有关于传统预测模式的MPM列表的构造的技术细节以及发展历程我在之前就写过啦,这里就不做介绍了
H.266/VVC相关技术学习笔记:传统帧内预测模式的MPM列表的构造
下面我主要给大家详细剖析一下传统MPM列表的构造函数 getIntraMPMs()
该函数要返回一个最可能模式的候选数目numCand,是在亮度预测的第二轮SATD的时候只对MPM列表中的numCand个最可能的模式进行粗选,其余模式跳过。
代码里有详细的注释,可以根据之前讲过的构造流程一个一个对着代码看。有不懂的地方可以私聊我
// PU tools
//传统角度模式的MPM列表的构造函数
int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
{
//定义最可能模式的数量
const int numMPMs = NUM_MOST_PROBABLE_MODES;
{
#if JVET_N0217_MATRIX_INTRAPRED
CHECK(channelType != CHANNEL_TYPE_LUMA, "Not harmonized yet");
#endif
int numCand = -1;//候选数量初始化为-1
int leftIntraDir = PLANAR_IDX, aboveIntraDir = PLANAR_IDX;//左相邻块的模式初始化为Planar,上相邻块初始化为Planar
const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));//亮度PU的区域
const Position posRT = area.topRight();//当前PU的右上区域位置
const Position posLB = area.bottomLeft();//当前PU的左下区域位置
// 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))
{
#if JVET_N0217_MATRIX_INTRAPRED
leftIntraDir = PU::getIntraDirLuma( *puLeft );//获取左块的预测模式
#endif
}
// 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))
{
#if JVET_N0217_MATRIX_INTRAPRED
aboveIntraDir = PU::getIntraDirLuma( *puAbove );//获取上块的预测模式
#endif
}
//如果MPM的数量小于2,则抛出异常
CHECK(2 >= numMPMs, "Invalid number of most probable modes");
const int offset = (int)NUM_LUMA_MODE - 6;//偏移量,用于MPM列表的构造
const int mod = offset + 3;//运算模,是除数,取余数
{
#if JVET_N0185_UNIFIED_MPM
mpm[0] = PLANAR_IDX;
mpm[1] = DC_IDX;
#endif
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)//如果该模式大于DC模式
{
#if JVET_N0185_UNIFIED_MPM
mpm[0] = PLANAR_IDX;
mpm[1] = leftIntraDir;
mpm[2] = ((leftIntraDir + offset) % mod) + 2;
mpm[3] = ((leftIntraDir - 1) % mod) + 2;
mpm[4] = DC_IDX;
mpm[5] = ((leftIntraDir + offset - 1) % mod) + 2;
#endif
}
}
else //左和上的模式不相等
{
numCand = 2;
#if JVET_N0185_UNIFIED_MPM
//选中概率最大的候选模式索引
int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;//首先从第一个和第二个中选出最大的模式
#endif
if ((leftIntraDir > DC_IDX) && (aboveIntraDir > DC_IDX))//左边模式大于1且上边模式大于1
{
#if JVET_N0185_UNIFIED_MPM
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;//从第二个和第三个中选出最小的模式
#endif
mpm[3] = DC_IDX;
#if JVET_N0185_UNIFIED_MPM
//最大最小候选模式号的差值在(1,63)之间,列表中最后两个模式是根据最小候选和最大候选模式的差值选择的
if ((mpm[maxCandModeIdx] - mpm[minCandModeIdx] < 63) && (mpm[maxCandModeIdx] - mpm[minCandModeIdx] > 1))
#endif
{
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)//如果左模式和上模式之和>=2
{
#if JVET_N0185_UNIFIED_MPM
mpm[0] = PLANAR_IDX;
mpm[1] = (leftIntraDir < aboveIntraDir) ? aboveIntraDir : leftIntraDir;
maxCandModeIdx = 1;
mpm[2] = DC_IDX;
#endif
mpm[3] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
mpm[4] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
mpm[5] = ((mpm[maxCandModeIdx] + offset - 1) % mod) + 2;
}
}
}
//检查MPM列表中是否有无效的列表
for (int i = 0; i < numMPMs; i++)
{
CHECK(mpm[i] >= NUM_LUMA_MODE, "Invalid MPM");
}
CHECK(numCand == 0, "No candidates found");
return numCand;
}
}