HEVC帧间预测在AMVP模式下是依靠xEstimateMvPredAMVP函数获取预测MV(MVP)的。

这部分内容的学习还可以参考这两篇博客:
HEVC代码学习15:AMVP相关函数
HM编码器代码阅读(16)——帧间预测之AMVP模式(四)预测MV的获取

xEstimateMvPredAMVP

xEstimateMvPredAMVP的作用是建立MVP列表并获取最优MVP,最终将最优MVP以及其索引等信息返回给上层函数——preInterSearch

xEstimateMvPredAMVP的基本流程如下:
(1)判断bFilled标识,该标识表示MVP候选列表是否已经建好;
(2)如果bFilled是false,通过fillMvpCand获取MVP候选列表;否则,不需要重新建立MVP候选列表
(3)先把MVP候选列表中的第一个MVP作为最优的MVP
(4)如果候选列表中MVP的数量小于等于1,那么直接把步骤3选出的MVP返回
(5)如果bFilled是true,表示MVP候选列表是原来已经建立好的,那么直接根据PU的相关信息得到最优的MVP,然后返回
(6)遍历MVP候选列表,选出代价最小的MVP作为当前PU的MVP,并设置相关信息,然后返回

xEstimateMvPredAMVP代码如下:

Void TEncSearch::xEstimateMvPredAMVP( TComDataCU* pcCU, TComYuv* pcOrgYuv, UInt uiPartIdx, RefPicList eRefPicList, Int iRefIdx, TComMv& rcMvPred, Bool bFilled, Distortion* puiDistBiP )
{AMVPInfo*  pcAMVPInfo = pcCU->getCUMvField(eRefPicList)->getAMVPInfo();TComMv     cBestMv;  //最优MVInt        iBestIdx   = 0; //最优MV在候选中的索引TComMv     cZeroMv;TComMv     cMvPred;  Distortion uiBestCost = std::numeric_limits<Distortion>::max();UInt       uiPartAddr = 0; // PU地址Int        iRoiWidth, iRoiHeight; // PU长宽Int        i;Int        minMVPCand;Int        maxMVPCand;// 获取当前PU的索引和大小pcCU->getPartIndexAndSize( uiPartIdx, uiPartAddr, iRoiWidth, iRoiHeight );// Fill the MV Candidatesif (!bFilled){pcCU->fillMvpCand( uiPartIdx, uiPartAddr, eRefPicList, iRefIdx, pcAMVPInfo ); // 建立MVP候选列表}// initialize Mvp index & Mvp
#if MCTS_ENC_CHECKif (m_pcEncCfg->getTMCTSSEITileConstraint() && pcCU->isLastColumnCTUInTile() && (pcAMVPInfo->numSpatialMVPCandidates < pcAMVPInfo->iN)){iBestIdx    = (pcAMVPInfo->numSpatialMVPCandidates == 0) ? 1 : 0;cBestMv     = pcAMVPInfo->m_acMvCand[(pcAMVPInfo->numSpatialMVPCandidates == 0) ? 1 : 0];minMVPCand  = (pcAMVPInfo->numSpatialMVPCandidates == 0) ? 1 : 0;maxMVPCand  = (pcAMVPInfo->numSpatialMVPCandidates == 0) ? pcAMVPInfo->iN : 1;}else{iBestIdx = 0;cBestMv  = pcAMVPInfo->m_acMvCand[0];minMVPCand  = 0;maxMVPCand  = pcAMVPInfo->iN;}
#elseiBestIdx = 0;cBestMv  = pcAMVPInfo->m_acMvCand[0];minMVPCand  = 0;maxMVPCand  = pcAMVPInfo->iN;
#endifif (pcAMVPInfo->iN <= 1){rcMvPred = cBestMv;pcCU->setMVPIdxSubParts( iBestIdx, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( pcAMVPInfo->iN, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));if(pcCU->getSlice()->getMvdL1ZeroFlag() && eRefPicList==REF_PIC_LIST_1){(*puiDistBiP) = xGetTemplateCost( pcCU, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, rcMvPred, 0, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight);}return;}// 上述步骤(5)if (bFilled){assert(pcCU->getMVPIdx(eRefPicList,uiPartAddr) >= 0);rcMvPred = pcAMVPInfo->m_acMvCand[pcCU->getMVPIdx(eRefPicList,uiPartAddr)];return;}m_cYuvPredTemp.clear();//-- Check Minimum Cost.// 比较得出最优MVfor ( i = minMVPCand ; i < maxMVPCand; i++){Distortion uiTmpCost;uiTmpCost = xGetTemplateCost( pcCU, uiPartAddr, pcOrgYuv, &m_cYuvPredTemp, pcAMVPInfo->m_acMvCand[i], i, AMVP_MAX_NUM_CANDS, eRefPicList, iRefIdx, iRoiWidth, iRoiHeight);if ( uiBestCost > uiTmpCost ){uiBestCost = uiTmpCost;cBestMv   = pcAMVPInfo->m_acMvCand[i];iBestIdx  = i;(*puiDistBiP) = uiTmpCost;}}m_cYuvPredTemp.clear();// Setting Best MVP// 设置最优MVP的信息,将获取的最优MVP信息返回给preInterSearchrcMvPred = cBestMv;pcCU->setMVPIdxSubParts( iBestIdx, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));pcCU->setMVPNumSubParts( pcAMVPInfo->iN, eRefPicList, uiPartAddr, uiPartIdx, pcCU->getDepth(uiPartAddr));return;
}

