• HM编码器代码阅读(14)——帧间预測之AMVP模式(二)predInterSearch函数


    简单介绍

        predInterSearch基本的工作是ME(运动预计)和MC(运动补偿)。

        函数中有一个bTestNormalMC变量。它表示是否进行正常的MC过程,正常的MC过程就是进行ME再进行MC。


        正常的MC流程是,遍历全部的參考帧,进行ME(运动预计:xEstimateMvPredAMVP和xMotionEstimation),然后记录MVP或者MV的信息,进行MC(运动补偿,目的是选出最优的參数)。然后更新最优的參数,遍历全然部的參考帧之后。就选出了最优的參数了;然后循环结束。接着进行正式的MC(运动补偿)。


    广义B帧技术

        在高效的预測模式下,HEVC仍然採用了H.264中的B预測方式,同一时候还添加了广义B(Generalized P and B picture。GPB)预測方式代替低时延应用场景中的P预測方式。GPB预測结构是指对传统P帧採取类似于B帧的双向预測方式进行预測。在这样的预測方式下。前向和后向參考列表中的參考图像都必须为当前图像之前的图像,且两个參考列表全然一致。

    对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

    以下的代码为了方便理解,删除了定义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; }





  • 相关阅读:
    mac 系统安装selenium注意事项
    (mac系统下)mysql 入门
    Mac系统下配置JAVA Maven Ant 环境变量
    解决mac 下mysql安装后root用户登录密码错误问题
    关于使用eclipse maven UpdateProject时报错,无法更新本地仓库的问题解决方案
    201920201学期20192403《网络空间安全专业导论》第一周学习总结
    与数值相关的全局方法 菜鸟
    Jquery插件开发 菜鸟
    Jquery对象和DOM对象Jquery API (1) 菜鸟
    新的一年,新的生活
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7241883.html
Copyright © 2020-2023  润新知