HM代码——XCompressCu()和CompressCu()

参考大佬资料:
http://blog.sina.com.cn/s/blog_bc1b2fb70101ep5o.html
https://blog.csdn.net/sinat_33718563/article/details/80259765
https://blog.csdn.net/sinat_33718563/article/details/80265012
http://blog.sina.com.cn/s/blog_9f945ced0101p3ut.html
XCompressCu()部分
该函数实现的功能:遍历每个预测模式,通过比较率失真代价值,得到最优的预测模式,保存到参数rpcBestCU中,然后通过调用自身进行子CU的编码。

一、参数rpcTempCU主要有两个作用:
1.存放每一个预测函数的最优参数,然后通过xCheckBestMode() 函数将最优参数传给rpcBestCU;注意的是,在进行下一个预测函数之前,rpcTempCU会被初始化,模式信息、代价值、失真和码率等参数将会被清零;
2.存放子CU的最优信息,当CU进行回溯裁剪时,通过比较rpcBestCU和 rpcTempCU两个参数的率失真代价值进行判断是否划分。

在这里插入图片描述

3、出现的各类变量参数
在这里插入图片描述
4、传递的形参在这里插入图片描述
Partsize的具体内容见下图
在这里插入图片描述
5、函数简介如下:

  1. xCheckRDCostMerge2Nx2N():merge和skip模式的预测函数,当残差为0时,为skip模式。这个函数与普通的帧间预测函数的区别是没有运动估计过程。
  2. xCheckRDCostInter():普通帧间模式2N×2N、N×N、N×2N、2N×N、2N×nD、2N×nU、nL×2N和nR×2N的预测函数,主要包括运动估计和运动补偿两个过程。作用:进行预测,得到每种帧间模式的率失真代价值、失真和码率,并通过比较得到最优的帧间模式。
  3. xCheckRDCostIntra():帧内预测函数,帧内模式包括平面模式、DC模式以及33种角度预测模式。
  4. copyPartFrom():将子CU的最优编码信息传递给当前深度的rpcTempCU中。
  5. xCheckBestMode():通过比较rpcTempCU和rpcBestCU中的编码信息,将最优的信息存放在rpcBestCU中。

二、函数的具体流程
在这里插入图片描述
1、这个函数是CU的模式决定:CB,PU,TU的划分及PU的帧间或是帧内的模式

1)给定CB,求PU过程

流程为:

帧间模式的遍历SIZE_2NX2N的merge mode、SIZE_2NX2N、SIZE_NXN、SIZE_NX2N、SIZE_2NXN、AMP模式

帧间模式的遍历:进行SIZE_2NX2N、最小的CB还要进行SIZE_NXN的分割。

PCM模式:直接传输像素值。

2)CB的划分

递归的树划分形式

基本的流程为:
1)深度为0,64x64的CB大小,求其最佳的PU分割,并判断是否进行划分;
2)深度为1,64x64划分为4个32x32的CB,求每个的最佳PU的分割。第一个分割CB要直到分割
到最深层或是不能分割返回了,才进行第二个CB的PU的决定,依次类推;
3)深度为2,四个16x16块同深度为1相同,前一个CB分割划分到最深层或是不能再继续分割返回了(CB划分完毕),才进行第二个CB的划分。

在这里插入图片描述
上图为CU的递归分割顺序
在这里插入图片描述
在这里插入图片描述
xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth);函数将未分割的CU和分割之后的4个CU进行比较来决定是否进行CU的划分。
代码流程图:
在这里插入图片描述
三、代码部分

// ====================================================================================================================
// Protected member functions
// ====================================================================================================================
/** Compress a CU block recursively with enabling sub-LCU-level delta QP
*\param   rpcBestCU 存放最优数据
*\param   rpcTempCU 存放局部最优数据
*\param   uiDepth  CU的深度
*\returns Void
*
*- for loop of QP value to compress the current CU with all possible QP 对于循环的QP值,用所有可能的QP压缩当前CU
*/
#if AMP_ENC_SPEEDUP //encoder only speed-up by AMP mode skipping编码器只有通过AMP模式skip加速
Void TEncCu::xCompressCU(TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU,
	UInt uiDepth DEBUG_STRING_FN_DECLARE(sDebug_), PartSize eParentPartSize)//个人感觉最后一个参数是父类的PU模式