而在上层函数preInterSearch中,则通过以下语句调用xEstimateMvPredAMVP并获取从其中返回的上述三个值:cBestMv(最优MV)、iBestIdx(最优MV索引)、pcAMVPInfo->iN(AMVP候选列表中有效候选数量)

xEstimateMvPredAMVP( pcCU, pcOrgYuv, iPartIdx, eRefPicList, iRefIdxTemp, cMvPred[iRefList][iRefIdxTemp], false, &biPDistTemp);
aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->getMVPIdx(eRefPicList, uiPartAddr);
aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->getMVPNum(eRefPicList, uiPartAddr);

通过调用上述语句,实则将上述三个值分别赋给以下三个值,以供后续使用:

 //最优MV存入cMvPred[iRefList][iRefIdxTemp]cMvPred[iRefList][iRefIdxTemp] = cBestMv// 最优MVP索引存入CU中(执行xEstimateMvPredAMVP的结果),然后赋值给aaiMvpIdx[iRefList][iRefIdxTemp]aaiMvpIdx[iRefList][iRefIdxTemp] = pcCU->m_apiMVPIdx[eRefPicList][uiPartAddr]  // MVP候选数量存入CU中(执行xEstimateMvPredAMVP的结果),然后赋值给aaiMvpNum[iRefList][iRefIdxTemp] aaiMvpNum[iRefList][iRefIdxTemp] = pcCU->m_apiMVPNum[eRefPicList][uiPartAddr]

AMVPInfo

另需要特别注意的是,在HM中,AMVP候选列表是用一个AMVPInfo类的结构体进行存储和传递的

typedef struct _AMVPInfo
{// MVP候选列表TComMv m_acMvCand[ AMVP_MAX_NUM_CANDS ];  ///< array of motion vector predictor candidates// 候选列表中有效候选数量Int    iN;                                ///< number of motion vector predictor candidates
#if MCTS_ENC_CHECK// 候选列表中空域候选数量UInt   numSpatialMVPCandidates;
#endif
} AMVPInfo;

这个存储MVP候选列表信息的结构体,在xEstimateMvPredAMVP中名为pcAMVPInfo,在fillMVPCand中名为pInfo

fillMVPCand

下面学习fillMVPCand函数,该函数用于建立MVP候选列表。
该函数学习可参考博客:
HEVC代码学习30:fillMvpCand函数


由于在视频中经常出现一大片区域(比如同一物体)运动方向一样的情况,因此运动向量MV不论在空域上还是时域上都有着很强的相关性,因此可以用当前PU在空域、时域上相邻的块的MV近似代替当前PU的MV以实现运动矢量预测(MVP)。

AMVP技术及将理论上与当前PU的MV最可能相近的空域相邻块、时域相邻块的若干MV收录进MVP候选列表中,以供之后进行选择。而这个建立MVP候选列表并收录MVP的过程就是在fillMVPCand函数中实现的。

