HEVC参考软件HM源码分析--帧间预测(3)--predInterSearch

帧间预测基本知识

对帧间预测有所了解的同学应该都知道,帧间预测操作一般都是由两个最基本也是最核心的操作组成:运动估计(ME)和运动补偿(MC)。
运动估计:负责找到当前帧和参考帧之间的匹配运动矢量(MV)
运动补偿:对MV所指向的参考帧中的匹配参考块,进行插值滤波等操作,得到最终的预测参考块(即用于和当前编码块作差得到残差系数等)
HM中,ME和MC便是在predInterSearch函数中完成的。
备注:想复习补充帧间预测相关知识的同学,可以参见大神的博客:https://blog.csdn.net/NB_vol_1/article/details/55272434

运动补偿为什么要插值

众所周知,现在的视频编码标准MV都支持分数像素,而参考帧中的原始参考块是整数像素级的,为了支持分数像素MV,所以需要对整数像素块进行插值操作,得到分数像素精度的预测参考块。从而进行后续的求残差、变换量化等操作。

函数基本流程

函数原型

Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv DEBUG_STRING_FN_DECLARE(sDebug), Bool bUseRes, Bool bUseMRG )
参数说明:
pcCU:当前评估的CU模式
pcOrgYuv:CU原始数据
pcPredYuv:预测数据
pcResiYuv:残差数据
pcRecoYuv:重建数据
bUseRes:默认为false,false–函数开头处会清空pcResiYuv。楼主看了下,程序中该变量恒为false,不是很明白该变量的用途。
bUseMRG:默认为false。true–对当前划分模式进行Merge评估,即找到最优的Merge候选项,不需要ME,false–进行ME + MC,计算代价

流程

P帧

对于CU下的每一个PU,遍历参考列表0中的各帧作为参考帧,进行如下操作:

  1. AMVP
    获得最优MVP,并将其作为ME的搜索起点,对应的函数是xEstimateMvPredAMVP

  2. ME
    以AMVP得到的MVP作为搜索起点,搜索最优MV,对应的函数是xMotionEstimation

    遍历完各参考帧后,便得到了最优的帧间预测参数:参考帧和对应的MV

  3. 设置最优的预测模式、参考帧、MV、MVD

  4. 若划分尺寸不是2N x 2N,则补充进行Merge模式评估,将最优的Merge模式代价和ME代价进行比较,更新最优模式。注:Merge省略了ME过程,直接使用时空域相邻候选项的MV。

B帧

B帧与P帧主要的区别就是:B帧会选用两个参考帧,对应两个MV。对应到预测流程上的区别基本如下

  1. 会遍历两个参考列表中的各帧作为参考帧,进行AMVP和ME操作,记录单向预测的预测性能
  2. 进行双向预测,即从参考列表0和参考列表1中各选择一个参考帧,进行预测。由于需要两个MV,所以在对参考列表X(X取0,1)进行ME之后,需要补充进行MC操作(得到预测参考块X),然后再对列表1-X进行ME操作,从而得到最优的两个MV。
  3. 将双向预测的性能与单向预测进行比较,得到最终的预测方式并记录详细的预测信息

Merge评估

bUseMRG为true时,只进行Merge模式评估,即只进行步骤4。

关键变量

最核心的变量是bTestNormalMC。bTestNormalMC由传入的形参bUseMRG决定,bUseMRG为true,bTestNormalMC才会被设为false,从而进行Merge模式评估。
作用:true–进行正常的ME和MC操作,false–进行Merge模式评估,省去了ME操作。
其他的重要变量都在源代码中以注释的形式进行了说明。注:注释中还包含了楼主的一些疑问,还希望有大佬能指点迷津或者一起交流探讨。

源代码分析

备注:此代码段是从HM16.20源代码TEncSearch.cpp中完整拷贝过来的,里面加入了个人的阅读注释。楼主对代码的注释可能会存在理解偏差,望读者可以指出来,一起交流探讨~毕竟楼主对于HM源代码研究不多,不少地方也存在疑惑。希望有大佬同仁一起学习交流,共同进步!

