HM编码器代码阅读(37)——帧内预测(四)帧内预测之候选模式列表的建立

候选模式列表的建立


注意:如果没有特别说明,操作的对象都是亮度块(Luma)


     帧内预测也可以分成两种方式,一种是常规方式,另一种是快速方式

在常规方式


    所有的35种帧内预测模式都被添加到候选模式列表中,在后续的惭怍中,对每一个模式都进行预测、变换、量化等操作,速度很慢。

    相关代码在estIntraPredQT函数中

		UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
		Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8

		Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true

		// 使用快速模式
		if (doFastSearch)
		{
			// 省略快速模式的实现代码
		}
		// 使用常规方式
		else
		{
			for( Int i=0; i < numModesForFullRD; i++)
			{
				uiRdModeList[i] = i;
			}
		}



在快速方式


    先遍历35种帧内预测模式,对于每一种模式都进行预测操作,选出代价最优的几种模式作为候选模式,添加到候选模式列表中。

    同时,因为相邻的PU通常具有很强的相关性,因此,可以使用相邻PU的帧内模式来预测当前PU的帧内模式。预测之后可以得到若干个模式,把这些模式加入模式候选列表中。

    在后续的操作中,只对这几种模式进行预测、变换、量化等操作,因为候选列表中的模式的数量比较少,因此速度会很快。


    相关代码在estIntraPredQT函数中

		UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];
		Int numModesForFullRD = g_aucIntraModeNumFast[ uiWidthBit ];//8

		Bool doFastSearch = (numModesForFullRD != numModesAvailable);//true

		// 使用快速模式
		if (doFastSearch)
		{
			assert(numModesForFullRD < numModesAvailable);

			for( Int i=0; i < numModesForFullRD; i++ ) 
			{
				// 用于存储每一种模式的消耗
				CandCostList[ i ] = MAX_DOUBLE;
			}
			CandNum = 0;

			// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式
			for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35
			{
				UInt uiMode = modeIdx;

				// 对亮度块进行预测
				predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );

				// use hadamard transform here
				// 使用hadamard变换,计算SATD的值
				UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );

				UInt   iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );

				// 计算此种模式的代价
				Double cost      = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();

				// 更新候选列表
				CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
			}

#if FAST_UDI_USE_MPM
			Int uiPreds[3] = {-1, -1, -1};
			Int iMode = -1;
			// 根据相邻块的预测模式来对当前块的模式进行预测,得到若干模式(称为预测模式),存放在uiPreds中
			Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );
			if( iMode >= 0 )
			{
				// 将候选列表的索引设置为此模式
				numCand = iMode;
			}

			// 遍历预测的模式,如果它不在模式候选列表中,那么把它添加到模式候选列表中
			for( Int j=0; j < numCand; j++)
			{
				Bool mostProbableModeIncluded = false;
				Int mostProbableMode = uiPreds[j];

				for( Int i=0; i < numModesForFullRD; i++)
				{
					mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
				}
				if (!mostProbableModeIncluded)
				{
					uiRdModeList[numModesForFullRD++] = mostProbableMode;
				}
			}
#endif // FAST_UDI_USE_MPM
		}
		// 使用常规方式
		else
		{
			for( Int i=0; i < numModesForFullRD; i++)
			{
				uiRdModeList[i] = i;
			}
		}

从所有的35种模式中选出若干最优模式

			// 遍历35种帧内预测模式,选取若干个代价比较小的模式作为后续处理的模式
			for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ ) // 总共有35种模式,numModesAvailable = 35
			{
				UInt uiMode = modeIdx;

				// 对亮度块进行预测
				predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );

				// use hadamard transform here
				// 使用hadamard变换,计算SATD的值
				UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );

				UInt   iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );

				// 计算此种模式的代价
				Double cost      = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();

				// 更新候选列表
				CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );
			}
亮度块的帧内预测函数

// 亮度块的帧内预测
Void TComPrediction::predIntraLumaAng(TComPattern* pcTComPattern, UInt uiDirMode, Pel* piPred, UInt uiStride, Int iWidth, Int iHeight, Bool bAbove, Bool bLeft )
{
	Pel *pDst = piPred;
	Int *ptrSrc;

	assert( g_aucConvertToBit[ iWidth ] >= 0 ); //   4x  4
	assert( g_aucConvertToBit[ iWidth ] <= 5 ); // 128x128
	assert( iWidth == iHeight  );

	// 获取数据的指针
	ptrSrc = pcTComPattern->getPredictorPtr( uiDirMode, g_aucConvertToBit[ iWidth ] + 2, m_piYuvExt );

	// get starting pixel in block
	// 获取块中的开始像素
	Int sw = 2 * iWidth + 1; // 9

	// Create the prediction
	// 如果指定了planar模式
	if ( uiDirMode == PLANAR_IDX )
	{
		// planar预测模式
		xPredIntraPlanar( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight );
	}
	else
	{
		// 没有指定planar模式
		// 区分水平或者垂直模式进行预测
		if ( (iWidth > 16) || (iHeight > 16) )
		{
			xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, false );
		}
		else
		{
			xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, true );

			// 观察是否为DC模式
			if( (uiDirMode == DC_IDX ) && bAbove && bLeft )
			{
				// 对DC模式进行滤波
				xDCPredFiltering( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight);
			}
		}
	}
}


