HEVC函数入门(19)——帧间编码AMVP
来源:互联网 发布:区和县的区别 知乎 编辑:程序博客网 时间:2024/06/14 00:33
本文整理自http://blog.csdn.net/NB_vol_1/article/details/51162391
简介
和
predInterSearch主要的工作是ME(运动估计)和MC(运动补偿)。
函数中有一个bTestNormalMC变量,它表示是否进行正常的MC过程,正常的MC过程就是进行ME再进行MC。
正常的MC流程是,遍历所有的参考帧,进行ME(运动估计:xEstimateMvPredAMVP和xMotionEstimation),然后记录MVP或者MV的信息,进行MC(运动补偿,目的是选出最优的参数),然后更新最优的参数,遍历完所有的参考帧之后,就选出了最优的参数了;然后循环结束,接着进行正式的MC(运动补偿)。原来不懂这里为何进行了两次MC,后来和大神交流了一下,第一次是所有的MV/MVP都进行运动补偿,是为了计算代价,然后得到最优的参数后,最终做一次MC确定最终代价。
广义B帧技术
在高效的预测模式下,HEVC仍然采用了H.264中的B预测方式,同时还增加了广义B(Generalized P and B picture,GPB)预测方式取代低时延应用场景中的P预测方式。GPB预测结构是指对传统P帧采取类似于B帧的双向预测方式进行预测。在这种预测方式下,前向和后向参考列表中的参考图像都必须为当前图像之前的图像,且两个参考列表完全一致。对P帧采取B帧的运动预测方式增加了运动估计的准确度,提高了编码效率,同时也有利于编码流程的统一。具体细节可以参考博客:http://blog.csdn.net/yangxiao_xiang/article/details/9045777 这里写一下我的简单理解,这个就是把P帧当成B帧,B帧不是前后向预测嘛,P帧预测的时候,也前后向预测,但是它参考列表和普通的P帧其实一样,只是分成前向后向用了两次,也就是说普通的P帧本来只有一个前向的参考列表,现在把它当成B帧来预测,但是由于没有后向的参考列表,就把前向的用两次。
函数流程
TEncSearch::predInterSearch的详解:
1、有个GPB_SIMPLE_UNI宏,表示广义B帧技术GPB,MvdL1ZeroFlag是一个和GPB技术相关的标志,如果它为true,那么表示使用GPB技术
2、对于CU下的每一个PU,遍历参考列表中的每一个图像,进行运动估计,找出最合适的参考帧以及对应的MV
3、如果是B类型的slice,因为他有两个MV,我们需要对后向参考的预测块进行运动补偿,motionCompensation( pcCU, pcYuvPred, REF_PIC_LIST_1, iPartIdx );在运动补偿之后,重新进行运动估计,找出合适的MV
4、保存MV的一些相关信息
5、如果分割类型不是2Nx2N,即一个CU会被划分成为多个PU,那么应该计算并合并它们的运动估计代价
6、进行运动补偿motionCompensation(cu, pu, *predYuv, true, bChromaMC);这是通用的,无论是P类型还是B类型的slice
发现HM16.3和原博主给的代码几乎一模一样,没什么变化,为了节约时间,放原博主的代码:下面的代码为了方便理解,删除了定义ZERO_MVD_EST宏才会生效的代码,以及其他的无关的代码
#if AMP_MRG Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv*& rpcPredYuv, TComYuv*& rpcResiYuv, TComYuv*& rpcRecoYuv, Bool bUseRes, Bool bUseMRG ) #else Void TEncSearch::predInterSearch( TComDataCU* pcCU, TComYuv* pcOrgYuv, TComYuv*& rpcPredYuv, TComYuv*& rpcResiYuv, TComYuv*& rpcRecoYuv, Bool bUseRes ) #endif { // ---------删除无关代码 // 当前CU下的所有PU,请注意PU是由CU划分得到的! for ( Int iPartIdx = 0; iPartIdx < iNumPart; iPartIdx++ ) { // ---------删除无关代码 // 得到某种模式下CU块的比特数 xGetBlkBits( ePartSize, pcCU->getSlice()->isInterP(), iPartIdx, uiLastMode, uiMbBits); // 得到当前PU的索引和大小 pcCU->getPartIndexAndSize( iPartIdx, uiPartAddr, iRoiWidth, iRoiHeight ); #if AMP_MRG Bool bTestNormalMC = true; if ( bUseMRG && pcCU->getWidth( 0 ) > 8 && iNumPart == 2 ) { bTestNormalMC = false; } if (bTestNormalMC) { #endif // Uni-directional prediction // 遍历两个参考图像列表(如果是P帧,只参考一个列表;如果是B帧,会参考两个列表) // 过这里就找到了应该使用哪个参考帧以及以及对应的MV 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++ ) { // ---------删除无关代码 // AMVP处理 xEstimateMvPredAMVP( pcCU, pcOrgYuv, iPartIdx, eRefPicList, iRefIdxTemp, cMvPred[iRefList][iRefIdxTemp], false, &biPDistTemp); // ---------删除无关代码 // 更新最优的参数 // ---------删除无关代码 #if GPB_SIMPLE_UNI // 广义B帧技术GPB,相关细节可以参考http://blog.csdn.net/yangxiao_xiang/article/details/9045777 // list1(只有B帧使用) if ( iRefList == 1 ) // list 1 { // 表示广义B帧技术GPB if ( pcCU->getSlice()->getList1IdxToList0Idx( iRefIdxTemp ) >= 0 ) { // 对于使用了广义的B帧技术,不再进行运动估计,而是直接计算代价 // ---------删除无关代码 } // 普通的B帧 else { // 运动估计 xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp ); } } // list0(P帧或者B帧使用) else { // 直接进行运动估计 xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp ); } #else // else of GPB_SIMPLE_UNI // 没有使用广义B帧技术 xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPred[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp ); #endif // end of GPB_SIMPLE_UNI xCopyAMVPInfo(pcCU->getCUMvField(eRefPicList)->getAMVPInfo(), &aacAMVPInfo[iRefList][iRefIdxTemp]); // must always be done ( also when AMVP_MODE = AM_NONE ) // 选择最优的MVP xCheckBestMVP(pcCU, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPred[iRefList][iRefIdxTemp], aaiMvpIdx[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp); // ---------删除无关代码 } } // Bi-directional prediction // 如果是B帧,且isBipredRestriction(用来判断当前PU尺寸是否为8,而且划分模式是不是2Nx2N),那么进入 if ( (pcCU->getSlice()->isInterB()) && (pcCU->isBipredRestriction(iPartIdx) == false) ) { // ---------删除无关代码 // MvdL1ZeroFlag这个东西也是和GPB相关的,那么进行运动补偿 if(pcCU->getSlice()->getMvdL1ZeroFlag()) { // ---------删除无关代码 // 运动补偿 motionCompensation( pcCU, pcYuvPred, REF_PIC_LIST_1, iPartIdx ); // ---------删除无关代码 } else { uiMotBits[0] = uiBits[0] - uiMbBits[0]; uiMotBits[1] = uiBits[1] - uiMbBits[1]; uiBits[2] = uiMbBits[2] + uiMotBits[0] + uiMotBits[1]; } // 4-times iteration (default) Int iNumIter = 4; // fast encoder setting: only one iteration if ( m_pcEncCfg->getUseFastEnc() || pcCU->getSlice()->getMvdL1ZeroFlag()) { iNumIter = 1; } // 遍历1次或者4次 for ( Int iIter = 0; iIter < iNumIter; iIter++ ) { Int iRefList = iIter % 2; if ( m_pcEncCfg->getUseFastEnc() ) { if( uiCost[0] <= uiCost[1] ) { iRefList = 1; } else { iRefList = 0; } } else if ( iIter == 0 ) { iRefList = 0; } // 如果不使用GPB技术,且是第一次迭代,那么进行运动补偿 if ( iIter == 0 && !pcCU->getSlice()->getMvdL1ZeroFlag()) { // ---------删除无关代码 // 运动补偿 motionCompensation ( pcCU, pcYuvPred, RefPicList(1-iRefList), iPartIdx ); } // 当前的参考列表 RefPicList eRefPicList = ( iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 ); if(pcCU->getSlice()->getMvdL1ZeroFlag()) { iRefList = 0; eRefPicList = REF_PIC_LIST_0; } Bool bChanged = false; iRefStart = 0; iRefEnd = pcCU->getSlice()->getNumRefIdx(eRefPicList)-1; // 遍历参考列表的所有参考帧,进行运动估计 for ( Int iRefIdxTemp = iRefStart; iRefIdxTemp <= iRefEnd; iRefIdxTemp++ ) { // ---------删除无关代码 // call ME // 运动估计 xMotionEstimation ( pcCU, pcOrgYuv, iPartIdx, eRefPicList, &cMvPredBi[iRefList][iRefIdxTemp], iRefIdxTemp, cMvTemp[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp, true ); xCopyAMVPInfo(&aacAMVPInfo[iRefList][iRefIdxTemp], pcCU->getCUMvField(eRefPicList)->getAMVPInfo()); // 检查最好的MVP xCheckBestMVP(pcCU, eRefPicList, cMvTemp[iRefList][iRefIdxTemp], cMvPredBi[iRefList][iRefIdxTemp], aaiMvpIdxBi[iRefList][iRefIdxTemp], uiBitsTemp, uiCostTemp); // 如果找到了一个代价更小的方式,那么更新 if ( uiCostTemp < uiCostBi ) { // ---------删除无关代码 } } // for loop-iRefIdxTemp if ( !bChanged ) { // ---------删除无关代码 } } // for loop-iter } // if (B_SLICE) #if AMP_MRG } //end if bTestNormalMC #endif // ---------删除无关代码 #if AMP_MRG // 这个if里面只是保存了一些MV的信息 if (bTestNormalMC) { #endif // ---------删除无关代码 #if AMP_MRG } // end if bTestNormalMC #endif // 如果分割类型不是2Nx2N,即一个CU会被划分成为多个PU // 那么应该计算并合并它们的运动估计代价 if ( pcCU->getPartitionSize( uiPartAddr ) != SIZE_2Nx2N ) { // ---------删除无关代码 #if AMP_MRG // calculate ME cost // ---------删除无关代码 if (bTestNormalMC) { xGetInterPredictionError( pcCU, pcOrgYuv, iPartIdx, uiMEError, m_pcEncCfg->getUseHADME() ); uiMECost = uiMEError + m_pcRdCost->getCost( uiMEBits ); } #else // calculate ME cost // 计算运动估计的代价 UInt uiMEError = MAX_UINT; xGetInterPredictionError( pcCU, pcOrgYuv, iPartIdx, uiMEError, m_pcEncCfg->getUseHADME() ); // ---------删除无关代码 #endif // save ME result. // ---------删除无关代码 // find Merge result UInt uiMRGCost = MAX_UINT; // 合并估计信息 xMergeEstimation( pcCU, pcOrgYuv, iPartIdx, uiMRGInterDir, cMRGMvField, uiMRGIndex, uiMRGCost, cMvFieldNeighbours, uhInterDirNeighbours, numValidMergeCand); // 设置运动估计的结果 if ( uiMRGCost < uiMECost ) { // set Merge result // ---------删除无关代码 } else { // set ME result // ---------删除无关代码 } } // MC // 运动补偿 motionCompensation ( pcCU, rpcPredYuv, REF_PIC_LIST_X, iPartIdx ); } // end of for ( Int iPartIdx = 0; iPartIdx < iNumPart; iPartIdx++ ) setWpScalingDistParam( pcCU, -1, REF_PIC_LIST_X ); return; }
- HEVC函数入门(19)——帧间编码AMVP
- HEVC函数入门(16)——Slice编码
- HEVC函数入门(17)——编码一个CU
- HEVC函数入门(23)——熵编码&CABAC
- HEVC函数入门(2)——帧内编码一个CU
- HEVC代码学习15:AMVP相关函数
- HM编码器代码阅读(14)——帧间预测之AMVP模式(二)predInterSearch函数
- HM编码器代码阅读(15)——帧间预测之AMVP模式(三)xGetBlkBits函数
- HEVC函数入门(14)——建议先看:整个编码流程以及相关的函数
- HEVC函数入门(18)——帧间预测的原理
- HEVC函数入门(21)——帧间预测之Merge
- HM编码器代码阅读(32)——帧间预测之AMVP/Merge模式(七)encodeResAndCalcRdInterCU函数:残差计算、变换量化
- HEVC函数入门(13)——HEVC中容易混淆的类和结构
- HEVC函数入门(8)——变换的实现
- HEVC函数入门(9)——tile相关
- HEVC函数入门(22)——变换&量化
- HEVC函数入门(24)——比特流
- HEVC新技术(一):基于MVC的AMVP技术
- js学习
- webrtc中关于VAD的总结
- 区块链在中国(1):IBM HyperLedger fabric
- Android的DrawText详解
- RecyclerView嵌套RecyclerView
- HEVC函数入门(19)——帧间编码AMVP
- Oracle实现主键自增长的几种方式
- 《数据库系统概念》(王珊)——Chapter6:关系数据理论 ------数据依赖,函数依赖,码
- 安卓编译进阶<Android:boot art+oat>功能的打包与取消及追溯代码思路历程
- 工厂设计模式最佳实践小结
- excel读取
- Java-A除以B (20)
- jqGrid的简单应用
- 实现嵌套滑动——NestedScrollingParent与NestedScrollingChild接口及其实现