【二十一】 H.266/VVC | 仿射运动估计AMVP候选列表的构建 | fillAffineMvpCand函数
程序员文章站
2022-07-07 13:40:36
...
/*
函数的作用: 构建Affine AMVP list候选列表
函数的参数说明:
1、PredictionUnit &pu 当前编码的PU
2、const RefPicList &eRefPicList 当前PU的参考图像列表
3、const int &refIdx 当前PU的参考图像在参考图像列表中的索引值
4、AffineAMVPInfo &affiAMVPInfo 用于存放AMVP的结构体,最终AMVP list存储于该变量中
*/
void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AffineAMVPInfo &affiAMVPInfo)
{
//初始化AMVP候选的数量为0
affiAMVPInfo.numCand = 0;
//如果当前PU参考图像的索引值小于0,直接退出
if (refIdx < 0)
{
return;
}
// insert inherited affine candidates
//插入继承的仿射候选
//定义一个MV数组,用于存放相邻块的运动信息(当前PU三个控制点的运动矢量)
Mv outputAffineMv[3];
//获取当前PU亮度分量Y的左上位置信息,返回值为this,说明当前PU的位置即为topLeft的位置信息
Position posLT = pu.Y().topLeft();
//获取当前PU亮度分量Y的右上位置信息
Position posRT = pu.Y().topRight();
//获取当前PU亮度分量Y的左下位置信息
Position posLB = pu.Y().bottomLeft();
/*
获取第一种AMVP候选类型,即相邻PU的可用运动信息继承
下面连续的if获取当前PU的可用相邻块的仿射运动信息
检查可用性的顺序为:
1、先检查左边 即左下->左
2、再检查上方 即右上->上->左上
*/
// check left neighbor
if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, affiAMVPInfo ) )
{
addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, affiAMVPInfo );
}
// check above neighbor
if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, affiAMVPInfo ) )
{
if ( !addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, affiAMVPInfo ) )
{
addAffineMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, affiAMVPInfo );
}
}
/*
下面的if和for的嵌套语句用于判断第一种类型的AMVP候选检查完毕后,AMVP候选列表是否已经被填满
AMVP列表的最大候选数量AMVP_MAX_NUM_CANDS为2
如果候选列表已经满了,则将候选的运动信息转换成1/16的像素精度
*/
if ( affiAMVPInfo.numCand >= AMVP_MAX_NUM_CANDS )
{
for (int i = 0; i < affiAMVPInfo.numCand; i++)
{
affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
}
return;
}
// insert constructed affine candidates
/*
向AMVP候选列表中插入构造的仿射候选:由三个方向的相邻块的平移运动信息去构建Affine AMVP候选
即由相邻PU的平移运动的MV来构建当前PU控制点的仿射MV
*/
//定义了一个整形变量,这个变量的含义
int cornerMVPattern = 0;
//------------------- V0 (START) -------------------//
/*
parameters for AMVP
struct AMVPInfo
{
Mv mvCand[ AMVP_MAX_NUM_CANDS_MEM ]; 用于存放普通AMVP的运动信息MV数组,数组的长度为3
unsigned numCand; 表示当前数组中拥有的候选数目
};
*/
//定义一个AMVP列表amvpInfo0 ,用于存放左上、上方、左边相邻块的可用平移运动矢量
AMVPInfo amvpInfo0;
//初始化列表的长度为0
amvpInfo0.numCand = 0;
/*
调用函数addMVPCandUnscaled,检查A->B->C相邻块的平移运动信息是否可用
函数的参数说明:
1、pu 当前编码的PU
2、eRefPicList 当前PU的参考列表
3、 refIdx 当前PU的参考图像在参考列表中的索引值
4、posLT 该参数用于定位当前块的左上、右上、左下的位置
5、MD_ABOVE_LEFT、MD_ABOVE、MD_LEFT、MD_ABOVE_RIGHT、MD_BELOW_LEFT分别表示当前PU的左上、上方、左边、右上、左下相邻PU的位置
候选列表amvpInfo0长度为1,即VO这些相邻块至多提供一个候选
*/
// A->C: Above Left, Above, Left
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, amvpInfo0 );
if ( amvpInfo0.numCand < 1 )
{
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE, amvpInfo0 );
}
if ( amvpInfo0.numCand < 1 )
{
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_LEFT, amvpInfo0 );
}
//|: 是位运算符,表示“按位或”
cornerMVPattern = cornerMVPattern | amvpInfo0.numCand;
/*
调用函数addMVPCandUnscaled,检查D->E相邻块的平移运动信息是否可用
候选列表amvpInfo1长度为1
*/
//------------------- V1 (START) -------------------//
AMVPInfo amvpInfo1;
amvpInfo1.numCand = 0;
// D->E: Above, Above Right
addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, amvpInfo1 );
if ( amvpInfo1.numCand < 1 )
{
addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, amvpInfo1 );
}
cornerMVPattern = cornerMVPattern | (amvpInfo1.numCand << 1);
/*
调用函数addMVPCandUnscaled,检查F->G相邻块的平移运动信息是否可用
候选列表amvpInfo2长度为1
*/
//------------------- V2 (START) -------------------//
AMVPInfo amvpInfo2;
amvpInfo2.numCand = 0;
// F->G: Left, Below Left
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, amvpInfo2 );
if ( amvpInfo2.numCand < 1 )
{
addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, amvpInfo2 );
}
cornerMVPattern = cornerMVPattern | (amvpInfo2.numCand << 2);
//将三个AMVP平移候选列表amvpInfo0、amvpInfo1、amvpInfo2候选存入到用于存放相邻块运动信息的Mv数组outputAffineMv[]中
outputAffineMv[0] = amvpInfo0.mvCand[0];
outputAffineMv[1] = amvpInfo1.mvCand[0];
outputAffineMv[2] = amvpInfo2.mvCand[0];
//将outputAffineMv[]数组中的运动矢量的精度转换成1/16的像素精度
outputAffineMv[0].roundAffinePrecInternal2Amvr(pu.cu->imv);
outputAffineMv[1].roundAffinePrecInternal2Amvr(pu.cu->imv);
outputAffineMv[2].roundAffinePrecInternal2Amvr(pu.cu->imv);
//通过相邻块的平移运动信息构建Affine AMVP候选
if ( cornerMVPattern == 7 || (cornerMVPattern == 3 && pu.cu->affineType == AFFINEMODEL_4PARAM) )
{
//仿射模型的三个控制点的运动矢量信息
affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];
affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];
affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];
affiAMVPInfo.numCand++;
}
//当三个平移候选列表,未同时有候选,且Affine AMVP列表的长度小于3时,直接使用相邻PU的平移MV
if ( affiAMVPInfo.numCand < 2 )
{
// check corner MVs
for ( int i = 2; i >= 0 && affiAMVPInfo.numCand < AMVP_MAX_NUM_CANDS; i-- )
{
if ( cornerMVPattern & (1 << i) ) // MV i exist
{
affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[i];
affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[i];
affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[i];
affiAMVPInfo.numCand++;
}
}
/*经过上述步骤,如果Affine AMVP列表还未被填满,则填充时域候选
填充时域候选的条件:
1、当前Affine AMVP候选列表长度小于3
2、当前PU允许使用时域候选
获取时域候选的原理,记不太清,所以不做详细说明
*/
// Get Temporal Motion Predictor
if ( affiAMVPInfo.numCand < 2 && pu.cs->picHeader->getEnableTMVPFlag() )
{
const int refIdxCol = refIdx;
Position posRB = pu.Y().bottomRight().offset( -3, -3 );
const PreCalcValues& pcv = *pu.cs->pcv;
Position posC0;
bool C0Avail = false;
Position posC1 = pu.Y().center();
Mv cColMv;
bool boundaryCond = ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight);
const SubPic &curSubPic = pu.cs->slice->getPPS()->getSubPicFromPos(pu.lumaPos());
if (curSubPic.getTreatedAsPicFlag())
{
boundaryCond = ((posRB.x + pcv.minCUWidth) <= curSubPic.getSubPicRight() &&
(posRB.y + pcv.minCUHeight) <= curSubPic.getSubPicBottom());
}
if (boundaryCond)
{
int posYInCtu = posRB.y & pcv.maxCUHeightMask;
if (posYInCtu + 4 < pcv.maxCUHeight)
{
posC0 = posRB.offset(4, 4);
C0Avail = true;
}
}
if ( ( C0Avail && getColocatedMVP( pu, eRefPicList, posC0, cColMv, refIdxCol, false ) ) || getColocatedMVP( pu, eRefPicList, posC1, cColMv, refIdxCol, false ) )
{
cColMv.roundAffinePrecInternal2Amvr(pu.cu->imv);
affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = cColMv;
affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = cColMv;
affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = cColMv;
affiAMVPInfo.numCand++;
}
}
//填充了时域候选之后,Affine AMVP还未被填充满,则使用零向量进行填充
if ( affiAMVPInfo.numCand < 2 )
{
// add zero MV
for ( int i = affiAMVPInfo.numCand; i < AMVP_MAX_NUM_CANDS; i++ )
{
affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand].setZero();
affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand].setZero();
affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand].setZero();
affiAMVPInfo.numCand++;
}
}
}
//将Affine AMVP候选转换成1/16的像素精度
for (int i = 0; i < affiAMVPInfo.numCand; i++)
{
affiAMVPInfo.mvCandLT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
affiAMVPInfo.mvCandRT[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
affiAMVPInfo.mvCandLB[i].roundAffinePrecInternal2Amvr(pu.cu->imv);
}
}
更多关于视频编码知识和资源的分享,更精致的文章排版,欢迎关注博主微信公众号,一起交流、学习、进步!!!