#else
Void TEncCu::xCompressCU(TComDataCU*& rpcBestCU, TComDataCU*& rpcTempCU, UInt uiDepth)
#endif
{
	TComPic* pcPic = rpcBestCU->getPic();
	DEBUG_STRING_NEW(sDebug)

		// get Original YUV data from picture从图片中得到原始的YUV数据
		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  Cbf快速模式PU决策的变量
	Bool    doNotBlockPu = true;
	Bool    earlyDetectionSkipMode = false;

	Bool bBoundary = false;
	UInt uiLPelX = rpcBestCU->getCUPelX();//获取最佳CU的X、Y坐标点的位置
	UInt uiRPelX = uiLPelX + rpcBestCU->getWidth(0) - 1;
	UInt uiTPelY = rpcBestCU->getCUPelY();
	UInt uiBPelY = uiTPelY + rpcBestCU->getHeight(0) - 1;

	Int iBaseQP = xComputeQP(rpcBestCU, uiDepth);//计算当前CU的QP
	Int iMinQP;
	Int iMaxQP;
	Bool isAddLowestQP = false;

	const UInt numberValidComponents = rpcBestCU->getPic()->getNumberValidComponents();
	/*******************************************************分配最大QP和最小QP*********************************************************/
	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
	{
		iMinQP = rpcTempCU->getQP(0);
		iMaxQP = rpcTempCU->getQP(0);
	}

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

	// transquant-bypass (TQB) processing loop variable initialisation --TQB处理循环变量初始化??

	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.
	//对于TQB来说,使用一个在非TQB的QP测试中最小的一个QP,这是为了使Qps更小,TQB可以应用全部CU级别的测试
	if ((rpcTempCU->getSlice()->getPPS()->getTransquantBypassEnableFlag()))
	{
		isAddLowestQP = true; // mark that the first iteration is to cost TQB mode.标记第一个迭代是cost TQB模式
		iMinQP = iMinQP - 1;  // increase loop variable range by 1, to allow testing of TQB mode along with other QPs将循环变量范围增加1,允许测试TQB模式和其他QPs
		if (m_pcEncCfg->getCUTransquantBypassFlagForceValue())
		{
			iMaxQP = iMinQP;
		}
	}

	// If slice start or slice end is within this cu...如果slice的开始或者结束在CU中
	TComSlice * pcSlice = rpcTempCU->getPic()->getSlice(rpcTempCU->getPic()->getCurrSliceIdx());
	Bool bSliceStart = pcSlice->getSliceSegmentCurStartCUAddr()>rpcTempCU->getSCUAddr() && pcSlice->getSliceSegmentCurStartCUAddr()<rpcTempCU->getSCUAddr() + rpcTempCU->getTotalNumPart();
	Bool bSliceEnd = (pcSlice->getSliceSegmentCurEndCUAddr()>rpcTempCU->getSCUAddr() && pcSlice->getSliceSegmentCurEndCUAddr()<rpcTempCU->getSCUAddr() + rpcTempCU->getTotalNumPart());
	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)
	{
		for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)
		{
			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);
			}

			rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);//当前CU初始化initEstData(uiDepth, iQP, bIsLosslessMode) initialize prediction data with enabling sub-LCU-level delta QP
/*******************************************************帧间模式选择************************************************************************/
			// do inter modes, SKIP and 2Nx2N
			if (rpcBestCU->getSlice()->getSliceType() != I_SLICE)//如果不是I帧
			{
				// 2Nx2N
				if (m_pcEncCfg->getUseEarlySkipDetection())//如果getUseEarlySkipDetection()为真
				{
					xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));
					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);

				if (!m_pcEncCfg->getUseEarlySkipDetection())//如果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)//如果earlyDetectionSkipMode为假
		{
			for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)//遍历所有的QP
			{
				const Bool bIsLosslessMode = isAddLowestQP && (iQP == iMinQP); // If lossless, then iQP is irrelevant for subsequent modules.如果是无损的,则iQP与后续模块无关。

				if (bIsLosslessMode)
				{
					iQP = lowestQP;
				}

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

				// do inter modes, NxN, 2NxN, and Nx2N 对帧间模式的对称划分进行遍历
				if (rpcBestCU->getSlice()->getSliceType() != I_SLICE)//如果不是I帧
				{
					// 2Nx2N, NxN
					if (!((rpcBestCU->getWidth(0) == 8) && (rpcBestCU->getHeight(0) == 8)))//块的尺寸不能是8x8
					{
						if (uiDepth == g_uiMaxCUDepth - g_uiAddCUDepth && doNotBlockPu)//计算NxN划分并计算率失真
						{
							xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_NxN DEBUG_STRING_PASS_INTO(sDebug));
							rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						}
					}

					if (doNotBlockPu)
					{
						xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_Nx2N DEBUG_STRING_PASS_INTO(sDebug));//计算Nx2N划分并计算率失真
						rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_Nx2N)
						{
							doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
						}
					}
					if (doNotBlockPu)
					{
						xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxN DEBUG_STRING_PASS_INTO(sDebug));//计算2NxN划分并计算率失真
						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)对四种帧间非对称划分进行遍历
					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;

						deriveTestModeAMP(rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver, bTestMergeAMP_Hor, bTestMergeAMP_Ver);
