HEVC中xcompressCU函数

本文详细介绍了HEVC视频编码标准中CU(编码单元)的压缩流程。从原始YUV数据复制到计算量化步长,再到遍历不同量化值确定最优模式,文章深入探讨了CU压缩的各个环节,并解释了如何通过递归操作实现最佳率失真优化。
详细流程:
(1)复制原始yuv数据
(2)计算当前CU四个角的坐标
(3)调用xComputeQP计算量化步长
(4)判断这个CU是不是slice第一个CU,是不是slice最后的一个CU
(5)判断这个CU是不是在图像内部(即不在图像的边缘)
(6)看一般的情况(不是slice第一个也不是最后一个CU,CU不在图像的边缘)。
     1)从最小的量化步长到最大的量化步长遍历每一个量化步长,对每一个量化步长执行下面的操作(目的是选出最优的量化步长):
          Ⅰ)判断是否为无损模式
          Ⅱ)初始tempCU的数据——initEstData(这也是个比较重要的函数)
          Ⅲ)如果不是I帧(即进行的是帧间预测),执行下面步骤:
               a)检测是否使用了EarlySkipDetection,如果使用了,就调用xCheckRDCostInter进行预测变换量化等操作,然后重新设置tempCU的估计数据——initEstData
               b)调用xCheckRDCostMerge2Nx2N来检测merge模式下的率失真(其实内部也是经过了一系列的预测变换量化的操作)
               c)设置tempCU的估计数据
               d)如果没有使用EarlySkipDetection,那么调用xCheckRDCostInter进行帧间预测变换量化等操作,再设置tempCU的估计数据
     2)如果没有使用earlyDetectionSkipMode,从最小量化步长到最大量化步长,遍历每一种量化步长:
          Ⅰ)判断是否为无损模式
          Ⅱ)初始化tempCU的估计数据
          Ⅲ)如果不是I帧(即进行的是帧间预测),执行下面步骤(实际是选择一种分割模式):
               a)如果CU是8x8或者是2Nx2N,调用xCheckRDCostInter进行帧间预测、变换、量化等操作,再设置tempCU的估计数据
               b)如果是2NxN或者Nx2N,那么调用xCheckRDCostInter进行帧间预测、变换、量化等操作,再设置tempCU的估计数据
               c)是否使用了准确的AMP方式(注意不是MVP),如果使用了,那么进行下面的操作(实际就是依次测试2NxnU,2NxnD,nLx2N、nRx2N哪种分割模式更好):
                    ①调用deriveTestModeAMP,测试是否需要对垂直和横向使用AMP模式
                    ②如果是水平AMP模式,那么表示分割模式是2NxnU或者2NxnD,仍然是调用xCheckRDCostInter(参数不一样),再设置tempCU的估计数据
                    ③如果是merge的水平AMP模式,同②一样调用,只不过参数不一样
                    ④如果是垂直的AMP模式,那么表示分割模式是nLx2N或者nRx2N,仍然是调用xCheckRDCostInter(参数不一样),再设置tempCU的估计数据
                    ⑤如果是merge的垂直AMP模式,同④一样调用,只不过参数不一样
          Ⅳ)如果是I帧,那么进行下面的步骤:
               a)调用xCheckRDCostIntra进行帧内预测、变换和量化
               b)设置tempCU的估计数据
          Ⅴ)如果使用了PCM模式:
               a)调用xCheckIntraPCM进行帧内预测、变换量化等
               b)设置tempCU的估计数据
     3)重置熵编码器的比特数,对split标志进行编码,然后统计比特数,计算总的消耗
     4)从最小量化步长到最大的量化步长进行遍历:
          Ⅰ)初始化tempCU的估计数据
          Ⅱ)如果还没有达到最深的深度,那么需要继续分割成四个子CU:
               a)对子最佳CU、子临时CU进行初始化(initSubCU)
               b)调用xCompressCU进行递归操作
               c)保存最佳的预测数据到tempCU中
          Ⅲ)计算代价
          Ⅳ)调用xCheckBestMode选择最好的模式
    

