HEVC学习-帧内预测-亮度分量预测主函数

QP的详解:https://blog.csdn.net/liangjiubujiu/article/details/80569391
代码部分:https://blog.csdn.net/HEVC_CJL/article/details/8200793
PU的帧内预测部分(MPM):https://www.cnblogs.com/DwyaneTalk/p/5711342.html
率失真优化方法部分:新一代高效视频编码H.265/HEVC:原理、标准与实现——万帅 杨付正 编著——电子工业出版社

一、QP的详解
Quantizer Parameter(量化参数),反映了空间细节压缩情况。值越小,量化越精细,图像质量越高,产生的码流也越长。如QP小,大部分的细节都会被保留;QP增大,一些细节丢失,码率降低,但图像失真加强和质量下降。
量化参数QP是量化步长Qstep的序号。对于亮度(Luma)编码而言,量化步长Qstep共有52个值,QP取值0—51,对于色度(Chroma)编码,Q的取值0—39。
QP取最小值0 时,表示量化最精细;相反,QP取最大值51时,表示量化是最粗糙的。QP和Qstep具有线性相关性,Qstep随着QP的增加而增加,每当QP值增加6,Qstep便增加一倍。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(QP应该有52个值,我也不太懂这里为啥原博只有50个,不过可以根据关系推算)
二、MPM确定策略:
对于luma CU:有35个可选的帧内预测方向(Plannar(0)、DC(1)和方向预测(2~34)),对于mininum size的luma CB,可以平均划分成4个方形的subblocks,对于每个subblock进行独立的帧内预测,有独立的intra prediction mode。也就是说对于帧内预测的CU,可以进行2Nx2N和NxN两种PU划分模式,且NxN模式只有对mininum size CB可以使用。
一个帧内luma PU块,预测模式确定之后,需要对预测模式进行编码。HEVC中在进行帧内预测模式编码时,先为每个intra PU确定3个最可能模式(MPM),假设为S={M1,M2,M3}。然后通过判断luma PU的帧内预测模式是否在S中,如果在S中,则需要2bit编码预测模式在S中的索引,否则需要5bit编码预测模式在另外32种模式中的索引。
对于luma PU,确定最可能3个预测模式是根据当前PU左边和上边的预测模式,假设左边和上边的预测模式分别是A和B,如果左边或上边PU不是帧内预测模式或是PCM模式,则A或B为DC;另外,如果上边PU块不在当前CTU内,那么B也为DC。确定好A和B之后:
当A=B时,如果A,B都大于2,即A和B都不是Planar或DC,那么:
M1=A;
M2=2+((A-2-1+32)%32)
M3=2+((A-2+1)%32)
当A=B时,如果A,B至少有一个小于2,即A或B是Planar或DC,那么:
M1=Planar,M2=DC,M3=26(竖直方向预测)
当A!=B时,M1=A,M2=B,对于M3按照下面规则决定:
如果A和B都不是Planar,那么M3=Planar;
如果A和B都不是DC,那么M3=DC;
否则,说明{A,B}={Planar,DC},那么M3=26。
当candModeList(即为上述的S)建立好了之后,可以利用该列表对当前的PU模式信息进行编码,具体如下:
(1)若当前PU的最优模式(记为Mode C,下同)在candModeList中出现,则只需要编码ModeC在candModeList中出现的位置即可;
(2)若ModeC未在candModeList中出现,则按照以下步骤编码ModeC
1)将candModeList中的候选模式按照从小到大重新排列;
2)遍历重新排列后的三个候选模式,分别于ModeC进行比较,若
ModeC>=candModeList[i]
ModeC自减一,遍历结束后对ModeC的最终值进行编码。