建立顺序如下:
一、建立空域候选列表:
1、按顺序搜索左侧块A0-A1-scaled A0-scaled A1,只要有一个MV存在,写入候选列表,跳出进行下一步。
2、按顺序搜索上方块B0-B1-B2(-scaled B0-scaled B1-scaled B2),只要有一个MV存在,写入候选列表,跳出进行下一步。而其中scaled B0-scaled B1-scaled B2只有当A0和A1的MV都不存在时,才进行搜索。
3、空域候选列表长度为2时,即左侧和上方各选出了一个候选,则对两者进行比较,相同时进行合并。
4、空域候选列表建立完成。
二、当空域候选数量少于2个,且启用时域候选时,建立时域候选列表。
三、如果空域+时域候选个数少于2,则使用(0,0)补足。

fillMVPCand代码如下:

Void TComDataCU::fillMvpCand ( const UInt partIdx, const UInt partAddr, const RefPicList eRefPicList, const Int refIdx, AMVPInfo* pInfo ) const
{pInfo->iN = 0; // 候选数量先置零if (refIdx < 0){#if MCTS_ENC_CHECKpInfo->numSpatialMVPCandidates = 0; // 空域候选数量先置零
#endifreturn;}//-- Get Spatial MV// 获取相邻块地址UInt partIdxLT, partIdxRT, partIdxLB;deriveLeftRightTopIdx( partIdx, partIdxLT, partIdxRT );deriveLeftBottomIdx( partIdx, partIdxLB );Bool isScaledFlagLX = false; /// variable name from specification; true when the PUs below left or left are available (availableA0 || availableA1).{UInt idx;const TComDataCU* tmpCU = getPUBelowLeft(idx, partIdxLB); // 获取左下角CUisScaledFlagLX = (tmpCU != NULL) && (tmpCU->isInter(idx));if (!isScaledFlagLX){tmpCU = getPULeft(idx, partIdxLB);isScaledFlagLX = (tmpCU != NULL) && (tmpCU->isInter(idx));}}// Left predictor search// 获取左侧块MVif (isScaledFlagLX){Bool bAdded = xAddMVPCandUnscaled( *pInfo, eRefPicList, refIdx, partIdxLB, MD_BELOW_LEFT); // 获取A0的MVif (!bAdded){bAdded = xAddMVPCandUnscaled( *pInfo, eRefPicList, refIdx, partIdxLB, MD_LEFT ); // 获取A1的MVif(!bAdded){bAdded = xAddMVPCandWithScaling( *pInfo, eRefPicList, refIdx, partIdxLB, MD_BELOW_LEFT); // 获取scaled A0的MVif (!bAdded){xAddMVPCandWithScaling( *pInfo, eRefPicList, refIdx, partIdxLB, MD_LEFT ); // 获取scaled A1的MV}}}}// Above predictor search{Bool bAdded = xAddMVPCandUnscaled( *pInfo, eRefPicList, refIdx, partIdxRT, MD_ABOVE_RIGHT);if (!bAdded){bAdded = xAddMVPCandUnscaled( *pInfo, eRefPicList, refIdx, partIdxRT, MD_ABOVE);if(!bAdded){xAddMVPCandUnscaled( *pInfo, eRefPicList, refIdx, partIdxLT, MD_ABOVE_LEFT);}}}if(!isScaledFlagLX){Bool bAdded = xAddMVPCandWithScaling( *pInfo, eRefPicList, refIdx, partIdxRT, MD_ABOVE_RIGHT);if (!bAdded){bAdded = xAddMVPCandWithScaling( *pInfo, eRefPicList, refIdx, partIdxRT, MD_ABOVE);if(!bAdded){xAddMVPCandWithScaling( *pInfo, eRefPicList, refIdx, partIdxLT, MD_ABOVE_LEFT);}}}if ( pInfo->iN == 2 ){if ( pInfo->m_acMvCand[ 0 ] == pInfo->m_acMvCand[ 1 ] ){pInfo->iN = 1;}}
#if MCTS_ENC_CHECKpInfo->numSpatialMVPCandidates = pInfo->iN;
#endif// 获取时域MVPif (pInfo->iN < AMVP_MAX_NUM_CANDS && getSlice()->getEnableTMVPFlag() ){// Get Temporal Motion Predictorconst UInt numPartInCtuWidth  = m_pcPic->getNumPartInCtuWidth();const UInt numPartInCtuHeight = m_pcPic->getNumPartInCtuHeight();const Int refIdx_Col = refIdx;TComMv cColMv;UInt partIdxRB;UInt absPartIdx;deriveRightBottomIdx( partIdx, partIdxRB );UInt absPartAddr = m_absZIdxInCtu + partAddr;//----  co-located RightBottom Temporal Predictor (H) ---//absPartIdx = g_auiZscanToRaster[partIdxRB];Int ctuRsAddr = -1;if (  ( ( m_pcPic->getCtu(m_ctuRsAddr)->getCUPelX() + g_auiRasterToPelX[absPartIdx] + m_pcPic->getMinCUWidth () ) < m_pcSlice->getSPS()->getPicWidthInLumaSamples () )  // image boundary check&& ( ( m_pcPic->getCtu(m_ctuRsAddr)->getCUPelY() + g_auiRasterToPelY[absPartIdx] + m_pcPic->getMinCUHeight() ) < m_pcSlice->getSPS()->getPicHeightInLumaSamples() ) ){if ( ( absPartIdx % numPartInCtuWidth < numPartInCtuWidth - 1 ) &&  // is not at the last column of CTU( absPartIdx / numPartInCtuWidth < numPartInCtuHeight - 1 ) )  // is not at the last row    of CTU{absPartAddr = g_auiRasterToZscan[ absPartIdx + numPartInCtuWidth + 1 ];ctuRsAddr = getCtuRsAddr();}else if ( absPartIdx % numPartInCtuWidth < numPartInCtuWidth - 1 )  // is not at the last column of CTU But is last row of CTU{absPartAddr = g_auiRasterToZscan[ (absPartIdx + numPartInCtuWidth + 1) % m_pcPic->getNumPartitionsInCtu() ];}else if ( absPartIdx / numPartInCtuWidth < numPartInCtuHeight - 1 ) // is not at the last row of CTU But is last column of CTU{absPartAddr = g_auiRasterToZscan[ absPartIdx + 1 ];ctuRsAddr = getCtuRsAddr() + 1;}else //is the right bottom corner of CTU{absPartAddr = 0;}}if ( ctuRsAddr >= 0 && xGetColMVP( eRefPicList, ctuRsAddr, absPartAddr, cColMv, refIdx_Col ) ){pInfo->m_acMvCand[pInfo->iN++] = cColMv;}else{UInt uiPartIdxCenter;xDeriveCenterIdx( partIdx, uiPartIdxCenter );if (xGetColMVP( eRefPicList, getCtuRsAddr(), uiPartIdxCenter,  cColMv, refIdx_Col )){pInfo->m_acMvCand[pInfo->iN++] = cColMv;}}//----  co-located RightBottom Temporal Predictor  ---//}// 若候选列表不满,补零while (pInfo->iN < AMVP_MAX_NUM_CANDS){pInfo->m_acMvCand[pInfo->iN].set(0,0);pInfo->iN++;}return ;
}