总结,其实compressCU的作用就是从LCU开始深度遍历,计算每一个depth上最优的模式,再综合比较各个depth上最优的模式,选出最优的模式。

// ====================================================================================================================
// Protected member functions
// ====================================================================================================================
/** Compress a CU block recursively with enabling sub-LCU-level delta QP
 *\param   rpcBestCU
 *\param   rpcTempCU
 *\param   uiDepth
 *\returns Void
 *
 *- for loop of QP value to compress the current CU with all possible QP
 */
/*
** 压缩CU的内部函数
*/
#if AMP_ENC_SPEEDUP // 编码加速宏
Void TEncCu::xCompressCU(TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize)
#else
Void TEncCu::xCompressCU( TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth )
#endif
{
	/*
	由此总结,每个深度的预测用的都是temp,
	预测完后跟best比较并交换。best保留作为当前深度的预测数据,
	而temp再次初始化。在下一深度的4个子CU预测中用的是subtemp,
	每预测完一个子CU,就跟subbest比较交换,再把subbest的数据复制到已经初始化的temp的相应位置。
	当temp获取完4个子CU的subbest的数据后,就代表了整个下一深度的数据,这时再与代表当前深度数据的best比较交换
	*/

	/*
	有如下的对应关系

	CU partition ---- encodeSplitFlag
	PU partition ---- encodePartSize
	TU partition ---- encodeTransformSubpFlag
	*/
	// 获取CU所在的图像
	TComPic* pcPic = rpcBestCU->getPic();
	DEBUG_STRING_NEW(sDebug)

		// get Original YUV data from picture
		// 从图像中获取原始YUV数据
		// getZorderIdxInCU获取CU在Z形扫描中的顺序
		m_ppcOrigYuv[uiDepth]->copyFromPicYuv(pcPic->getPicYuvOrg(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU());

	// variable for Early CU determination
	//该CU是否还需要细分的标志
	Bool    bSubBranch = true;

	// variable for Cbf fast mode PU decision
	//块状的PU标志
	Bool    doNotBlockPu = true;
	//早检测跳过模式
	Bool    earlyDetectionSkipMode = false;
	//是否为边界
	Bool bBoundary = false;

	// 获取当前CU的左边像素(X)
	// 深度不同,四个位置的像素坐标也会不同
	UInt uiLPelX = rpcBestCU->getCUPelX();// 0  (left)
	UInt uiRPelX = uiLPelX + rpcBestCU->getWidth(0) - 1;// 63 (right)
	UInt uiTPelY = rpcBestCU->getCUPelY(); // 0    (top)
	UInt uiBPelY = uiTPelY + rpcBestCU->getHeight(0) - 1;// 63   (buttom)

	// 计算量化步长
	// 深度不同,量化步长可能也不相同
	// 由于HM15.0中的默认配置并没有开启自适应量化步长
	// 因此xComputeQP返回的结果总是32
	// 可以设置的返回其实可以从0-51
	Int iBaseQP = xComputeQP(rpcBestCU, uiDepth);// 基本的量化步长32 
	// 最小的步长  
	Int iMinQP;
	// 最大的步长
	Int iMaxQP;
	//是否增加最小的量化步长
	Bool isAddLowestQP = false;

	const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();

	//计算最小/最大的量化步长
	if ((g_uiMaxCUWidth >> uiDepth) >= rpcTempCU->getSlice()->getPPS()->getMinCuDQPSize())
	{
		Int idQP = m_pcEncCfg->getMaxDeltaQP();// 配置文件中MaxDeltaQP设置为0
		iMinQP = Clip3(-rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - idQP);//32
		iMaxQP = Clip3(-rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP + idQP);//32
	}
	else
	{
		iMinQP = rpcTempCU->getQP(0);
		iMaxQP = rpcTempCU->getQP(0);
	}

	// 使用码率控制  
	// 注意这里的QP使用了,码率控制对象计算出来的QP  
	// 通过QP,码率控制对象控制了编码器的比特率  
	// 没有进去,因为没有使用码率控制
	if (m_pcEncCfg->getUseRateCtrl())
	{
		iMinQP = m_pcRateCtrl->getRCQP();
		iMaxQP = m_pcRateCtrl->getRCQP();
	}

	// transquant-bypass (TQB) processing loop variable initialisation ---
	//32
	const Int lowestQP = iMinQP; // For TQB, use this QP which is the lowest non TQB QP tested (rather than QP'=0) - that way delta QPs are smaller, and TQB can be tested at all CU levels.
	// 是否启用了变换bypass标志
	if ((rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag()))
	{
		isAddLowestQP = true; // mark that the first iteration is to cost TQB mode.
		iMinQP = iMinQP - 1;  // increase loop variable range by 1, to allow testing of TQB mode along with other QPs
		if (m_pcEncCfg->getCUTransquantBypassFlagForceValue())
		{
			iMaxQP = iMinQP;
		}
	}

	// If slice start or slice end is within this cu...
	//获取CU所属的slice
	TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx());
	//false
	//是否为slice开始的CU
	Bool bSliceStart = pcSlice->getSliceSegmentCurStartCUAddr() > rpcTempCU->getSCUAddr() && pcSlice->getSliceSegmentCurStartCUAddr()<rpcTempCU->getSCUAddr() + rpcTempCU->getTotalNumPart();
	//false
	//是否为slice最后的CU
	Bool bSliceEnd = (pcSlice->getSliceSegmentCurEndCUAddr()>rpcTempCU->getSCUAddr() && pcSlice->getSliceSegmentCurEndCUAddr() < rpcTempCU->getSCUAddr() + rpcTempCU->getTotalNumPart());
	// true
	// 是否在图片内部(即不在图像的边缘)
	Bool bInsidePicture = (uiRPelX < rpcBestCU->getSlice()->getSPS()->getPicWidthInLumaSamples()) && (uiBPelY < rpcBestCU->getSlice()->getSPS()->getPicHeightInLumaSamples());
	// We need to split, so don't try these modes.
	/*
	** 核心
	*/
	if (!bSliceEnd && !bSliceStart && bInsidePicture)//不是slice开始的CU也不是slice结束的CU,同时在图像内部
	{
		// 此循环测试每一种量化步长,计算率失真,选出最优的QP  
		for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)//1次循环,iMinQP==iMaxQP 
		{
			// 是否为无损模式  
			const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP);

			if (bIsLosslessMode)
			{
				iQP = lowestQP;
			}

			m_ChromaQpAdjIdc = 0;
			if (pcSlice->getUseChromaQpAdj())
			{
				/* Pre-estimation of chroma QP based on input block activity may be performed
				 * here, using for example m_ppcOrigYuv[uiDepth] */
				/* To exercise the current code, the index used for adjustment is based on
				 * block position
				 */
				Int lgMinCuSize = pcSlice->getSPS()->getLog2MinCodingBlockSize();
				m_ChromaQpAdjIdc = ((uiLPelX >> lgMinCuSize) + (uiTPelY >> lgMinCuSize)) % (pcSlice->getPPS()->getChromaQpAdjTableSize() + 1);
			}
			// 初始化(很重要)
			// 当前CU初始化估计数据 对当前CU以4x4大小进行初始化
			rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);

			// do inter modes, SKIP and 2Nx2N
			/*
			** 在处理所有的其他模式之前,先处理帧间skip和2Nx2N的模式
			** 特别是对于2Nx2N的划分,要分两次处理:
			** 1、尝试merge模式——xCheckRDCostMerge2Nx2N
			** 2、尝试普通的帧间预测(即AMVP)——xCheckRDCostInter
			*/
			// 如果不是I条带,就进行帧间预测编码
			if (rpcBestCU->getSlice()->getSliceType() != I_SLICE)
			{
				// 2Nx2N
				// 如果使用了早期skip检测  
				if (m_pcEncCfg->getUseEarlySkipDetection())//  这里是一个快速算法的判断语句,默认不执行这里
				{
					// 预测变换量化等等步骤
					xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));  //skip 2NX2N
					rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
					//by Competition for inter_2Nx2N  
				}
				// SKIP
				// 检测merge模式下的率失真
				xCheckRDCostMerge2Nx2N(rpcBestCU, rpcTempCU DEBUG_STRING_PASS_INTO(sDebug), &earlyDetectionSkipMode);//by Merge for inter_2Nx2N
				rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);

				// 没有使用早期skip检测 
				if (!m_pcEncCfg->getUseEarlySkipDetection())
				{
					// 2Nx2N, NxN
					xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));
					rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
					if (m_pcEncCfg->getUseCbfFastMode())
					{
						doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
					}
				}
			}

			if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.
			{
				iQP = iMinQP;
			}
		}
		// 没有使用早期检测跳过模式,进入这里!!!!
		if (!earlyDetectionSkipMode)
		{
			// 在实际的处理过程当中,对LCU的划分都是以4x4大小的块进行划分的,这是为了处理方便,然后以Z扫描的方式进行扫描,这也是为了方便递归  
			// 遍历每一种量化步长  
			for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)
			{
				const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP); // If lossless, then iQP is irrelevant for subsequent modules.

				if (bIsLosslessMode)
				{
					iQP = lowestQP;
				}

				rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);

				// do inter modes, NxN, 2NxN, and Nx2N
				/*
				** 普通的帧间预测(普通的帧间预测就是AMVP)开始:
				** 注意:这里不再处理merge模式和普通帧间的2Nx2N划分模式,
				** 这是因为前面已经处理过2Nx2N的划分模式了,merge模式只对于2Nx2N的划分才有效
				** 因此下面的处理是没有merge模式和2Nx2N的划分模式的
				*/
				// 如果不是I条带,那么进行帧间预测编码
				if (rpcBestCU->getSlice()->getSliceType() != I_SLICE)
				{
					// 2Nx2N, NxN
					// 一下三个if判断的目的是决定使用哪种类型的分割NxN,2NxN,Nx2N
					if (!((rpcBestCU->getWidth(0) == 8) && (rpcBestCU->getHeight(0) == 8)))
					{
						if (uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth && doNotBlockPu)
						{
							xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug));
							rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						}
					}

					// Nx2N模式的处理  
					if (doNotBlockPu)
					{
						xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_Nx2N DEBUG_STRING_PASS_INTO(sDebug));
						rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_Nx2N)
						{
							doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
						}
					}
					// 2NxN的模式  
					if (doNotBlockPu)
					{
						xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug));
						rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxN)
						{
							doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
						}
					}

					//! Try AMP (SIZE_2NxnU, SIZE_2NxnD, SIZE_nLx2N, SIZE_nRx2N)
					// 接下来是2NxnU、2NxnD、nLx2N、nRx2N的划分模式的处理  
					/*
					** 接下来的处理有点讲究:
					** 1、首先测试AMP_ENC_SPEEDUP宏(表示是否加快编码速度)是否开启
					** 2、如果AMP_ENC_SPEEDUP宏开启
					**      (1)默认情况下,如果TestAMP_Hor、TestAMP_Ver为真,那么可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
					**      (2)如果TestAMP_Hor、TestAMP_Ver为假,但是开启了AMP_MRG宏,而且TestMergeAMP_Hor、TestMergeAMP_Ver为真,那么还是可以处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
					**          否则不再处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式
					**      (3)由于上面会根据一些条件来判断是否需要处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因此某些时候速度会快一点
					** 3、如果AMP_ENC_SPEEDUP关闭
					**      那么直接处理2NxnU、2NxnD、nLx2N、nRx2N这四种模式,因为没有了条件限制,这四种模式都要测试,因此,速度会慢一点

					*/
					// 是否需要使用准确的AMP方式
					if (pcPic->getSlice(0)->getSPS()->getAMPAcc(uiDepth))
					{
#if AMP_ENC_SPEEDUP
						Bool bTestAMP_Hor = false, bTestAMP_Ver = false;

#if AMP_MRG
						Bool bTestMergeAMP_Hor = false, bTestMergeAMP_Ver = false;

						// 测试TestAMP_Hor和TestAMP_Ver是否为真  
						// 测试,看看是否需要对垂直,横向使用AMP模式
						deriveTestModeAMP(rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver, bTestMergeAMP_Hor, bTestMergeAMP_Ver);
#else
						deriveTestModeAMP (rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver);
#endif

						//! Do horizontal AMP
						// TestAMP_Hor为真的话,可以使用2NxnU和2NxnD这两种划分模式 
						//水平AMP
						if (bTestAMP_Hor)
						{
							// 处理2NxnU模式 
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug));
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}

							// 处理2NxnD模式  
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug));
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
						}