#else
						deriveTestModeAMP(rpcBestCU, eParentPartSize, bTestAMP_Hor, bTestAMP_Ver);
#endif

						//! Do horizontal AMP
						if (bTestAMP_Hor)
						{
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug));//计算2NxnU划分并计算率失真
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug));//计算2NxnD划分并计算率失真
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
						}
#if AMP_MRG            //Merge模式下在进行遍历
						else if (bTestMergeAMP_Hor)
						{
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnU DEBUG_STRING_PASS_INTO(sDebug), true);//计算2NxnU划分并计算率失真
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnU)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_2NxnD DEBUG_STRING_PASS_INTO(sDebug), true);//计算2NxnD划分并计算率失真
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_2NxnD)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
						}
#endif

						//! Do horizontal AMP
						if (bTestAMP_Ver)
						{
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug));//计算nLx2N划分并进行率失真计算
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug));//计算nRx2N划分并进行率失真计算
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
							}
						}
#if AMP_MRG            //Merge模式下在进行遍历
						else if (bTestMergeAMP_Ver)
						{
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nLx2N DEBUG_STRING_PASS_INTO(sDebug), true);//计算nLx2N划分并进行率失真计算
								rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
								if (m_pcEncCfg->getUseCbfFastMode() && rpcBestCU->getPartitionSize(0) == SIZE_nLx2N)
								{
									doNotBlockPu = rpcBestCU->getQtRootCbf(0) != 0;
								}
							}
							if (doNotBlockPu)
							{
								xCheckRDCostInter(rpcBestCU, rpcTempCU, SIZE_nRx2N DEBUG_STRING_PASS_INTO(sDebug), true);//计算nRx2N划分并进行率失真计算
								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
				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避免比较复杂的帧内
				{
					xCheckRDCostIntra(rpcBestCU, rpcTempCU, intraCost, SIZE_2Nx2N DEBUG_STRING_PASS_INTO(sDebug));//计算2Nx2N划分并进行率失真计算(帧内)
					rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
					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));//计算NxN划分并进行率失真计算(帧内)
							intraCost = std::min(intraCost, tmpIntraCost);
							rpcTempCU->initEstData(uiDepth, iQP, bIsLosslessMode);
						}
					}
				}

				// test PCM是否为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早期CU决策
		if (m_pcEncCfg->getUseEarlyCU() && rpcBestCU->isSkipped(0))
		{
			bSubBranch = false;
		}
		else
		{
			bSubBranch = true;
		}
	}
	else if (!(bSliceEnd && bInsidePicture))
	{
		bBoundary = true;
	}

	// copy orginal YUV samples to PCM buffer将原始的YUV样本复制进PCM的缓存区
	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.
	}
//********************************************实现下层分割**************************************************//
	for (Int iQP = iMinQP; iQP <= iMaxQP; iQP++)//遍历所有的QP
	{
		const Bool bIsLosslessMode = false; // False at this level. Next level down may set it to true.无损模式在这个级别为False,下一层可能会设置为true。

		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)

				for (UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++)//体现四叉树子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.清除子分区数据或初始化

					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使用之前的深度缓冲区初始化RD
						{
							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))
							{
								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
						xCompressCU(pcSubBestPartCU, pcSubTempPartCU, uhNextDepth);
#endif

						rpcTempCU->copyPartFrom(pcSubBestPartCU, uiPartUnitIdx, uhNextDepth);         // Keep best part data to current temporary data.每预测完一个子CU后,都要执行一次rpcTempCU->copyPartFrom(),即把子CU的最佳预测数据复制到当前CU的Temp里面。

						xCopyYuv2Tmp(pcSubBestPartCU->getTotalNumPart()*uiPartUnitIdx, uhNextDepth);
					}
					else if (bInSlice)
					{
						pcSubBestPartCU->copyToPic(uhNextDepth);
						rpcTempCU->copyPartFrom(pcSubBestPartCU, uiPartUnitIdx, uhNextDepth);
					}
				}

			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();
			}
			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
				
			//这个就是决定划分深度的关键。这个函数里面的rpcBestCU代表的就是当前深度的预测数据,rpcTempCU代表的就是4个子CU,也就是下一深度的总预测数据。通过这个函数,就能实现两层深度的预测数据比较,并确定采用哪层深度。 // 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);
}

