HEVC亮度分量帧内预测模式代码详解
作者:66
(转载请注明出处)
从我自身的学习历程来看,建议大家先把理论扎实了再去理解代码,代码里也有注释,与理论的地方一对应加上一点C编程基础很容易就能理解。
我们先了解相关理论,此处可参考这一篇博文,基本和他推荐的书上大同小异,链接:http://blog.csdn.net/linpengbin/article/details/44158331,个别图片不好理解,但配文字足够了。
按从内到外的顺序,能够更清晰地理解代码实现。
1. HEVC帧内Planar模式预测-预测像素计算
图一、参考像素和预测像素分布
预测像素可以看成水平、垂直方向的平均值,计算如下:
(不支持公式编辑,只能截图上传)
其实通过公式可以知道,在planar模式下,HEVC是将右面那行和下面那行的参考像素分别用右上和左下那一点填充全部,然后按像素渐变处理。
对于4x4的TU,参考像素不滤波。
xPredIntraPlanar函数处理细节:
代码部分:
/** Function for deriving planar intra prediction.
* \param pSrc pointer to reconstructed sample array
* \param srcStride the stride of the reconstructed sample array
* \param rpDst reference to pointer for the prediction sample array
* \param dstStride the stride of the prediction sample array
* \param width the width of the block
* \param height the height of the block
*
* This function derives the prediction samples for planar mode (intra coding).
*/
//planar模式下计算预测像素
Void TComPrediction::xPredIntraPlanar( Int* pSrc, Int srcStride, Pel* rpDst, Int dstStride, UInt width, UInt height )
{
assert(width == height);
Int k, l, bottomLeft, topRight;
Int horPred;
Int leftColumn[MAX_CU_SIZE+1], topRow[MAX_CU_SIZE+1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
UInt blkSize = width;//像素块宽度
UInt offset2D = width;//N
UInt shift1D = g_aucConvertToBit[ width ] + 2;//LOG2(N)
UInt shift2D = shift1D + 1;//LOG2(N) + 1
// Get left and above reference column and row
for(k=0;k<blkSize+1;k++)//不需要左上顶点
{
topRow[k] = pSrc[k-srcStride];//当前PU上方一行零一个
leftColumn[k] = pSrc[k*srcStride-1];//当前PU左面一列零一个
}
// Prepare intermediate variables used in interpolation
bottomLeft = leftColumn[blkSize];//左下那一个
topRight = topRow[blkSize];//右上那一个
//计算公式
//Ph(x,y) = (width - x)leftColumn(y) + x*topRight,这样写能看出距离越远,权值不同
//Pv(x,y) = (width - y)topRow(x) + y*bottomleft
//P(x,y) = (Ph(x,y) + Pv(x,y) + offset2D)>>(shift2D),明明就一个width,它偏叫个offset2D。
//代码里做了调整如下
//Ph(x,y) = width*leftColumn(y) + x*(topRight - leftColumn(y))
//Pv(x,y) = width*topRow(x) + y*(bottomleft - topRow(x))
for (k=0;k<blkSize;k++)
{
bottomRow[k] = bottomLeft - topRow[k];//
rightColumn[k] = topRight - leftColumn[k];//
topRow[k] <<= shift1D;//等同于topRow[k] *= width
leftColumn[k] <<= shift1D;//等同于leftColumn[k] *= width
}
// Generate prediction signal
for (k=0;k<blkSize;k++)//k代表y向,I代表x向
{
horPred = leftColumn[k] + offset2D;//每次出循环更新一次
for (l=0;l<blkSize;l++)//这里用加减替掉了乘,虽然简单,但自己以前没想到过,学习了。
{
horPred += rightColumn[k];
topRow[l] += bottomRow[l];
rpDst[k*dstStride+l] = ( (horPred + topRow[l]) >> shift2D );
}
}
}
2. HEVC帧内角度模式预测-预测像素计算
HEVC亮度分量帧内预测支持5中大小的PU:4X4 ~ 64X64,都对应35中预测模式。
其中编号2~34为角度模式,在下面解析的函数xPredIntraAng中,也包含了编号为1的DC模式预测像素计算。
图二、角度模式的方向
每个角度模式都有相应的偏移编号,通过偏移可以计算角度。
图三、角度模式下的偏移编号。
上面数格子可以看到偏移量的绝对值有[0, 2, 5, 9, 13, 17, 21, 26, 32]。
对于水平模式11~17、垂直模式18~25即角度偏移量为负的角度模式,在计算重构像素前要进行投影。
具体投影方式以模式20(offset=-21)为例,左侧边界的参考像素按角度方向可以投影到上方参考像素的左侧。
图四、角度投影示意
Ref表示投影buf:
接着计算当前像素对应的参考像素在Ref中的位置:
加权因子ω:
最后是像素预测值:
具体的计算方式和最后像素预测值在代码中有详细解释,不作赘述。
代码中包含了DC模式下预测像素的初步填充:
Void TComPrediction::xPredIntraAng(Int bitDepth, Int* pSrc, Int srcStride, Pel*& rpDst, Int dstStride, UInt width, UInt height, UInt dirMode, Bool blkAboveAvailable, Bool blkLeftAvailable, Bool bFilter )
{
Int k,l;
Int blkSize = width;
Pel* pDst = rpDst;
// Map the mode index to main prediction direction and angle
assert( dirMode > 0 ); //no planar
Bool modeDC = dirMode < 2;//DC是1
Bool modeHor = !modeDC && (dirMode < 18);//水平类角度2~17
Bool modeVer = !modeDC && !modeHor;//垂直类角度18~34
Int intraPredAngle = modeVer ? (Int)dirMode - VER_IDX : modeHor ? -((Int)dirMode - HOR_IDX) : 0;//ver:(18到34-26=-8到8,hor:2至17-10 = -8至7.
Int absAng = abs(intraPredAngle);
Int signAng = intraPredAngle < 0 ? -1 : 1;//水平左半部分为-,右半部分为+;垂直下半部分为-,上半部分角度为+。
// Set bitshifts and scale the angle parameter to block size
Int angTable[9] = {0, 2, 5, 9, 13, 17, 21, 26, 32};
Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle
Int invAngle = invAngTable[absAng];//选择偏移度
absAng = angTable[absAng];//映射时*32
intraPredAngle = signAng * absAng;//加上符号表示偏移方向
// Do the DC prediction
if (modeDC)//如果是DC模式,所有值设为dcval
{
Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height, blkAboveAvailable, blkLeftAvailable);
//dcbal取可用参考像素平均值,仅算上面和左面
for (k=0;k<blkSize;k++)
{
for (l=0;l<blkSize;l++)
{
pDst[k*dstStride+l] = dcval;
}
}
}
// Do angular predictions角度预测部分
else
{
Pel* refMain;
Pel* refSide;
Pel refAbove[2*MAX_CU_SIZE+1];
Pel refLeft[2*MAX_CU_SIZE+1];
// Initialise the Main and Left reference array.
if (intraPredAngle < 0)
{
for (k=0;k<blkSize+1;k++)
{
refAbove[k+blkSize-1] = pSrc[k-srcStride-1];//上方复制参考像素
}
for (k=0;k<blkSize+1;k++)
{
refLeft[k+blkSize-1] = pSrc[(k-1)*srcStride-1];//同上
}
refMain = (modeVer ? refAbove : refLeft) + (blkSize-1);//存放ref的buf
refSide = (modeVer ? refLeft : refAbove) + (blkSize-1);//需映射的像素的起点
// Extend the Main reference to the left.
Int invAngleSum = 128; // rounding for (shift by 8)
for (k=-1; k>blkSize*intraPredAngle>>5; k--)//填充映射像素
{
invAngleSum += invAngle;
refMain[k] = refSide[invAngleSum>>8];//一次一步将side映射到ref中
}
}
else//intraPredAngle>0,不需要映射
{
for (k=0;k<2*blkSize+1;k++)//拷贝上和左参考像素
{
refAbove[k] = pSrc[k-srcStride-1];
}
for (k=0;k<2*blkSize+1;k++)
{
refLeft[k] = pSrc[(k-1)*srcStride-1];
}
refMain = modeVer ? refAbove : refLeft;
refSide = modeVer ? refLeft : refAbove;
}
if (intraPredAngle == 0)//10水平或26垂直的模式,
{
for (k=0;k<blkSize;k++)
{
for (l=0;l<blkSize;l++)
{
pDst[k*dstStride+l] = refMain[l+1];
}
}
if ( bFilter )//修正一行或一列的像素,refSide[k+1]-refSide[0]为预测方向上的像素变化
{
for (k=0;k<blkSize;k++)
{
pDst[k*dstStride] = Clip3(0, (1<<bitDepth)-1, pDst[k*dstStride] + (( refSide[k+1] - refSide[0] ) >> 1) );
}
}
}
else
{//开始计算预测像素值
Int deltaPos=0;
Int deltaInt;
Int deltaFract;
Int refMainIndex;
//当前像素的预测值,P(x,y)= ( ((32-deltaFract) * refMain[refMainIndex] + deltaFract * refMain[refMainIndex + 1] + 16) >> 5 )
for (k=0;k<blkSize;k++)
{
deltaPos += intraPredAngle;
deltaInt = deltaPos >> 5;
deltaFract = deltaPos & (32 - 1);
if (deltaFract)
{
// Do linear filtering
for (l=0;l<blkSize;l++)
{
refMainIndex = l+deltaInt+1;
pDst[k*dstStride+l] = (Pel) ( ((32-deltaFract)*refMain[refMainIndex]+deltaFract*refMain[refMainIndex+1]+16) >> 5 );
}
}
else
{
// Just copy the integer samples
for (l=0;l<blkSize;l++)//就是(((32-0) * refMain[refMainIndex] + 0 * refMain[refMainIndex+1] + 16) >> 5 );
{
pDst[k*dstStride+l] = refMain[l+deltaInt+1];
}
}
}
}
//上面没有refmain,refside避免区分上和左,但像素填充全按垂直的那几个模式,所以水平模式还要再翻转一下。
// Flip the block if this is the horizontal mode
if (modeHor)
{
Pel tmp;
for (k=0;k<blkSize-1;k++)
{
for (l=k+1;l<blkSize;l++)
{
tmp = pDst[k*dstStride+l];
pDst[k*dstStride+l] = pDst[l*dstStride+k];
pDst[l*dstStride+k] = tmp;
}
}
}
}
}
3.DC模式下预测像素计算
在角度预测中将DC模式下预测值设为dcval,再进一步修正:
左上角:
P(1,1)=(R(1,0)+R(0,1)+2*dcValue+2)>>2
第一行(除左上角):
P(x,1) = (R(x,0) + 3*dcValue + 2)>>2
第一列(除左上角):
P(1,y) = (R(0,y) + 3*dcValue + 2)>>2
这段代码很简单,如下:
/** Function for filtering intra DC predictor.
* \param pSrc pointer to reconstructed sample array
* \param iSrcStride the stride of the reconstructed sample array
* \param rpDst reference to pointer for the prediction sample array
* \param iDstStride the stride of the prediction sample array
* \param iWidth the width of the block
* \param iHeight the height of the block
*
* This function performs filtering left and top edges of the prediction samples for DC mode (intra coding).
*/
Void TComPrediction::xDCPredFiltering( Int* pSrc, Int iSrcStride, Pel*& rpDst, Int iDstStride, Int iWidth, Int iHeight )
{
Pel* pDst = rpDst;
Int x, y, iDstStride2, iSrcStride2;
// boundary pixels processing
pDst[0] = (Pel)((pSrc[-iSrcStride] + pSrc[-1] + 2 * pDst[0] + 2) >> 2);//左上角
for ( x = 1; x < iWidth; x++ )//第一行
{
pDst[x] = (Pel)((pSrc[x - iSrcStride] + 3 * pDst[x] + 2) >> 2);
}
for ( y = 1, iDstStride2 = iDstStride, iSrcStride2 = iSrcStride-1; y < iHeight; y++, iDstStride2+=iDstStride, iSrcStride2+=iSrcStride )//第一列
{
pDst[iDstStride2] = (Pel)((pSrc[iSrcStride2] + 3 * pDst[iDstStride2] + 2) >> 2);
}
return;
}
4.亮度分量预测像素的计算
调用上面三个函数,实现亮度预测像素计算的函数。
使用if~else判断来调用以上三个函数。
//亮度分量角度预测
Void TComPrediction::predIntraLumaAng(TComPattern* pcTComPattern, UInt uiDirMode, Pel* piPred, UInt uiStride, Int iWidth, Int iHeight, Bool bAbove, Bool bLeft )
{
Pel *pDst = piPred;
Int *ptrSrc;
assert( g_aucConvertToBit[ iWidth ] >= 0 ); // 4x 4
assert( g_aucConvertToBit[ iWidth ] <= 5 ); // 128x128
assert( iWidth == iHeight );
//指向经滤波后的参考像素首地址
ptrSrc = pcTComPattern->getPredictorPtr( uiDirMode, g_aucConvertToBit[ iWidth ] + 2, m_piYuvExt );
// get starting pixel in block
Int sw = 2 * iWidth + 1;//当前PU首地址在ptrSrc中的偏移
// Create the prediction
if ( uiDirMode == PLANAR_IDX )//planar模式
{
xPredIntraPlanar( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight );//计算像素预测值
}
else
{
if ( (iWidth > 16) || (iHeight > 16) )//角度模式,计算像素预测值
{
xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, false );
}
else
{
xPredIntraAng(g_bitDepthY, ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight, uiDirMode, bAbove, bLeft, true );//注意,最后一个为true
if( (uiDirMode == DC_IDX ) && bAbove && bLeft )//DC模式
{
xDCPredFiltering( ptrSrc+sw+1, sw, pDst, uiStride, iWidth, iHeight);
}
}
}
}
5.estIntraPredQT亮度分量预测全过程
函数中主要完成对亮度分量的预测,遍历所有分割和最优预测模式,从中选出率失真最好的模式,因涉及到更多的内容,在之后的总结总进行分析。
图五、本文分析函数结构图
代码如下:
//亮度分量帧内预测全过程
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;
}
(转载请务必注明出处)
作者:66