#if AMP_MRG
						// TestMergeAMP_Hor为真的话可以使用2NxnU、2NxnD这两种模式 
						//最优的merge AMP--水平
						else if (bTestMergeAMP_Hor)
						{
							// 处理2NxnU模式 
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug), true);
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							// 处理2NxnD模式  
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug), true);
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
						}
#endif

						//! Do horizontal AMP
						// TestAMP_Ver为真可以处理nLx2N、nRx2N两种模式  
						if (bTestAMP_Ver)
						{
							// 处理nLx2N模式  
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug));
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							// 处理nRx2N模式  
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug));
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
							}
						}
#if AMP_MRG
						// TestMergeAMP_Ver为真可以处理nLx2N、nRx2N模式 
						else if (bTestMergeAMP_Ver)
						{
							// 处理nLx2N模式 
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug), true);
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							// 处理nRx2N模式  
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug), true);
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
							}
						}
#endif

#else
						xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnU );
						rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
						xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_2NxnD );
						rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );
						xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nLx2N );
						rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

						xCheckRDCostInter( rpcBestCU, rpcTempCU, SIZE_nRx2N );
						rpcTempCU->initEstData( uiDepth, iQP, bIsLosslessMode );

#endif
					}// 帧间预测结束!!!!  
				}

				// do normal intra modes
				// speedup for inter frames
				// 帧内预测开始,帧内预测只有两种划分:2Nx2N、NxN  
				Double intraCost = 0.0;

				if ((rpcBestCU->getSlice()->getSliceType() == I_SLICE) ||
					(rpcBestCU->getCbf(0, COMPONENT_Y) != 0) ||
					((rpcBestCU->getCbf(0, COMPONENT_Cb) != 0) && (numberValidComponents > COMPONENT_Cb)) ||
					((rpcBestCU->getCbf(0, COMPONENT_Cr) != 0) && (numberValidComponents > COMPONENT_Cr))) // avoid very complex intra if it is unlikely
				{
					// 帧内2Nx2N模式 
					xCheckRDCostIntra(rpcBestCU, rpcTempCU, intraCost, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));
					rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
					// 帧内NxN
					if (uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth)
					{
						if (rpcTempCU->getWidth(0) > (1 << rpcTempCU->getSlice()->getSPS()->getQuadtreeTULog2MinSize()))
						{
							Double tmpIntraCost;
							xCheckRDCostIntra(rpcBestCU, rpcTempCU, tmpIntraCost, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug));
							intraCost = std::min(intraCost, tmpIntraCost);
							rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						}
					}
				}// 帧内预测结束!!! 

				// test PCM
				//没有进入,因为没有使用PCM模式
				//bool b = pcPic->getSlice(0)->getSPS()->getUsePCM();
				// 尝试PCM模式
				if (pcPic->getSlice(0)->getSPS()->getUsePCM()
					&& rpcTempCU->getWidth(0) <= (1 << pcPic->getSlice(0)->getSPS()->getPCMLog2MaxSize())
					&& rpcTempCU->getWidth(0) >= (1 << pcPic->getSlice(0)->getSPS()->getPCMLog2MinSize()))
				{
					UInt uiRawBits = getTotalBits(rpcBestCU->getWidth(0), rpcBestCU->getHeight(0), rpcBestCU->getPic()->getChromaFormat(), g_bitDepth);
					UInt uiBestBits = rpcBestCU->getTotalBits();
					if ((uiBestBits > uiRawBits) || (rpcBestCU->getTotalCost() > m_pcRdCost->calcRdCost(uiRawBits, 0)))
					{
						xCheckIntraPCM(rpcBestCU, rpcTempCU);
						rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
					}
				}

				if (bIsLosslessMode) // Restore loop variable if lossless mode was searched.
				{
					iQP = iMinQP;
				}
			}
		}

		// 重置比特数  
		m_pcEntropyCoder->resetBits();
		// 对分割标志进行编码
		m_pcEntropyCoder->encodeSplitFlag(rpcBestCU, 0, uiDepth, true);
		// 比特数量统计  
		rpcBestCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
		rpcBestCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
		// 总的消耗统计  
		rpcBestCU->getTotalCost() = m_pcRdCost->calcRdCost(rpcBestCU->getTotalBits(), rpcBestCU->getTotalDistortion());

		// Early CU determination
		// HM15.0的配置中没有使用早期的CU  
		if (m_pcEncCfg->getUseEarlyCU() && rpcBestCU->isSkipped(0))
		{
			bSubBranch = false;
		}
		else
		{
			bSubBranch = true;
		}
	}//if(!bSliceEnd && !bSliceStart && bInsidePicture )  
	else if (!(bSliceEnd && bInsidePicture))
	{
		bBoundary = true;
	}

	// copy orginal YUV samples to PCM buffer
	// HM15.0的配置中没有使用无损编码,不进入
	if (rpcBestCU->isLosslessCoded(0) && (rpcBestCU->getIPCMFlag(0) == false))
	{
		xFillPCMBuffer(rpcBestCU, m_ppcOrigYuv[uiDepth]);
	}

	if ((g_uiMaxCUWidth >> uiDepth) == rpcTempCU->getSlice()->getPPS()->getMinCuDQPSize())
	{
		Int idQP = m_pcEncCfg->getMaxDeltaQP();
		iMinQP = Clip3(-rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP - idQP);
		iMaxQP = Clip3(-rpcTempCU->getSlice()->getSPS()->getQpBDOffset(CHANNEL_TYPE_LUMA), MAX_QP, iBaseQP + idQP);
	}
	else if ((g_uiMaxCUWidth >> uiDepth) > rpcTempCU->getSlice()->getPPS()->getMinCuDQPSize())
	{
		iMinQP = iBaseQP;
		iMaxQP = iBaseQP;
	}
	else
	{
		Int iStartQP;
		if (pcPic->getCU(rpcTempCU->getAddr())->getSliceSegmentStartCU(rpcTempCU->getZorderIdxInCU()) == pcSlice->getSliceSegmentCurStartCUAddr())
		{
			iStartQP = rpcTempCU->getQP(0);
		}
		else
		{
			UInt uiCurSliceStartPartIdx = pcSlice->getSliceSegmentCurStartCUAddr() % pcPic->getNumPartInCU() - rpcTempCU->getZorderIdxInCU();
			iStartQP = rpcTempCU->getQP(uiCurSliceStartPartIdx);
		}
		iMinQP = iStartQP;
		iMaxQP = iStartQP;
	}

	if (m_pcEncCfg->getUseRateCtrl())
	{
		iMinQP = m_pcRateCtrl->getRCQP();
		iMaxQP = m_pcRateCtrl->getRCQP();
	}

	if (m_pcEncCfg->getCUTransquantBypassFlagForceValue())
	{
		iMaxQP = iMinQP; // If all TUs are forced into using transquant bypass, do not loop here.
	}

	// 从最小量化步长到最大量化步长,递归处理子CU,然后选取最优的量化步长和最优划分模式
	for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)
	{
		//这个字段总是false,因为HEVC是有损的压缩
		const Bool bIsLosslessMode = false; // False at this level. Next level down may set it to true.
		// 以4x4的方式初始临时CU
		rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);

		// further split
		// 进一步的分割
		if (bSubBranch && uiDepth < g_uiMaxCUDepth - g_uiAddCUDepth)
		{
			UChar       uhNextDepth = uiDepth + 1;
			TComDataCU* pcSubBestPartCU = m_ppcBestCU[uhNextDepth];
			TComDataCU* pcSubTempPartCU = m_ppcTempCU[uhNextDepth];
			DEBUG_STRING_NEW(sTempDebug)

				// 进一步的分割,当前CU又被划分成为4个子CU  
			for (UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++)
			{
				// 子CU的最佳CU,初始化,仍然是以4x4的方式初始化CU
				pcSubBestPartCU->initSubCU(rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP);           // clear sub partition datas or init.
				pcSubTempPartCU->initSubCU(rpcTempCU, uiPartUnitIdx, uhNextDepth, iQP);           // clear sub partition datas or init.

				// 判断该CU是否在slice内
				Bool bInSlice = pcSubBestPartCU->getSCUAddr() + pcSubBestPartCU->getTotalNumPart()>pcSlice->getSliceSegmentCurStartCUAddr() && pcSubBestPartCU->getSCUAddr() < pcSlice->getSliceSegmentCurEndCUAddr();
				if (bInSlice && (pcSubBestPartCU->getCUPelX() < pcSlice->getSPS()->getPicWidthInLumaSamples()) && (pcSubBestPartCU->getCUPelY() < pcSlice->getSPS()->getPicHeightInLumaSamples()))
				{
					if (0 == uiPartUnitIdx) //initialize RD with previous depth buffer
					{
						m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
					}
					else
					{
						m_pppcRDSbacCoder[uhNextDepth][CI_CURR_BEST]->load(m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]);
					}

#if AMP_ENC_SPEEDUP // 如果启用了编码加速选项
					DEBUG_STRING_NEW(sChild)
					if (!rpcBestCU->isInter(0))
					{
						// 对CU进行压缩,这是一个递归调用
						xCompressCU(pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), NUMBER_OF_PART_SIZES);
					}
					else
					{

						xCompressCU(pcSubBestPartCU, pcSubTempPartCU, uhNextDepth DEBUG_STRING_PASS_INTO(sChild), rpcBestCU->getPartitionSize(0));
					}
					DEBUG_STRING_APPEND(sTempDebug, sChild)
#else  // 没有使用编码加速选项
					// 递归处理子CU  
					xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth );
