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;
}




  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值