【十三】 H.266/VVC | 帧间预测技术 | 解码端运动向量修正技术(DMVR)

目的:为了提高merge模式下双向预测MV的准确性

基本思路:双向预测是在list0和list1中分别寻找一个运动向量,然后将MV0和MV1所指向的预测块进行加权得到最终预测块,而DMVR技术不是直接使用MV0和MV1,而是在MV0和MV1周围1-2个像素范围内搜索一个更加精确的MV,具体做法是直接计算前后两个经过偏移后的预测块(下图中红色的部分)之间的SAD值,选取SAD值较小的那个偏移后的MV的组合,即为要选取的修正后的MV,并最终利用修正后的MV生成当前块的双向加权预测值。

注意:DMVR生成的修正MV用于生成帧间预测值和后续连续图像编码的时域运动向量预测值(TMVR);原始的MV用于去方块效应和后续CU编码的空域运动向量预测值(SMVP)

在这里插入图片描述

当满足下列所有条件时,可以使用该技术,即dmvrFlag被置为1
  • CU使用merge模式,且是双向预测
  • 两个参考帧分别位于当当前帧之前和当前帧之后
  • 当前图片与前向参考图片的POC差和当前图片与后向参考图片的POC差相等
  • CU至少需要有128个像素点
  • CU的宽和高都大于或等于8
  • BCW使用相等的权值
  • 当前块使不使用WP模式

所以条件在具体代码中实现如下:

bool PU::checkDMVRCondition(const PredictionUnit& pu)   //传入的参数为当前编码的PU
{
   
  WPScalingParam *wp0;//前向WP加权权重
  WPScalingParam *wp1;//后向WP加权权重
  int refIdx0 = pu.refIdx[REF_PIC_LIST_0];   //前向参考帧在L0中的索引值
  int refIdx1 = pu.refIdx[REF_PIC_LIST_1];   //后向参考帧在L1中的索引值
  pu.cu->slice->getWpScaling(REF_PIC_LIST_0, refIdx0, wp0);  //获取前向WP的权重
  pu.cu->slice->getWpScaling(REF_PIC_LIST_1, refIdx1, wp1);  //获取后向WP的权重
  if (pu.cs->sps->getUseDMVR() && (!pu.cs->picHeader->getDisDmvrFlag()))
  {
   
    //满足下列所有条件函数的返回值为真
    return pu.mergeFlag   //为merge模式
      && pu.mergeType == MRG_TYPE_DEFAULT_N  //为常规的merge模式
      && !pu.ciipFlag   //非CIIP模式
      && !pu.cu->affine //非Affine模式
      && !pu.mmvdMergeFlag  //非MMVD_Merge模式
      && !pu.cu->mmvdSkip   //非mmvd_Skip模式
      && PU::isBiPredFromDifferentDirEqDistPoc(pu)  //当前图片与前向参考图片的POC差和当前图片与后向参考图片的POC差相等
      && (pu.lheight() >= 8)  //PU的高大于或等于8
      && (pu.lwidth() >= 8)   //PU的宽大于或等于8
      && ((pu.lheight() * pu.lwidth()) >= 128)   //PU的尺寸大于或等于128
      && (pu.cu->BcwIdx == BCW_DEFAULT)
#if JVET_Q0128_DMVR_BDOF_ENABLING_CONDITION
      //亮度分量和相应的色度分量的前向MV权重和后向MV权重存在flag都等于0(即都不可以用)
      && ((!wp0[COMPONENT_Y].bPresentFlag) && (!wp0[COMPONENT_Cb].bPresentFlag) && (!wp0[COMPONENT_Cr].bPresentFlag) && (!wp1[COMPONENT_Y].bPresentFlag) && (!wp1[COMPONENT_Cb].bPresentFlag) && (!wp1[COMPONENT_Cr].bPresentFlag))
#else
      && ((!wp0[COMPONENT_Y].bPresentFlag) && (!wp1[COMPONENT_Y].bPresentFlag))
#endif
#if JVET_Q0487_SCALING_WINDOW_ISSUES  //缩放窗口问题
      && ( refIdx0 < 0 ? true : (pu.cu->slice->getRefPic( REF_PIC_LIST_0, refIdx0 )->isRefScaled( pu.cs->pps ) == false) )
      && ( refIdx1 < 0 ? true : (pu.cu->slice->getRefPic( REF_PIC_LIST_1, refIdx1 )->isRefScaled( pu.cs->pps ) == false) )
#else
      && ( refIdx0 < 0 ? true : pu.cu->slice->getScalingRatio( REF_PIC_LIST_0, refIdx0 ) == SCALE_1X ) && ( refIdx1 < 0 ? true : pu.cu->slice->getScalingRatio( REF_PIC_LIST_1, refIdx1 ) == SCALE_1X )
#endif
      ;
  }
  else
  {
   
    return false;
  }
}
DMVR技术在VTM8.0中的具体实现
  • 第一步:对初始点的周边位置进行搜索,以寻求更优的MV的偏移位置

由于在VTM中需要计算前后两向的两个候选MV的预测块之间的SAD值,因此使得每一个搜索点处MV的偏移都要遵循一个镜像原则,意思就是前后两个初始MV都必须同时使用同一个搜索点处的MV的偏移,然后按照镜像规则,前向初始点加一个偏移,后向的初始点减去对应的相同的偏移,具体公式如下所示:

M V L 0 ′ = M V L 0 + M V o f f s e t M V L 1 ′ = M V L 1 − M V o f f s e t MV_{L_0}'=MV_{L_0}+MV_{offset}\\ MV_{L_1}'=MV_{L_1}-MV_{offset} MVL0=MVL0+MVoffsetMVL1=MVL1MVoffset
void InterPrediction::xProcessDMVR相关代码:

//初始的MV计算出的子CU的前向及后向预测值(这里前后向的搜索点遵循净吸纳过方式,即对于同一个MV偏移,前向MV是加上偏移量,后向MV是减去偏移量)
Pel *addrL0 = biLinearPredL0 + totalDeltaMV[0] + (totalDeltaMV[1] * m_biLinearBufStride);
Pel *addrL1 = biLinearPredL1 - totalDeltaMV[0] - (totalDeltaMV[1] * m_biLinearBufStride);
//子pu的最终修正后的MV是原来的Merge的初始MV加上子pu的修正MVD(即MV的偏置,前向加偏置,后向减去偏置,呈现镜像的方式)
subPu.mv[0] = mergeMv[REF_PIC_LIST_0] + pu.mvdL0SubPu[num];
subPu.mv[1] = mergeMv[REF_PIC_LIST_1] - pu.mvdL0SubPu[num];

其中MV偏移量代表的是修正后的MV和初始MV之间的偏移量,在VTM中,搜索的最大偏移量为2个整像素,因此总共有25个搜索点(一个初始点和24个周围的点),在VTM8.0代码中的缓存如下(可以在commmonLib库中的InterPrediction.h头文件中找到):

//25个搜索偏置,其中一个是零偏置,代表初始MV本身,其余24个搜素点都是在初始MV基础上在水平和垂直方向上进行一定整像素的偏置,最多偏移2个整像素位置
Mv m_pSearchOffset[25] = {
    Mv(-2,-2), Mv(-1,-2), Mv(0,-2), Mv(1,-2), Mv(2,-2),
                             Mv(-2,-1), Mv(-1,-1), Mv(0,-1), Mv(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值