#endif

					rpcTempCU->copyPartFrom(pcSubBestPartCU, uiPartUnitIdx, uhNextDepth);         // Keep best part data to current temporary data.
					xCopyYuv2Tmp(pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth);
				}
				else if (bInSlice)
				{
					pcSubBestPartCU->copyToPic(uhNextDepth);
					rpcTempCU->copyPartFrom(pcSubBestPartCU, uiPartUnitIdx, uhNextDepth);
				}
			}

			// 计算并更新最优的代价——begin  
			if (!bBoundary)
			{
				m_pcEntropyCoder->resetBits();
				m_pcEntropyCoder->encodeSplitFlag(rpcTempCU, 0, uiDepth, true);

				rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // split bits
				rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
			}
			// 计算RD代价 
			rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost(rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion());

			if ((g_uiMaxCUWidth >> uiDepth) == rpcTempCU->getSlice()->getPPS()->getMinCuDQPSize() && rpcTempCU->getSlice()->getPPS()->getUseDQP())
			{
				Bool hasResidual = false;
				for (UInt uiBlkIdx = 0; uiBlkIdx < rpcTempCU->getTotalNumPart(); uiBlkIdx++)
				{
					if ((pcPic->getCU(rpcTempCU->getAddr())->getSliceSegmentStartCU(uiBlkIdx + rpcTempCU->getZorderIdxInCU()) == rpcTempCU->getSlice()->getSliceSegmentCurStartCUAddr()) &&
						(rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Y)
						|| (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cb) && (numberValidComponents > COMPONENT_Cb))
						|| (rpcTempCU->getCbf(uiBlkIdx, COMPONENT_Cr) && (numberValidComponents > COMPONENT_Cr))))
					{
						hasResidual = true;
						break;
					}
				}

				UInt uiTargetPartIdx;
				if (pcPic->getCU(rpcTempCU->getAddr())->getSliceSegmentStartCU(rpcTempCU->getZorderIdxInCU()) != pcSlice->getSliceSegmentCurStartCUAddr())
				{
					uiTargetPartIdx = pcSlice->getSliceSegmentCurStartCUAddr() % pcPic->getNumPartInCU() - rpcTempCU->getZorderIdxInCU();
				}
				else
				{
					uiTargetPartIdx = 0;
				}
				if (hasResidual)
				{
#if !RDO_WITHOUT_DQP_BITS
					m_pcEntropyCoder->resetBits();
					m_pcEntropyCoder->encodeQP(rpcTempCU, uiTargetPartIdx, false);
					rpcTempCU->getTotalBits() += m_pcEntropyCoder->getNumberOfWrittenBits(); // dQP bits
					rpcTempCU->getTotalBins() += ((TEncBinCABAC *)((TEncSbac*)m_pcEntropyCoder->m_pcEntropyCoderIf)->getEncBinIf())->getBinsCoded();
					rpcTempCU->getTotalCost() = m_pcRdCost->calcRdCost(rpcTempCU->getTotalBits(), rpcTempCU->getTotalDistortion());
#endif

					Bool foundNonZeroCbf = false;
					rpcTempCU->setQPSubCUs(rpcTempCU->getRefQP(uiTargetPartIdx), rpcTempCU, 0, uiDepth, foundNonZeroCbf);
					assert(foundNonZeroCbf);
				}
				else
				{
					rpcTempCU->setQPSubParts(rpcTempCU->getRefQP(uiTargetPartIdx), 0, uiDepth); // set QP to default QP
				}
			}

			m_pppcRDSbacCoder[uhNextDepth][CI_NEXT_BEST]->store(m_pppcRDSbacCoder[uiDepth][CI_TEMP_BEST]);

			Bool isEndOfSlice = rpcBestCU->getSlice()->getSliceMode() == FIXED_NUMBER_OF_BYTES
				&& (rpcBestCU->getTotalBits() > rpcBestCU->getSlice()->getSliceArgument() << 3);
			Bool isEndOfSliceSegment = rpcBestCU->getSlice()->getSliceSegmentMode() == FIXED_NUMBER_OF_BYTES
				&& (rpcBestCU->getTotalBits() > rpcBestCU->getSlice()->getSliceSegmentArgument() << 3);
			if (isEndOfSlice || isEndOfSliceSegment)
			{
				if (m_pcEncCfg->getCostMode() == COST_MIXED_LOSSLESS_LOSSY_CODING)
					rpcBestCU->getTotalCost() = rpcTempCU->getTotalCost() + (1.0 / m_pcRdCost->getLambda());
				else
					rpcBestCU->getTotalCost() = rpcTempCU->getTotalCost() + 1;
			}
			// 选择最优的划分模式  
			xCheckBestMode(rpcBestCU, rpcTempCU, uiDepth DEBUG_STRING_PASS_INTO(sDebug) DEBUG_STRING_PASS_INTO(sTempDebug) DEBUG_STRING_PASS_INTO(false)); // RD compare current larger prediction
			// 计算并更新最优代价——end                                                                   // with sub partitioned prediction.
		}
	}

	DEBUG_STRING_APPEND(sDebug_, sDebug);

	rpcBestCU->copyToPic(uiDepth);                                                     // Copy Best data to Picture for next partition prediction.

	xCopyYuv2Pic(rpcBestCU->getPic(), rpcBestCU->getAddr(), rpcBestCU->getZorderIdxInCU(), uiDepth, uiDepth, rpcBestCU, uiLPelX, uiTPelY);   // Copy Yuv data to picture Yuv
	if (bBoundary || (bSliceEnd && bInsidePicture))
	{
		return;
	}

	// Assert if Best prediction mode is NONE
	// Selected mode's RD-cost must be not MAX_DOUBLE.
	assert(rpcBestCU->getPartitionSize(0) != NUMBER_OF_PART_SIZES);
	assert(rpcBestCU->getPredictionMode(0) != NUMBER_OF_PREDICTION_MODES);
	assert(rpcBestCU->getTotalCost() != MAX_DOUBLE);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值