1. 程式人生 > >HEVC亮度分量幀內預測模式程式碼詳解

HEVC亮度分量幀內預測模式程式碼詳解

作者:66

(轉載請註明出處)

從我自身的學習歷程來看,建議大家先把理論紮實了再去理解程式碼,程式碼裡也有註釋,與理論的地方一對應加上一點C程式設計基礎很容易就能理解。

按從內到外的順序,能夠更清晰地理解程式碼實現。

1. HEVC幀內Planar模式預測-預測畫素計算

 

圖一、參考畫素和預測畫素分佈

預測畫素可以看成水平、垂直方向的平均值,計算如下:

 

(不支援公式編輯,只能截圖上傳)

其實通過公式可以知道,在planar模式下,HEVC是將右面那行和下面那行的參考畫素分別用右上和左下那一點填充全部,然後按畫素漸變處理。

對於4x4TU,參考畫素不濾波。

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中,也包含了編號為1DC模式預測畫素計算。


圖二、角度模式的方向

每個角度模式都有相應的偏移編號,通過偏移可以計算角度。

 

圖三、角度模式下的偏移編號。

上面數格子可以看到偏移量的絕對值有[0,    2,    5,   9,  13,  17,  21,  26,  32]

對於水平模式11~17、垂直模式18~25即角度偏移量為負的角度模式,在計算重構畫素前要進行投影。

具體投影方式以模式

20offset=-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,再進一步修正:

左上角:

P1,1=R1,0+R0,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