HEVC亮度分量幀內預測模式程式碼詳解
作者:66
(轉載請註明出處)
從我自身的學習歷程來看,建議大家先把理論紮實了再去理解程式碼,程式碼裡也有註釋,與理論的地方一對應加上一點C程式設計基礎很容易就能理解。
按從內到外的順序,能夠更清晰地理解程式碼實現。
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即角度偏移量為負的角度模式,在計算重構畫素前要進行投影。
具體投影方式以模式
圖四、角度投影示意
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