HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)
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候选列表存入了上述的名为pInfo
的AMVPInfo
结构体中,并返回给xEstimateMvPredAMVP
的pcAMVPInfo
。
另外,从相邻块获取MV的函数分别为xAddMVPCandUnscaled
以及xAddMVPCandWithScaling
。前者为获取A0~B2 MV的函数,后者为获取scaled A0~B2的函数。
HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)相关推荐
- HEVC代码学习:帧间预测——MVP过程中MV的获取、传递及存储
作为一个视频编码小白,最近开始着手啃HEVC帧间预测的代码,想用博客记录一下自己的学习过程,也想与大家分享.交流一下. HEVC代码的学习主要是参考两位大神岳麓吹雪.NB_vol_1的博客以及HM参考 ...
- H.266/VVC相关技术学习笔记16:VTM6.0中的CIIP技术(帧内帧间联合预测)
今天讲一下目前VTM6.0版本中的CIIP技术,CIIP即为帧内帧间联合预测技术,这属于Merge系列的一个分支. 该技术需要先计算当前预测块的帧内预测值,即用Planar.DC.角度预测等传统的帧内 ...
- H.266/VVC-VTM代码学习-帧内预测05-Angular模式下计算预测像素值xPredIntraAng
H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习-帧内预测04-Planar模式下计算预测像素值xPredIntraPlanar 下一篇:H.266/VVC-VTM代码学习-帧内 ...
- 【十五】 VVC/H.266 | 帧内帧间联合预测技术CIIP详解
文章目录 一.基本原理 二.具体技术细节 一.基本原理 CIIP是帧内帧间联合技术,这属于Merge系列技术的一个分支.该技术需要计算当前预测块的帧内预测值,即用传统的帧内预测模式去预测当前块的像素值 ...
- HEVC代码学习39:decodeCtu和xDecodeCU函数
在之前 HEVC代码学习38:decompressSlice函数 学习中提到,解码slice会遍历所有CTU,调用decodeCtu和decompressCtu解码每一个CTU.下面就来学习一下dec ...
- HEVC代码学习42:estIntraPredLumaQT函数
在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到,estIntraPredLumaQT是亮度帧内预测的入口函数,下面将对该函数进行详细学习. estIntraPredLumaQT中完成 ...
- HEVC代码学习35:xEncodeCU函数
xEncodeCU是由encodeCtu调用,其作用是从CTU开始迭代对每个CU进行编码.注意,xEncodeCU是在最优分块已经划分完成后进行编码时使用的,在xCompressCU中没有使用. xE ...
- Overview of HEVC之5 帧间预测
预测块(PB)的划分:与帧内预测的CB相比, HEVC为帧间预测的CB提供了更多的PB划分形状: PART_2N×2N的划分模式表示CB不划分:PART_2N×N的划分模式表示CB水平划分成两个相等尺 ...
- H.266/VVC帧间预测技术学习:帧间和帧内联合预测(Combined inter and intra prediction, CIIP)
在HEVC中一个CU在预测时要么使用帧内预测要么使用帧间预测,二者只能取其一.而VVC中提出的CIIP技术,是将帧间预测信号与帧内预测信号相结合. 在VVC中,当CU以Merge模式编码时,且CU包含 ...
最新文章
- 话里话外:为什么管理咨询业必须走专业化服务之路
- 在ubuntu16.04中一键创建LAMP环境
- flask连接不到mysql数据库,即使使用了python flask mysql,也无法将数据提交到数据库(使用python flask mysql)连接.提交()...
- Java中Map集合类的用法(HashMap)
- TP、Yii、Laravel的区别
- 干货下载丨开源数据库安全管理
- 荣耀20/20 Pro相机规格曝光:DxOMark排名或将再次改变
- 华为linux配置ip地址命令是什么,华为S5700基础配置命令
- PAT乙级1088 三人行 (20分)
- C++-bit转hex(四位二进制转十六进制)
- PHP图形图像的典型应用 --常用图像的应用(统计图)
- MFC简单的登入界面设计
- visio图形包解压
- 教育认证有效期 有道云笔记_有道云笔记使用指南
- 北京科技大学与北京工业大学计算机,请问北京工业大学和北京科技大学2010年计算机专业研究生的录取分数线是多少?谢谢!...
- 团队博客-第六周:Alpha阶段项目复审(科利尔拉弗队)
- 十进制数转换为二进制,八进制,十六进制数的算法(欢迎拍砖)
- 2017百度之星初赛B场总结
- RS485保护电路的设计
- 免费开源的编辑器 - SciTE
热门文章
- linux kill ps的结果,(转载)linux 查看某进程 并杀死进程 ps grep kill
- 安卓点击图片跳转界面_详解拳头注册 + 安卓LOL试玩教程
- 知识型IP与网红的区别
- 【Error】西部数据磁盘插上不显示盘符
- python进阶数据分析_数据分析--Part 2: Python进阶
- Win10删除文件夹
- F2FS源码分析-1.4 [F2FS 元数据布局部分] Segment Infomation Table-SIT结构
- 一种典型的手机APP远程控制PLC解决方案
- matlab motor,MATLAB在电机仿真中的应用 Application of MATLAB in motor simulation.pdf
- 芯片SA58672(功放芯片)