三、RD(Rate Distortion)率失真的优化方法
CTU是H.265/HEVC的基本编码单元,每一个CTU可以被划分为不同的编码单元CU,每个CU可以使用不同的PU模式和TU模式,每个PU又可以选择不同的预测模式。因此,可以把CTU编码参数的优化过程分成:CTU层主要选择不同的CU划分模式,CU层主要选择不同的PU模式和TU模式,PU层主要选择不同的预测模式。由于主要编码参数属于CTU及其以下层,只关注CTU及其以下层编码单元编码参数的优化过程。
1、CTU优化方法
H.265/HEVC参考模型HM采用拉格朗日优化方法为每个CTU确定除量化参数外的编码参数,主要包括CU划分模式、CU的PU模式和TU模式、PU的预测参数等。一个CTU包含大量的编码参数组合,采用分级的方式对不同层的编码参数进行确定,主要步骤如下:
(1)遍历所有的CU划分模式进行编码,按照(11-11)确定最优CU划分模式。
在这里插入图片描述
(2)对其中的每个CU,遍历所有的PU模式和TU模式组合,按照(11-12)确定最优的PU和TU模式。
在这里插入图片描述
(3)对其中的每个PU,遍历所有的预测模式,按照(11-13)或(11-14)确定最优的预测模式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.快速模式判决策略
在这里插入图片描述
在这里插入图片描述
3.CTU编码参数优化流程
(实在是太多了,在HEVC那本书的第337页)
四、代码部分

