在最新的VVC的参考软件VTM5.0中新加入的技术Mip亮度预测模式有其独立于传统的68种预测模式的MPM列表,而其MPM列表的构造也不同于传统的MPM列表构造。今天来详细讲一下关于Mip模式的MPM列表的构造函数。
传统亮度预测模式的MPM列表的构造函数的代码之前我已经讲过啦,这里直接给出链接:
H.266/VVC代码学习笔记6:传统的MPM列表构造函数——getIntraMPMs()
Mip模式的技术全称是ALWIP技术,关于ALWIP技术的具体技术细节我在之前的博客也深入讲过啦,这里直接给出链接:
H.266/VVC代码学习笔记4:带你深入解析VTM5.0中的最新技术ALWIP
接下来就是关于Mip模式的MPM列表构造函数的代码详解,我都加了非常详细的注释,方便大家学习
//构造MIp模式的MPM列表
int PU::getMipMPMs(const PredictionUnit &pu, unsigned *mpm)
{
const CompArea &area = pu.block( getFirstComponentOfChannel( CHANNEL_TYPE_LUMA ) );//获取当前亮度PU区域
const Position &pos = area.pos();//获取当前亮度PU区域的位置
bool realMode = false;
// Get intra mode of left PU
//获取左边PU块的帧内模式
int leftIntraMode = -1;
//获取指向左块的指针
const PredictionUnit *puLeft = pu.cs->getPURestricted( pos.offset( -1, 0 ), pu, CHANNEL_TYPE_LUMA );
//如果左块存在且左块的CU的模式是帧内预测模式
if( puLeft && CU::isIntra( *puLeft->cu ) )
{
//如果左块是Mip模式
if( PU::isMIP( *puLeft ) )
{
//如果左块的Mip尺寸等级和当前PU的MIp的尺寸等级相同,否则leftIntraMode默认为-1
if (getMipSizeId(*puLeft) == getMipSizeId(pu))
{
leftIntraMode = puLeft->intraDir[CHANNEL_TYPE_LUMA];//leftIntraMode就保存左块的帧内亮度Mip模式
realMode = true;
}
}
else//如果左块的模式不是Mip模式,则将左块的传统角度模式通过转换列表转换到Mip模式再赋给leftIntraMode
{
leftIntraMode = g_mapAngular33ToMip[getMipSizeId(pu)][g_intraMode65to33AngMapping[puLeft->intraDir[CHANNEL_TYPE_LUMA]]];
}
}
// Get intra mode of above PU
//获取上PU的帧内模式
int aboveIntraMode = -1;
//获取指向上PU的指针
const PredictionUnit *puAbove = pu.cs->getPURestricted( pos.offset( 0, -1 ), pu, CHANNEL_TYPE_LUMA );
//如果上块存在且上块的CU的模式是帧内预测模式且上部CU和当前CU属于同一个CTU
if( puAbove && CU::isIntra( *puAbove->cu ) && CU::isSameCtu(*pu.cu, *puAbove->cu) )
{
//如果上块是Mip模式
if( PU::isMIP( *puAbove ) )
{
//如果上块的Mip尺寸等级和当前PU的MIp的尺寸等级相同,否则aboveIntraMode默认为-1
if (getMipSizeId(*puAbove) == getMipSizeId(pu))
{
aboveIntraMode = puAbove->intraDir[CHANNEL_TYPE_LUMA];//aboveIntraMode就保存上块的帧内亮度Mip模式
realMode = true;
}
}
else//如果上块的模式不是Mip模式,则将上块的传统角度模式通过转换列表转换到Mip模式再赋给aboveIntraMode
{
aboveIntraMode = g_mapAngular33ToMip[getMipSizeId(pu)][g_intraMode65to33AngMapping[puAbove->intraDir[CHANNEL_TYPE_LUMA]]];
}
}
// derive MPMs
//开始推导Mip模式的MPM列表,并检查是否MPM列表的数目相等
CHECKD(NUM_MPM_MIP != 3, "Error: wrong number of MPMs for MIP");
//根据当前的块大小构造一个默认的初始MPM列表
const int* modeList = g_sortedMipMpms[getMipSizeId(pu)];
int numCand = 0;//候选的模式数量
//如果左和上的模式相等
if( leftIntraMode == aboveIntraMode )
{
//如果左块和当前块的等级相同
if( leftIntraMode > -1 )
{
mpm[0] = leftIntraMode;//MPM的第一个放入左块中的模式
numCand = 1;//候选的模式数量为1
if( leftIntraMode != modeList[0] )//如果左块的模式不等于默认列表中的第一个模式
{
mpm[1] = modeList[0];//将默认列表中的第一个模式放入MPM的第二个模式
mpm[2] = (leftIntraMode != modeList[1]) ? modeList[1] : modeList[2];
//如果左块的模式不等于默认列表中的第二个模式,则将默认列表中的第二个模式放入MPM的第三个模式
//否则,将默认列表中的第三个模式放入MPM的第三个模式
}
else//如果左块的模式等于默认列表中的第一个模式,则默认的MPM列表就是当前的MPM列表
{
mpm[1] = modeList[1];
mpm[2] = modeList[2];
}
}
else//如果左块和当前块的等级不相同,则默认的MPM列表就是当前的MPM列表
{
mpm[0] = modeList[0];
mpm[1] = modeList[1];
mpm[2] = modeList[2];
}
}
else//如果左和上的模式不相等
{
if( leftIntraMode > -1 && aboveIntraMode > -1 )//如果左和上的块尺寸等级都和当前CU相等
{
mpm[0] = leftIntraMode;//左模式放入第一个
mpm[1] = aboveIntraMode;//左模式放入第二个
numCand = 2;//候选的模式数量为2
//为MPM列表的最后一个元素定义索引
int index = 0;
//循环三次
for( int i = 0; i < 3; i++ )
{
//如果左和上的模式都不和默认的MPM列表中的模式不相等
if( (leftIntraMode != modeList[i]) && (aboveIntraMode != modeList[i]) )
{
index = i;//默认的列表索引给index
break;
}
}
CHECK( index > 2, "Error" );
mpm[2] = modeList[index];//MPM列表的最后一个模式放入选中的index模式
}
else//如果左和上的块尺寸等级至少有一个和当前CU不相等
{
mpm[0] = leftIntraMode > -1 ? leftIntraMode : aboveIntraMode;//如果左和当前等级相同,MPM第一个放入左模式;否则放入右模式
numCand = 1;
if( mpm[0] != modeList[0] )//如果当前MPM的第一个和默认MPM列表的第一个模式不相等
{
mpm[1] = modeList[0];//将默认MPM列表的第一个模式放入当前MPM的第二个中
mpm[2] = (mpm[0] != modeList[1]) ? modeList[1] : modeList[2];
//如果mpm[0]不等于默认列表中的第二个模式,则将默认列表中的第二个模式放入当前MPM的第三个模式
//否则,将默认列表中的第三个模式放入当前MPM的第三个模式
}
else//如果当前MPM的第一个和默认MPM列表的第一个模式相等,则默认的MPM列表就是当前的MPM列表
{
mpm[1] = modeList[1];
mpm[2] = modeList[2];
}
}
}
return (realMode ? numCand : 0);//返回MPM中最优的模式候选个数
}