作者:66
(转载请注明出处)
参考链接:http://blog.csdn.net/hevc_cjl/article/details/8200793
亮度分量的帧内预测涉及到的模块比较多,CU->PU的划分,参考像素的填充,哈达玛变换计算STAD(代替率失真),参考预测模式的选择,最佳模式的选择等。
先了解相关的知识:1.CU->PU的划分,在帧内预测中,2Nx2N的CU有两种PU划分方式:2Nx2N,NxN。帧间预测模式有八种:分别为:2Nx2N,NxN,2NxN,Nx2N,nRx2N(左右3:1),nLx2N(左右1:3),2NxnU(上下1:3),2NxnD(上下3:1)。
2. 参考像素的填充:前面说过,HEVC较后h.264增加了左下边界,配合增加的角度模式。
3. 哈达玛变换,WHT(Walsh-Hadamard Transform),广义傅里叶变换的一种,变换矩阵Hm是个大小为2的次幂的矩阵,如下:
图一、hadamard矩阵
这种矩阵的特性主要有,元素+1、-1,计算方便;正交对称;奇行(列)偶对称,偶行(列)奇对称;变换前后能量守恒;因残差信号经WHT后的求绝对值和与DCT之后系数绝对值之和接近,可以作为视频编码中的快速模式选择。
4. 参考预测模式的选择:
图二、参考PU与当前PU的位置关系
HEVC中共35种亮度预测模式,包括planar(0)、DC(1)和33种角度模式(2~34)。
如上图,帧内预测参考其他PU,并建立候选列表candModeList[3],三个备选项。
选择的规则如下:
①modeA预测模式与modeB相同
1. 同为planar或DC
candModeList[3] = {planar, DC, 角度26(垂直)}
2. 同为角度模式
candModeList[3] = {modeA, modeA-1, modeA+1}
注:mode2相邻为3和32,mode34相邻为33和3。
②modeA与modeB不同
candModeList = {modeA, modeB, choice}
关于choice的选择:
1. modeA与modeB都不为planar
choice = planar
2. modeA与modeB都不为DC
choice = DC
3. 当前两个都不符合时,
choice = 26
在candModelist建立后,对当前PU信息进行编码,经过率失真遍历计算后,
①最优预测模式modeC在列表中,仅编码模式在列表中的位置
②若不在,依次进行如下步骤:
1. candModeList中的候选模式从小到大重排
2. 比较最优预测模式modeC与列表中每个的标号大小,若modeC >= candModeList[i],则令modeC = modeC - 1,遍历结束后,对modeC最终值进行编码。
整体上看,思路为:
当前CU划分为PU ->遍历每个PU ->对每个PU计算35个模式中的最优模式,与预测对比,最后确定最佳预测模式。
详细情况见代码,目前对代码还存有疑问,之后搞清楚了来修改:
//亮度分量帧内预测
Void
TEncSearch::estIntraPredQT( TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
UInt& ruiDistC,
Bool bLumaOnly )
{
UInt uiDepth = pcCU->getDepth(0);//当前CU的深度
//uiNumPU,划分后pu的个数,PU的划分模式,帧内有2种:2NX2N,NxN;帧间有8种:4种对称模式:2Nx2N,2NxN,Nx2N,NxN,四种非对称模式,2NxnU(上下1:3),2NxnD(上下3:1),nLx2N(左右1:3),nRx2N(左右3:1)。帧间还有一种skip模式,即不需要编码残差信息时。
UInt uiNumPU = pcCU->getNumPartitions();//当前cu划分为pu的数目
UInt uiInitTrDepth = pcCU->getPartitionSize(0) == SIZE_2Nx2N ? 0 : 1;//计算变换深度,实际为uiDepth
UInt uiWidth = pcCU->getWidth (0) >> uiInitTrDepth;//当前cu的宽度
UInt uiHeight = pcCU->getHeight(0) >> uiInitTrDepth;//当前cu的长度
UInt uiQNumParts = pcCU->getTotalNumPart() >> 2;//当前cu包含的最小分区4x4的数目。
UInt uiWidthBit = pcCU->getIntraSizeIdx(0);
UInt uiOverallDistY = 0;
UInt uiOverallDistC = 0;
UInt CandNum;
Double CandCostList[ FAST_UDI_MAX_RDMODE_NUM ];
//===== set QP and clear Cbf =====
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 )
{
//===== init pattern for luma prediction =====
Bool bAboveAvail = false;//
Bool bLeftAvail = false;
pcCU->getPattern()->initPattern ( pcCU, uiInitTrDepth, uiPartOffset );
//获取当前PU邻域的可用性,对参考像素进行滤波,代码里的宽长都为当前cu的宽长,但是pu与tu的划分是以depth为基础的隐式划分,名字上仍以Cu表示,实际此Cu已经代表了PU或TU V
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];
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恒为真
if (doFastSearch)//此时是肯定会进入
{
assert(numModesForFullRD < numModesAvailable);//确定numModesForFullRD < numModesAvailable
for( Int i=0; i < numModesForFullRD; i++ )
{
CandCostList[ i ] = MAX_DOUBLE;//初始化率失真表,全部为最大值,方便后面比较。
}
CandNum = 0;
for( Int modeIdx = 0; modeIdx < numModesAvailable; modeIdx++ )//遍历35中预测模式
{
UInt uiMode = modeIdx;
//调用亮度帧内预测函数 V
predIntraLumaAng( pcCU->getPattern(), uiMode, piPred, uiStride, uiWidth, uiHeight, bAboveAvail, bLeftAvail );
// use hadamard transform here哈达玛矩阵计算率失真
UInt uiSad = m_pcRdCost->calcHAD(g_bitDepthY, piOrg, uiStride, piPred, uiStride, uiWidth, uiHeight );//计算SATD(残差经HAD后的绝对值总和)
UInt iModeBits = xModeBitsIntra( pcCU, uiMode, uiPU, uiPartOffset, uiDepth, uiInitTrDepth );//计算编码当面所需的bits
Double cost = (Double)uiSad + (Double)iModeBits * m_pcRdCost->getSqrtLambda();//率失真代价
//iModeBits编码当前模式需要的bits,
CandNum += xUpdateCandList( uiMode, cost, numModesForFullRD, uiRdModeList, CandCostList );//帧内预测模式候选列表
}
#if FAST_UDI_USE_MPM
Int uiPreds[3] = {-1, -1, -1};
Int iMode = -1;//分两种情况,如果前两个相同iMode=1,否则iMode=2
Int numCand = pcCU->getIntraDirLumaPredictor( uiPartOffset, uiPreds, &iMode );//获取亮度预测的前三个MPMs
if( iMode >= 0 )
{
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
}
else
{
for( Int i=0; i < numModesForFullRD; i++)
{
uiRdModeList[i] = i;
}
}
//check modes 确定帧内预测模式的最佳值主要有以下几个步骤:
//1. 对numModesForFullRD中预测模式进行遍历,算出RDcosts,但至多对depth=1的CU进行遍历,提高了速度。
//2.得到最优,有可能包括次优的两个。
//3.最佳模式下的分割模式遍历,以得最优结果。
//===== check modes (using r-d costs) =====
#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;//RDcosts
for( UInt uiMode = 0; uiMode < numModesForFullRD; uiMode++ )
{
// set luma prediction mode
UInt uiOrgMode = uiRdModeList[uiMode];
pcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );
// set context models
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
// determine residual for partition
UInt uiPUDistY = 0;//当前预测模式的亮度失真
UInt uiPUDistC = 0;//当前色度失真
Double dPUCost = 0.0;//当前预测RDcost
#if HHI_RQT_INTRA_SPEEDUP
xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, true, dPUCost );
#else
xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, dPUCost );
#endif
// check r-d cost
if( dPUCost < dBestPUCost )//更新最佳预测模式相关参数
{
#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 )
{
uiSecondBestMode = uiOrgMode;
dSecondBestPUCost = dPUCost;
}
#endif
} // Mode loop
#if HHI_RQT_INTRA_SPEEDUP
#if HHI_RQT_INTRA_SPEEDUP_MOD
for( UInt ui =0; ui < 2; ++ui )
#endif
{
#if HHI_RQT_INTRA_SPEEDUP_MOD
UInt uiOrgMode = ui ? uiSecondBestMode : uiBestPUMode;
if( uiOrgMode == MAX_UINT )
{
break;
}
#else
UInt uiOrgMode = uiBestPUMode;//设置为最佳模式
#endif
pcCU->setLumaIntraDirSubParts ( uiOrgMode, uiPartOffset, uiDepth + uiInitTrDepth );
// set context models
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
// determine residual for partition
UInt uiPUDistY = 0;
UInt uiPUDistC = 0;
Double dPUCost = 0.0;
xRecurIntraCodingQT( pcCU, uiInitTrDepth, uiPartOffset, bLumaOnly, pcOrgYuv, pcPredYuv, pcResiYuv, uiPUDistY, uiPUDistC, false, dPUCost );
//此时倒数第二个参数为false
// check r-d cost
if( dPUCost < dBestPUCost )
{
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 ---
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 ====
pcCU->setLumaIntraDirSubParts ( uiBestPUMode, uiPartOffset, uiDepth + uiInitTrDepth );
pcCU->copyToPic ( uiDepth, uiPU, uiInitTrDepth );
} // PU loop
if( uiNumPU > 1 )
{ // set Cbf for all blocks
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 =====
m_pcRDGoOnSbacCoder->load(m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST]);
//===== set distortion (rate and r-d costs are determined later) =====
ruiDistC = uiOverallDistC;
pcCU->getTotalDistortion() = uiOverallDistY + uiOverallDistC;
}
Void
TEncSearch::estIntraPredChromaQT( TComDataCU* pcCU,
TComYuv* pcOrgYuv,
TComYuv* pcPredYuv,
TComYuv* pcResiYuv,
TComYuv* pcRecoYuv,
UInt uiPreCalcDistC )
{
UInt uiDepth = pcCU->getDepth(0);
UInt uiBestMode = 0;
UInt uiBestDist = 0;
Double dBestCost = MAX_DOUBLE;
//----- init mode list -----
UInt uiMinMode = 0;
UInt uiModeList[ NUM_CHROMA_MODE ];
pcCU->getAllowedChromaDir( 0, uiModeList );
UInt uiMaxMode = NUM_CHROMA_MODE;
//----- check chroma modes -----
for( UInt uiMode = uiMinMode; uiMode < uiMaxMode; uiMode++ )
{
//----- restore context models -----
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
//----- chroma coding -----
UInt uiDist = 0;
pcCU->setChromIntraDirSubParts ( uiModeList[uiMode], 0, uiDepth );
xRecurIntraChromaCodingQT ( pcCU, 0, 0, pcOrgYuv, pcPredYuv, pcResiYuv, uiDist );
if( pcCU->getSlice()->getPPS()->getUseTransformSkip() )
{
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
}
UInt uiBits = xGetIntraBitsQT( pcCU, 0, 0, false, true, false );
Double dCost = m_pcRdCost->calcRdCost( uiBits, uiDist );
//----- compare -----
if( dCost < dBestCost )
{
dBestCost = dCost;
uiBestDist = uiDist;
uiBestMode = uiModeList[uiMode];
UInt uiQPN = pcCU->getPic()->getNumPartInCU() >> ( uiDepth << 1 );
xSetIntraResultChromaQT( pcCU, 0, 0, pcRecoYuv );
::memcpy( m_puhQTTempCbf[1], pcCU->getCbf( TEXT_CHROMA_U ), uiQPN * sizeof( UChar ) );
::memcpy( m_puhQTTempCbf[2], pcCU->getCbf( TEXT_CHROMA_V ), uiQPN * sizeof( UChar ) );
::memcpy( m_puhQTTempTransformSkipFlag[1], pcCU->getTransformSkip( TEXT_CHROMA_U ), uiQPN * sizeof( UChar ) );
::memcpy( m_puhQTTempTransformSkipFlag[2], pcCU->getTransformSkip( TEXT_CHROMA_V ), uiQPN * sizeof( UChar ) );
}
}
//----- set data -----
UInt uiQPN = pcCU->getPic()->getNumPartInCU() >> ( uiDepth << 1 );
::memcpy( pcCU->getCbf( TEXT_CHROMA_U ), m_puhQTTempCbf[1], uiQPN * sizeof( UChar ) );
::memcpy( pcCU->getCbf( TEXT_CHROMA_V ), m_puhQTTempCbf[2], uiQPN * sizeof( UChar ) );
::memcpy( pcCU->getTransformSkip( TEXT_CHROMA_U ), m_puhQTTempTransformSkipFlag[1], uiQPN * sizeof( UChar ) );
::memcpy( pcCU->getTransformSkip( TEXT_CHROMA_V ), m_puhQTTempTransformSkipFlag[2], uiQPN * sizeof( UChar ) );
pcCU->setChromIntraDirSubParts( uiBestMode, 0, uiDepth );
pcCU->getTotalDistortion () += uiBestDist - uiPreCalcDistC;
//----- restore context models -----
m_pcRDGoOnSbacCoder->load( m_pppcRDSbacCoder[uiDepth][CI_CURR_BEST] );
}