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