可见,最终构建的MVP候选列表存入了上述的名为pInfoAMVPInfo结构体中,并返回给xEstimateMvPredAMVPpcAMVPInfo

另外,从相邻块获取MV的函数分别为xAddMVPCandUnscaled以及xAddMVPCandWithScaling。前者为获取A0~B2 MV的函数,后者为获取scaled A0~B2的函数。

HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)相关推荐

  1. HEVC代码学习:帧间预测——MVP过程中MV的获取、传递及存储

    作为一个视频编码小白,最近开始着手啃HEVC帧间预测的代码,想用博客记录一下自己的学习过程,也想与大家分享.交流一下. HEVC代码的学习主要是参考两位大神岳麓吹雪.NB_vol_1的博客以及HM参考 ...

  2. H.266/VVC相关技术学习笔记16:VTM6.0中的CIIP技术(帧内帧间联合预测)

    今天讲一下目前VTM6.0版本中的CIIP技术,CIIP即为帧内帧间联合预测技术,这属于Merge系列的一个分支. 该技术需要先计算当前预测块的帧内预测值,即用Planar.DC.角度预测等传统的帧内 ...

  3. H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng

    H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar 下一篇:H.266/VVC-VTM代码学习-帧内 ...

  4. 【十五】 VVC/H.266 | 帧内帧间联合预测技术CIIP详解

    文章目录 一.基本原理 二.具体技术细节 一.基本原理 CIIP是帧内帧间联合技术,这属于Merge系列技术的一个分支.该技术需要计算当前预测块的帧内预测值,即用传统的帧内预测模式去预测当前块的像素值 ...

  5. HEVC代码学习39:decodeCtu和xDecodeCU函数

    在之前 HEVC代码学习38:decompressSlice函数 学习中提到,解码slice会遍历所有CTU,调用decodeCtu和decompressCtu解码每一个CTU.下面就来学习一下dec ...

  6. HEVC代码学习42:estIntraPredLumaQT函数

    在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到,estIntraPredLumaQT是亮度帧内预测的入口函数,下面将对该函数进行详细学习. estIntraPredLumaQT中完成 ...

  7. HEVC代码学习35:xEncodeCU函数

    xEncodeCU是由encodeCtu调用,其作用是从CTU开始迭代对每个CU进行编码.注意,xEncodeCU是在最优分块已经划分完成后进行编码时使用的,在xCompressCU中没有使用. xE ...

  8. Overview of HEVC之5 帧间预测

    预测块(PB)的划分:与帧内预测的CB相比, HEVC为帧间预测的CB提供了更多的PB划分形状: PART_2N×2N的划分模式表示CB不划分:PART_2N×N的划分模式表示CB水平划分成两个相等尺 ...

  9. H.266/VVC帧间预测技术学习:帧间和帧内联合预测(Combined inter and intra prediction, CIIP)

    在HEVC中一个CU在预测时要么使用帧内预测要么使用帧间预测,二者只能取其一.而VVC中提出的CIIP技术,是将帧间预测信号与帧内预测信号相结合. 在VVC中,当CU以Merge模式编码时,且CU包含 ...