整体代码分析:https://blog.csdn.net/feixiang_john/article/details/8237106(通过三次遍历所有的QP系统分析)

CompressCu()部分
1.xCompressCU 函数的调用

在编码一个片的函数CompressSlice 函数中有这个几行代码

 // run CU encoder

 m_pcCuEncoder->compressCU( pcCU ); 就是一个LCU的编码,包括CU的划分,PU模式的决定,TU的划分

 m_pcCuEncoder->encodeCU( pcCU ); 这里可以看出来pcCU是存储着需要编码的信息。

2.进入这个函数

Void TEncCu::compressCU( TComDataCU*&rpcCU )

{

//initialize CU data

m_ppcBestCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );

//这里的 m_ppcBestCU[0]和m_ppcTempCU[0]都是记录模式信息的,至于如何记录我们一点点的来看。由于是递归的划分CU,这里容易绕晕啊。

m_ppcTempCU[0]->initCU( rpcCU->getPic(), rpcCU->getAddr() );

//analysis of CU

xCompressCU( m_ppcBestCU[0], m_ppcTempCU[0], 0 ); 这里进入xCompressCU函数,这里的第三个参数是CU的分割深度。64x64的深度为0,以此类推,最小的CU为8x8深度为3。至于如何将最优的模式信息赋值到pcCU里,我们后面跟踪代码可以看到。

这里首先要弄清一个概念,就是CU的划分是递归的

第一步 CU的大小为64x64, 搜索最优的PU的划分得到最优的预测模式,进行TU的划分

第二步 CU的大小为32x32, 第一个CU(按之子扫描顺序) 同上

第三步 CU的大小为16x16, 第一个CU 同上

第四步 CU的大小为8x8, 以此进行第一个CU,第二个CU,第三个CU和第四个CU的PU和TU的划分和最优模式的选择。这里面完成每个CU后将这个的RD与前面进行累加。

第五步 返回到CU为 16x16的CU,将其RD-COST 与第四部记录的四个8X8的CU的RD-cost进行比较。决定了这个16X16的最优的CU划分及最优的CU下的PU和CU的划分。

第六步 CU的大小为 16X16,第二个CU。重复第四步第五步,可以得到第二个最优的16x16的CU的划分和PU TU 的模式。同时将改第二个CU的最优的RD-COST与5步得到的第一个16x16的CURD-COST进行累加。

第七步:同理完成第三个和第四个的16X16的CU的最优的划分和模式的选择,将其RD-COST累加。这样我们就得到了分割为16X16最佳的RD-cost。

第八步: 返回到第二步,比较第一个32X32CU的RD-cost 和 分割为4个16X16的CU的RD-cost,得了第一个32X32CU的分割信息和最优的模式。

第九步:同理完成第二个32x32,第三个32X32和第四个32X32的最优的划分和模式选择。通过记录和累加每一个32x32的RD-cost,与64x64的CU的RD-Cost进行比较。我们得到了最终的CU 的划分和每个CU的最优的PU的划分及PU的预测模式以及TU的划分。

}

3.xCompressCU( m_ppcBestCU[0],m_ppcTempCU[0], 0 )函数

现在就按照2中的过程来看一看这函数的流程:

首先进入该函数,CU的大小已经确定了为64x64,进行PU的划分和TU的划分

PU的划分将按下列顺序进行尝试:

帧间

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_2Nx2N, bFMD ) skip 2NX2N

xCheckRDCostMerge2Nx2N( rpcBestCU,rpcTempCU, &earlyDetectionSkipMode ); Merge 2NX2N

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_2Nx2N, bFMD ); 2NX2N

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_NxN, bFMD ); NXN 划分为4个PU

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_Nx2N, bFMD ); Nx2N 划分为2个PU

xCheckRDCostInter ( rpcBestCU, rpcTempCU, SIZE_2NxN,bFMD );

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_2NxnU, bFMD );

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_2NxnD, bFMD ); 4种非对称的划分

xCheckRDCostInter( rpcBestCU, rpcTempCU,SIZE_nLx2N, bFMD );