Void
TEncSearch::estIntraPredQT(TComDataCU* pcCU,//指向当前CU块
	TComYuv*    pcOrgYuv,//指向原始YUV样本序列
	TComYuv*    pcPredYuv,//指向预测YUV样本序列
	TComYuv*    pcResiYuv,//指向剩余YUV样本序列
	TComYuv*    pcRecoYuv,//指向重建YUV样本序列
	UInt&       ruiDistC,
	Bool        bLumaOnly)
{
	UInt    uiDepth = pcCU->getDepth(0); //!< 当前CU的深度
	UInt    uiNumPU = pcCU->getNumPartInter(); //!< 当前CU的分割模式,(SIZE_2Nx2N:1, SIZE_2NxN:2, SIZE_Nx2N:2, SIZE_NxN:4 ... )
	UInt    uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1; //!< 用于计算变换的深度,实际深度为该值+uiDepth
	UInt    uiWidth = pcCU->getWidth(0) >> uiInitTrDepth; //!< 当前PU的宽度,如果又分成4个子块,则宽度除以2
	UInt    uiHeight = pcCU->getHeight(0) >> uiInitTrDepth; //!< 当前PU的高度,如果又分成4个子块,则高度除以2
	UInt    uiQNumParts = pcCU->getTotalNumPart() >> 2; // 最小的分区是4x4大小的块,这里计算出以该4x4块为单位的分割数,这么做便于计算当前CU的Zorder坐标
	UInt    uiWidthBit = pcCU->getIntraSizeIdx(0);
	UInt    uiOverallDistY = 0;
	UInt    uiOverallDistC = 0;
	UInt    CandNum;//记录一记录候选模式数量,
	Double  CandCostList[FAST_UDI_MAX_RDMODE_NUM];//CandCostList计算每种候选模式代价的变量;FAST_UDI_MAX_RDMODE_NUM=在快速udi估计循环中进行RD比较的最大次数        

	//===== set QP and clear Cbf =====设置QP和清空Cbf(cbf是code-block-flag标志)
	if (pcCU->getSlice()->getPPS()->getUseDQP() == true)
	{
		pcCU->setQPSubParts(pcCU->getQP(0), 0, uiDepth);
	}
	else
	{
		pcCU->setQPSubParts(pcCU->getSlice()->getSliceQp(), 0, uiDepth);
	}

	//===== loop over partitions =====遍历分区
	UInt uiPartOffset = 0; //!< 用于记录当前PU的Zorder坐标
	for (UInt uiPU = 0; uiPU < uiNumPU; uiPU++, uiPartOffset += uiQNumParts) //!< 对当前CU中的每个PU进行遍历
	{
		//===== init pattern for luma prediction =====用于luma预测的init模式
		Bool bAboveAvail = false;
		Bool bLeftAvail = false;
		pcCU->getPattern()->initPattern(pcCU, uiInitTrDepth, uiPartOffset); // set parameters from CU data for accessing neighbouring pixels从CU数据中设置参数来访问邻近的像素
																			// set luma parameters from CU data for accessing ADI data //!< 主要获取当前PU的邻域可用性,对参考样点进行设置及滤波
		pcCU->getPattern()->initAdiPattern(pcCU, uiPartOffset, uiInitTrDepth, m_piYuvExt, m_iYuvExtStride, m_iYuvExtHeight, bAboveAvail, bLeftAvail);

		//===== determine set of modes to be tested (using prediction signal only) =====确定要测试的模式集(仅使用预测信号)
		Int numModesAvailable = 35; //total number of Intra modes帧内预测模式的总数
		Pel* piOrg = pcOrgYuv->getLumaAddr(uiPU, uiWidth);
		Pel* piPred = pcPredYuv->getLumaAddr(uiPU, uiWidth);
		UInt uiStride = pcPredYuv->getStride();
		UInt uiRdModeList[FAST_UDI_MAX_RDMODE_NUM];//FAST_UDI_MAX_RDMODE_NUM=在快速udi估计循环中进行RD比较的最大次数
		Int numModesForFullRD = g_aucIntraModeNumFast[uiWidthBit]; //!< MPM数目
																   //!< g_aucIntraModeNumFast[] = {3, 8, 8, 3, 3, 3, 3}; 2x2, 4x4, 8x8, 16x16, 32x32, 64x64, 128x128
		Bool doFastSearch = (numModesForFullRD != numModesAvailable); //!< 此处doFastSearch恒为真,MPM为3,帧内模式为35
		if (doFastSearch)
		{
			assert(numModesForFullRD < numModesAvailable);

			for (Int i = 0; i < numModesForFullRD; i++)
			{
				CandCostList[i] = MAX_DOUBLE;// ///< max. value of Double-type value

			}
			CandNum = 0;

			for (Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++) //!< 遍历35种帧内预测模式
			{
				UInt uiMode = modeIdx;
				//! 调用亮度帧内预测函数
				predIntraLumaAng(pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, pcCU, bAboveAvail, bLeftAvail);

				// use hadamard transform here在这里使用hadamard变换
				UInt uiSad = m_pcRdCost->calcHAD(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 // UDI---Unified Directional Intra统一定向帧内
			Int uiPreds[3] = { -1, -1, -1 };
			Int iMode = -1;  //!< 如果三个MPMs的前两个相同,则iMode=1,否则iMode=2
			Int numCand = pcCU->getIntraDirLumaPredictor(uiPartOffset, uiPreds, &iMode); //!< 获取亮度帧内预测模式的三个MPM:most probable mode
			if (iMode >= 0) //!< iMode = 1 or 2,因此,numCand会被重新赋值为iMode
			{
				numCand = iMode;
			}

			for (Int j = 0; j < numCand; j++)

			{
				Bool mostProbableModeIncluded = false;
				Int mostProbableMode = uiPreds[j]; //!< 取出MPM

				for (Int i = 0; i < numModesForFullRD; i++)
				{
					mostProbableModeIncluded |= (mostProbableMode == uiRdModeList[i]); //!< 检查MPMs是否被uiRdModeList所包含
				}
				if (!mostProbableModeIncluded)  //!< 如果没被包含,则将该MPM包含到uiRdModeList里
				{
					uiRdModeList[numModesForFullRD++] = mostProbableMode;
				}
			}
#endif // FAST_UDI_USE_MPM
		} //!< if (doFastSearch)
		else
		{
			for (Int i = 0; i < numModesForFullRD; i++)
			{
				uiRdModeList[i] = i;
			}
		}

		//===== check modes (using r-d costs) =====RD(Rate Distortion)率失真、RD costs率失真代价
		//! 帧内预测模式最佳值的确定主要有以下几个步骤:1. 对numModesForFullRD种预测模式进行遍历,即对每种模式计算出
		//! 对应的RD costs,但该步骤中,并不会把一个CU的所有分割都算一遍,而仅仅对于至多深度为1的分割进行遍历,这么做
		//! 大大减少了运算量,提高速度;2. 在第1个步骤中,会粗略得到最佳预测模式(在HM9.0中会得到包括次优解在内的两个
		//! 预测模式),存储下来,以供第3步使用;3. 在第2步的基础上,对最佳(及次优)预测模式的所有分割模式遍历一遍,
		//! 得到最终的最佳结果
#if HHI_RQT_INTRA_SPEEDUP_MOD
		UInt   uiSecondBestMode = MAX_UINT;
		Double dSecondBestPUCost = MAX_DOUBLE;
#endif

		UInt    uiBestPUMode = 0; //!< 存放最佳预测模式
		UInt    uiBestPUDistY = 0; //!< 存放最佳预测模式对应的亮度失真值
		UInt    uiBestPUDistC = 0; //!< 存放最佳预测模式对应的色度失真值
		Double  dBestPUCost = MAX_DOUBLE;  //!< 存放最佳预测模式对应的总代价
		for (UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++) //!< 遍历存储在uiRdModeList里的模式
		{
			// set luma prediction mode设置luma预测模式
			UInt uiOrgMode = uiRdModeList[uiMode];

			pcCU->setLumaIntraDirSubParts(uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth);

			// set context models设置上下文模型
			if (m_bUseSBACRD)
			{
				m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
			}

			// determine residual for partition确定分区的剩余值
			UInt   uiPUDistY = 0;  //!< 存放当前预测模式对应的亮度失真值
			UInt   uiPUDistC = 0;  //!< 存放当前预测模式对应的色度失真值
			Double dPUCost = 0.0; //!< 存放当前预测模式对应的代价
#if HHI_RQT_INTRA_SPEEDUP //! 注意这个函数倒数第二个参数,此时为true,仅仅对当前深度的失真值进行计算
			xRecurIntraCodingQT(pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, true, dPUCost);//重点就在倒数第二个参数上,它控制着函数中bCheckSplit的值,如果该值为ture,则TU只进行当前深度的计算,如果该值为false,则TU进行所有深度的计算。
#else
			xRecurIntraCodingQT(pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, dPUCost);
#endif

			// check r-d cost检查RD代价
			if (dPUCost < dBestPUCost) //!< 更新最佳预测模式相关参数,如果当前PU的预测模式的cost要比最佳模式的cost小,则做如下工作:
			{
#if HHI_RQT_INTRA_SPEEDUP_MOD
				uiSecondBestMode = uiBestPUMode;//首先把最佳模式和最佳模式代价付给第二佳模式和第二佳模式代价
				dSecondBestPUCost = dBestPUCost;
#endif
				uiBestPUMode = uiOrgMode;
				uiBestPUDistY = uiPUDistY;
				uiBestPUDistC = uiPUDistC;
				dBestPUCost = dPUCost;//将当前的模式的各个参数赋给最佳模式

				xSetIntraResultQT(pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcRecoYuv);

				UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ((pcCU->getDepth(0) + uiInitTrDepth) << 1);
				::memcpy(m_puhQTTempTrIdx, pcCU->getTransformIdx() + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[0], pcCU->getCbf(TEXT_LUMA) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[1], pcCU->getCbf(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[2], pcCU->getCbf(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[0], pcCU->getTransformSkip(TEXT_LUMA) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[1], pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[2], pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof(UChar));
			}
#if HHI_RQT_INTRA_SPEEDUP_MOD
			else if (dPUCost < dSecondBestPUCost)//如果当前PU的预测模式的cost要比第二佳模式的cost要小,则把当前模式的各个参数赋给第二佳模式
			{
				uiSecondBestMode = uiOrgMode;
				dSecondBestPUCost = dPUCost;
			}
#endif
		} // Mode loop

#if HHI_RQT_INTRA_SPEEDUP        //   1     tests one best mode with full rqt   测试一个最佳模式的所有rqt(我也不知道这个rqt是什么鬼。。。哭)
#if HHI_RQT_INTRA_SPEEDUP_MOD//   0     tests two best mode with full rqt      测试两个最佳模式的所有rqt
		for (UInt ui = 0; ui < 2; ++ui)
#endif
		{
#if HHI_RQT_INTRA_SPEEDUP_MOD
			UInt uiOrgMode = ui ? uiSecondBestMode : uiBestPUMode;
			if (uiOrgMode == MAX_UINT)
			{
				break;//如果是两种最佳模式,取最佳模式赋给uiOrgMode
			}
#else
			UInt uiOrgMode = uiBestPUMode; //!< 如果是一种最佳模式,设置uiOrgMode模式为最佳预测模式
#endif

			pcCU->setLumaIntraDirSubParts(uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth);

			// set context models 设置上下文模型
			if (m_bUseSBACRD)
			{
				m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
			}

			// determine residual for partition 确定用来划分的残差
			UInt   uiPUDistY = 0;  //!< 存放当前预测模式对应的亮度失真值
			UInt   uiPUDistC = 0;  //!< 存放当前预测模式对应的色度失真值
			Double dPUCost = 0.0; //!< 存放当前预测模式对应的代价
			//! 注意这个函数倒数第二个参数,此时为false,对所有深度的失真值进行计算
			xRecurIntraCodingQT(pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, false, dPUCost);

			// check r-d cost检查r-d cost
			if (dPUCost < dBestPUCost)//如果模式的PU的代价小于最佳模式的PU代价,则用当前的模式代替最佳模式
			{
				uiBestPUMode = uiOrgMode;
				uiBestPUDistY = uiPUDistY;
				uiBestPUDistC = uiPUDistC;
				dBestPUCost = dPUCost;

				xSetIntraResultQT(pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcRecoYuv);

				UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ((pcCU->getDepth(0) + uiInitTrDepth) << 1);
				::memcpy(m_puhQTTempTrIdx, pcCU->getTransformIdx() + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[0], pcCU->getCbf(TEXT_LUMA) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[1], pcCU->getCbf(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempCbf[2], pcCU->getCbf(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[0], pcCU->getTransformSkip(TEXT_LUMA) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[1], pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, uiQPartNum * sizeof(UChar));
				::memcpy(m_puhQTTempTransformSkipFlag[2], pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, uiQPartNum * sizeof(UChar));
			}
		} // Mode loop 遍历了所有的模式
#endif

		  //--- update overall distortion ---更新所有的失真
		uiOverallDistY += uiBestPUDistY;
		uiOverallDistC += uiBestPUDistC;

		//--- update transform index and cbf ---更新传输索引和cbf
		UInt uiQPartNum = pcCU->getPic()->getNumPartInCU() >> ((pcCU->getDepth(0) + uiInitTrDepth) << 1);
		::memcpy(pcCU->getTransformIdx() + uiPartOffset, m_puhQTTempTrIdx, uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getCbf(TEXT_LUMA) + uiPartOffset, m_puhQTTempCbf[0], uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getCbf(TEXT_CHROMA_U) + uiPartOffset, m_puhQTTempCbf[1], uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getCbf(TEXT_CHROMA_V) + uiPartOffset, m_puhQTTempCbf[2], uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getTransformSkip(TEXT_LUMA) + uiPartOffset, m_puhQTTempTransformSkipFlag[0], uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getTransformSkip(TEXT_CHROMA_U) + uiPartOffset, m_puhQTTempTransformSkipFlag[1], uiQPartNum * sizeof(UChar));
		::memcpy(pcCU->getTransformSkip(TEXT_CHROMA_V) + uiPartOffset, m_puhQTTempTransformSkipFlag[2], uiQPartNum * sizeof(UChar));
		//--- set reconstruction for next intra prediction blocks ---设置下个帧内预测块的重建
		if (uiPU != uiNumPU - 1)
		{
			Bool bSkipChroma = false;
			Bool bChromaSame = false;
			UInt uiLog2TrSize = g_aucConvertToBit[pcCU->getSlice()->getSPS()->getMaxCUWidth() >> (pcCU->getDepth(0) + uiInitTrDepth)] + 2;
			if (!bLumaOnly && uiLog2TrSize == 2)
			{
				assert(uiInitTrDepth  > 0);
				bSkipChroma = (uiPU != 0);
				bChromaSame = true;
			}

			UInt    uiCompWidth = pcCU->getWidth(0) >> uiInitTrDepth;
			UInt    uiCompHeight = pcCU->getHeight(0) >> uiInitTrDepth;
			UInt    uiZOrder = pcCU->getZorderIdxInCU() + uiPartOffset;
			Pel*    piDes = pcCU->getPic()->getPicYuvRec()->getLumaAddr(pcCU->getAddr(), uiZOrder);
			UInt    uiDesStride = pcCU->getPic()->getPicYuvRec()->getStride();
			Pel*    piSrc = pcRecoYuv->getLumaAddr(uiPartOffset);
			UInt    uiSrcStride = pcRecoYuv->getStride();
			for (UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride)
			{
				for (UInt uiX = 0; uiX < uiCompWidth; uiX++)
				{
					piDes[uiX] = piSrc[uiX];
				}
			}
			if (!bLumaOnly && !bSkipChroma)
			{
				if (!bChromaSame)
				{
					uiCompWidth >>= 1;
					uiCompHeight >>= 1;
				}
				piDes = pcCU->getPic()->getPicYuvRec()->getCbAddr(pcCU->getAddr(), uiZOrder);
				uiDesStride = pcCU->getPic()->getPicYuvRec()->getCStride();
				piSrc = pcRecoYuv->getCbAddr(uiPartOffset);
				uiSrcStride = pcRecoYuv->getCStride();
				for (UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride)
				{
					for (UInt uiX = 0; uiX < uiCompWidth; uiX++)
					{
						piDes[uiX] = piSrc[uiX];
					}
				}
				piDes = pcCU->getPic()->getPicYuvRec()->getCrAddr(pcCU->getAddr(), uiZOrder);
				piSrc = pcRecoYuv->getCrAddr(uiPartOffset);
				for (UInt uiY = 0; uiY < uiCompHeight; uiY++, piSrc += uiSrcStride, piDes += uiDesStride)
				{
					for (UInt uiX = 0; uiX < uiCompWidth; uiX++)
					{
						piDes[uiX] = piSrc[uiX];
					}
				}
			}
		}

		//=== update PU data ====更新PU数据
		pcCU->setLumaIntraDirSubParts(uiBestPUMode, uiPartOffset, uiDepth + uiInitTrDepth);
		pcCU->copyToPic(uiDepth, uiPU, uiInitTrDepth);
	} // PU loop


	if (uiNumPU > 1)
	{ // set Cbf for all blocks对于所有块设置cbf
		UInt uiCombCbfY = 0;
		UInt uiCombCbfU = 0;
		UInt uiCombCbfV = 0;
		UInt uiPartIdx = 0;
		for (UInt uiPart = 0; uiPart < 4; uiPart++, uiPartIdx += uiQNumParts)
		{
			uiCombCbfY |= pcCU->getCbf(uiPartIdx, TEXT_LUMA, 1);
			uiCombCbfU |= pcCU->getCbf(uiPartIdx, TEXT_CHROMA_U, 1);
			uiCombCbfV |= pcCU->getCbf(uiPartIdx, TEXT_CHROMA_V, 1);
		}
		for (UInt uiOffs = 0; uiOffs < 4 * uiQNumParts; uiOffs++)
		{
			pcCU->getCbf(TEXT_LUMA)[uiOffs] |= uiCombCbfY;
			pcCU->getCbf(TEXT_CHROMA_U)[uiOffs] |= uiCombCbfU;
			pcCU->getCbf(TEXT_CHROMA_V)[uiOffs] |= uiCombCbfV;
		}
	}

	//===== reset context models =====重新设置上下文模式
	if (m_bUseSBACRD)
	{
		m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
	}

	//===== set distortion (rate and r-d costs are determined later) =====速率和r-d失真确定之后的失真设置
	ruiDistC = uiOverallDistC;
	pcCU->getTotalDistortion() = uiOverallDistY + uiOverallDistC;
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值