最新文章

  1. 话里话外:为什么管理咨询业必须走专业化服务之路
  2. 在ubuntu16.04中一键创建LAMP环境
  3. flask连接不到mysql数据库,即使使用了python flask mysql,也无法将数据提交到数据库(使用python flask mysql)连接.提交()...
  4. Java中Map集合类的用法(HashMap)
  5. TP、Yii、Laravel的区别
  6. 干货下载丨开源数据库安全管理
  7. 荣耀20/20 Pro相机规格曝光:DxOMark排名或将再次改变
  8. 华为linux配置ip地址命令是什么,华为S5700基础配置命令
  9. PAT乙级1088 三人行 (20分)
  10. C++-bit转hex(四位二进制转十六进制)
  11. PHP图形图像的典型应用 --常用图像的应用(统计图)
  12. MFC简单的登入界面设计
  13. visio图形包解压
  14. 教育认证有效期 有道云笔记_有道云笔记使用指南
  15. 北京科技大学与北京工业大学计算机,请问北京工业大学和北京科技大学2010年计算机专业研究生的录取分数线是多少?谢谢!...
  16. 团队博客-第六周:Alpha阶段项目复审(科利尔拉弗队)
  17. 十进制数转换为二进制,八进制,十六进制数的算法(欢迎拍砖)
  18. 2017百度之星初赛B场总结
  19. RS485保护电路的设计
  20. 免费开源的编辑器 - SciTE

热门文章

  1. linux kill ps的结果,(转载)linux 查看某进程 并杀死进程 ps grep kill
  2. 安卓点击图片跳转界面_详解拳头注册 + 安卓LOL试玩教程
  3. 知识型IP与网红的区别
  4. 【Error】西部数据磁盘插上不显示盘符
  5. python进阶数据分析_数据分析--Part 2: Python进阶
  6. Win10删除文件夹
  7. F2FS源码分析-1.4 [F2FS 元数据布局部分] Segment Infomation Table-SIT结构
  8. 一种典型的手机APP远程控制PLC解决方案
  9. matlab motor,MATLAB在电机仿真中的应用 Application of MATLAB in motor simulation.pdf
  10. 芯片SA58672(功放芯片)