//! search of the best candidate for inter prediction
#if AMP_MRG
Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv DEBUG_STRING_FN_DECLARE(sDebug), Bool bUseRes, Bool bUseMRG )
#else
Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv* pcPredYuv, TComYuv* pcResiYuv, TComYuv* pcRecoYuv, Bool bUseRes )
#endif
{
   
  for(UInt i=0; i<NUM_REF_PIC_LIST_01; i++)
  {
   
    m_acYuvPred[i].clear();
  }
  m_cYuvPredTemp.clear();
  pcPredYuv->clear();
  
  if ( !bUseRes ) 
  {
   
    pcResiYuv->clear(); // false--清空残差块
  }

  pcRecoYuv->clear();

  TComMv       cMvSrchRngLT;
  TComMv       cMvSrchRngRB;

  TComMv       cMvZero;
  TComMv       TempMv; //kolya

  TComMv       cMv[2]; // 存储单向预测的MV(分别对应前向和后向)
  TComMv       cMvBi[2]; // 存储双向预测的两个MV
  TComMv       cMvTemp[2][33];

  Int          iNumPart    = pcCU->getNumPartitions(); // 获取当前PU划分模式下子块的个数(例如2N x 2N 对应 1, 2N x N 对应 2, N x N 对应 4)
  Int          iNumPredDir = pcCU->getSlice()->isInterP() ? 1 : 2; // P帧预测方向数为1,B帧为2

  TComMv       cMvPred[2][33];

  TComMv       cMvPredBi[2][33];
  Int          aaiMvpIdxBi[2][33];

  Int          aaiMvpIdx[2][33]; // *** 存储 AMVP 得到的最优 MVP的 列表索引
  Int          aaiMvpNum[2][33]; // *** 存储 AMVP 得到的最优 MVP的 列表长度

  AMVPInfo     aacAMVPInfo[2][33];

  Int          iRefIdx[2]={
   0,0}; //If un-initialized, may cause SEGV in bi-directional prediction iterative stage.
  Int          iRefIdxBi[2];

  UInt         uiPartAddr;
  Int          iRoiWidth, iRoiHeight; // 宽高

  UInt         uiMbBits[3] = {
   1, 1, 0}; // *** 对应的是PU划分模式所需要消耗的编码比特数

  UInt         uiLastMode = 0; // *** 0--前向预测,1--后向预测,2--双向预测
  Int          iRefStart, iRefEnd; // 参考帧的范围

  PartSize     ePartSize = pcCU->getPartitionSize( 0 ); // *** 获取CU的划分尺寸

  Int          bestBiPRefIdxL1 = 0;
  Int          bestBiPMvpL1 = 0;
  Distortion   biPDistTemp = std::numeric_limits<Distortion>::max();

  TComMvField cMvFieldNeighbours[MRG_MAX_NUM_CANDS << 1]; // double length for mv of both lists
  UChar uhInterDirNeighbours[MRG_MAX_NUM_CANDS]; // *** MRG_MAX_NUM_CANDS 宏值为5,说明AMVP是从5个候选项中进行选择
  Int numValidMergeCand = 0 ;

  for ( Int iPartIdx = 0; iPartIdx < iNumPart; iPartIdx++ )
  {
   
    Distortion   uiCost[2] = {
    std::numeric_limits<Distortion>::max(), std::numeric_limits<Distortion>::max() };
    Distortion   uiCostBi  =   std::numeric_limits<Distortion>::max();
    Distortion   uiCostTemp;

    UInt         uiBits[3]; // 对应的是3种预测模式:0-前向预测,1-后向预测,2-双向预测
    UInt         uiBitsTemp;
    Distortion   bestBiPDist = std::numeric_limits<Distortion>::max();

    Distortion   uiCostTempL0[MAX_NUM_REF]; // *** MAX_NUM_REF 该宏的值为16,说明HM中参考帧缓冲最多为16
    for (Int iNumRef=0; iNumRef < MAX_NUM_REF; iNumRef++)
    {
   
      uiCostTempL0[iNumRef] = std::numeric_limits<Distortion>::max();
    }
    UInt         uiBitsTempL0[MAX_NUM_REF];

    TComMv       mvValidList1;
    Int          refIdxValidList1 = 0;
    UInt         bitsValidList1 = MAX_UINT;
    Distortion   costValidList1 = std::numeric_limits<Distortion>::max();

    xGetBlkBits( ePartSize, pcCU->getSlice()->isInterP(), iPartIdx, uiLastMode, uiMbBits); // 计算当前PU划分模式所需要消耗的编码比特数

    pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iRoiWidth, iRoiHeight ); // 计算当前PU块的地址 uiPartAddr, 宽度 iRoiWidth, 高度 iRoiHeight

#if AMP_MRG
    Bool bTestNormalMC = true; // false--只对Merge模式进行评估选择
	
    if ( bUseMRG && pcCU->getWidth( 0 ) > 8 && iNumPart == 2 ) // *** bUseMRG为true时,才可能会将bTestNormalMC设为false,关键代码段
    {
   
      bTestNormalMC = false; // *** bTestNormalMC 设为 false,会跳过对PU的多参考帧运动估计搜索过程,只对Merge模式进行评估选择,注意 bTestNormalMC为true时,也会对Merge模式进行评估
    }

    if (bTestNormalMC)
    {
   
#endif

    //  Uni-directional prediction
    for ( Int iRefList = 0; iRefList < iNumPredDir; iRefList++ ) // ***
    {
   
      RefPicList  eRefPicList = ( iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );

      for ( Int iRefIdxTemp = 0; iRefIdxTemp < pcCU->getSlice()->getNumRefIdx(eRefPicList); iRefIdxTemp++ ) 
      {
   
        uiBitsTemp = uiMbBits[iRefList];
        if ( pcCU->getSlice()->getNumRefIdx(eRefPicList) > 1 )
        {
   
          uiBitsTemp += iRefIdxTemp+1;
          if ( iRefIdxTemp == pcCU->getSlice()->getNumRefIdx(eRefPicList)-1 )
          {
   
            uiBitsTemp--;
          }
        }
        xEstimateMvPredAMVP( pcCU, pcOrgYuv, iPartIdx, eRefPicList, iRefIdxTemp, cMvPred[iRefList][iRefIdxTemp], false, &biPDistTemp); // 对当前参考帧(iRefIdxTemp) 进行AMVP, 获得最优MVP
        aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->getMVPIdx(eRefPicList, uiPartAddr);
        aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->getMVPNum(eRefPicList, uiPartAddr);

        if(pcCU->getSlice()->getMvdL1ZeroFlag() && iRefList==1 && biPDistTemp 
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值