这里在调用这个帧内RDcost函数时,rpcBestCU中始终存放的是当前CU下最优的PU的模式和划分信息的CU结构体。

帧内:

xCheckRDCostIntra( rpcBestCU, rpcTempCU,SIZE_2Nx2N ); 2NX2N的划分。 PU为CU的大小。

xCheckRDCostIntra( rpcBestCU, rpcTempCU,SIZE_NxN ); 当CU为最小的CU的时候,将尝试 分割为4个PU。

在RD-cost的函数的最后有这样一个函数

xCheckBestMode(rpcBestCU, rpcTempCU,uiDepth);

接下来就该递归的分割LCU,下面看看分割的判断

if(bSubBranch && bTrySplitDQP && uiDepth < g_uiMaxCUDepth -g_uiAddCUDepth ) 条件为真进行分割

{
  UChar      uhNextDepth         = uiDepth+1;
  TComDataCU* pcSubBestPartCU     =m_ppcBestCU[uhNextDepth];

 TComDataCU* pcSubTempPartCU     =m_ppcTempCU[uhNextDepth];



 for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 4; uiPartUnitIdx++ )

 {

 pcSubBestPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth,iQP );         

 pcSubTempPartCU->initSubCU( rpcTempCU, uiPartUnitIdx, uhNextDepth,iQP ); 

 注意这里是用rpcTempCU对pcSubBestPartCU和pcSubTemPartC进行初始化,函数到此 rpcBestCU里面还是当前CU64x64大小最优的PU信息。

接下来是递归的调用自己

  if ( rpcBestCU->isIntra(0) )


       {
          xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth, SIZE_NONE );

 进入这个函数后,CU的大小为32X32 iPartUnitIdx 这里pcSubBestPartCU就和rpcBestCU一样始终放着当前最优的预测的信息。

     }

     else

     {

       xCompressCU( pcSubBestPartCU, pcSubTempPartCU, uhNextDepth,rpcBestCU->getPartitionSize(0) );

     }

显然这里要做的是将4个最优的32X32CU的RD-cost累加。注意这里有一个语句是

rpcTempCU->copyPartFrom( pcSubBestPartCU,uiPartUnitIdx, uhNextDepth );

跟进去一看这个函数的几个代码就知道这个语句就是完成4个划分的最优的信息的累加,以便和为分割前的CU的最优的预测模式的RD-cost进行比较也就是m_ppcBestCU进行比较。

} 分割的循环

完成了四个分割,即2中的过程2中的第九步。这里就 rpcTempCU存储的是CU的4个划分的信息。

可以看见 xCheckBestMode( rpcBestCU, rpcTempCU, uiDepth);函数将未分割的CU和分割之后的4个CU进行比较来决定是否进行CU的划分。

}

但是奇怪的是如何和最开始CompressCU的参数pcCU的参数联系上。这里有个函数。在最后面

rpcBestCU->copyToPic(uiDepth);

这个函数就是将得到的最优的PU的模式和预测信息,及CU的划分的信息赋值到pcCU中。

进去看这个函数, TComDataCU*& rpcCU = m_pcPic->getCU(m_uiCUAddr );第一个语句中的rpcCU正好是CompressCU的参数。
代码分步分析:
https://blog.csdn.net/FRD2009041510/article/details/40888141
代码部分:

/ ====================================================================================================================
// Public member functions
// ====================================================================================================================

/** \param  rpcCU pointer of CU data classCU数据类的参数指针
*/
Void TEncCu::compressCU(TComDataCU*& rpcCU)//指向CU的参数
{
	// initialize CU data初始化CU的数据
	m_ppcBestCU[0]->initCU(rpcCU->getPic(), rpcCU->getAddr());
	m_ppcTempCU[0]->initCU(rpcCU->getPic(), rpcCU->getAddr());
	

	// analysis of CU分析CU
	DEBUG_STRING_NEW(sDebug)

		xCompressCU(m_ppcBestCU[0], m_ppcTempCU[0], 0 DEBUG_STRING_PASS_INTO(sDebug));//这里进入xCompressCU函数,这里的第三个参数是CU的分割深度。64x64的深度为0,以此类推,最小的CU为8x8深度为3
	DEBUG_STRING_OUTPUT(std::cout, sDebug)

#if ADAPTIVE_QP_SELECTION
		if (m_pcEncCfg->getUseAdaptQpSelect())
		{
			if (rpcCU->getSlice()->getSliceType() != I_SLICE) //IIII
			{
				xLcuCollectARLStats(rpcCU);
			}
		}
#endif
}
  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值