模式的预测

    因为相邻的PU通常具有很强的相关性,因此,可以使用相邻PU的帧内模式来预测当前PU的帧内模式。预测之后可以得到若干个模式,把这些模式加入模式候选列表中。

    相关代码在estIntraPredQT函数中

			Int uiPreds[3] = {-1, -1, -1};
			Int iMode = -1;
			// 根据相邻块的预测模式来对当前块的模式进行预测,得到若干模式(称为预测模式),存放在uiPreds中
			Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );
			if( iMode >= 0 )
			{
				// 将候选列表的索引设置为此模式
				numCand = iMode;
			}

			// 遍历预测的模式,如果它不在模式候选列表中,那么把它添加到模式候选列表中
			for( Int j=0; j < numCand; j++)
			{
				Bool mostProbableModeIncluded = false;
				Int mostProbableMode = uiPreds[j];

				for( Int i=0; i < numModesForFullRD; i++)
				{
					mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]);
				}
				if (!mostProbableModeIncluded)
				{
					uiRdModeList[numModesForFullRD++] = mostProbableMode;
				}
			}

模式预测函数

预测的步骤如下:

1、假设当前块的左边是A,上面的是B,candList数组用于存放预测的模式    
2、如果modeA==modeB
    (1)modeA和modeB都是planar或者DC模式,那么candList[0] =planar,candList[1]=DC,candList[2]=26
    (2)modeA和modeB都是角度模式,那么,candList[0]=modeA,candList[1]和candList[2]等于modeA相邻的两个模式
3、如果modeA!=modeB,那么candList[0]=modeA,candList[1]=modeB,candList[2]则要分几种情况
   (1)modeA和modeB都不是planar模式,那么candList[2]=planar
   (2)当(1)不满足,而且modeA和modeB都不是DC模式,那么candList[2]=DC
   (3)当(1)和(2)不满足,candList[2]=26

Int TComDataCU::getIntraDirLumaPredictor( UInt uiAbsPartIdx, Int* uiIntraDirPred, Int* piMode  )
{
	TComDataCU* pcTempCU;
	UInt        uiTempPartIdx;
	Int         iLeftIntraDir, iAboveIntraDir;
	Int         uiPredNum = 0;

	// Get intra direction of left PU
	pcTempCU = getPULeft( uiTempPartIdx, m_uiAbsIdxInLCU + uiAbsPartIdx );

	iLeftIntraDir  = pcTempCU ? ( pcTempCU->isIntra( uiTempPartIdx ) ? pcTempCU->getLumaIntraDir( uiTempPartIdx ) : DC_IDX ) : DC_IDX;

	// Get intra direction of above PU
	pcTempCU = getPUAbove( uiTempPartIdx, m_uiAbsIdxInLCU + uiAbsPartIdx, true, true );

	iAboveIntraDir = pcTempCU ? ( pcTempCU->isIntra( uiTempPartIdx ) ? pcTempCU->getLumaIntraDir( uiTempPartIdx ) : DC_IDX ) : DC_IDX;

	uiPredNum = 3;
	if(iLeftIntraDir == iAboveIntraDir)
	{
		if( piMode )
		{
			*piMode = 1;
		}

		if (iLeftIntraDir > 1) // angular modes
		{
			uiIntraDirPred[0] = iLeftIntraDir;
			uiIntraDirPred[1] = ((iLeftIntraDir + 29) % 32) + 2;
			uiIntraDirPred[2] = ((iLeftIntraDir - 1 ) % 32) + 2;
		}
		else //non-angular
		{
			uiIntraDirPred[0] = PLANAR_IDX;
			uiIntraDirPred[1] = DC_IDX;
			uiIntraDirPred[2] = VER_IDX; 
		}
	}
	else
	{
		if( piMode )
		{
			*piMode = 2;
		}
		uiIntraDirPred[0] = iLeftIntraDir;
		uiIntraDirPred[1] = iAboveIntraDir;

		if (iLeftIntraDir && iAboveIntraDir ) //both modes are non-planar
		{
			uiIntraDirPred[2] = PLANAR_IDX;
		}
		else
		{
			uiIntraDirPred[2] =  (iLeftIntraDir+iAboveIntraDir)<2? VER_IDX : DC_IDX;
		}
	}

	return uiPredNum;
}



  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值