欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【二十一】 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);
  }
}

更多关于视频编码知识和资源的分享,更精致的文章排版,欢迎关注博主微信公众号,一起交流、学习、进步!!!
【二十一】 H.266/VVC | 仿射运动估计AMVP候选列表的构建 | fillAffineMvpCand函数

相关标签: VVC/H.266