HEVC学习(十一) —— 帧内预测系列之七

这个可以算是帧内预测中最为核心的一个部分了,不过相信有了前面那些基础积淀,这个部分看起来反而没有原来那么难了。下面直接给出代码及其相关注释:

// Function for deriving the angular Intra predictions

/** Function for deriving the simplified angular intra predictions.
 * \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
 * \param dirMode the intra prediction mode index
 * \param blkAboveAvailable boolean indication if the block above is available
 * \param blkLeftAvailable boolean indication if the block to the left is available
 *
 * This function derives the prediction samples for the angular mode based on the prediction direction indicated by
 * the prediction mode index. The prediction direction is given by the displacement of the bottom row of the block and
 * the reference row above the block in the case of vertical prediction or displacement of the rightmost column
 * of the block and reference column left from the block in the case of the horizontal prediction. The displacement
 * is signalled at 1/32 pixel accuracy. When projection of the predicted pixel falls inbetween reference samples,
 * the predicted value for the pixel is linearly interpolated from the reference samples. All reference samples are taken
 * from the extended main reference.
 */
Void TComPrediction::xPredIntraAng( 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;	//!< 当前PU的宽度
  Pel* pDst          = rpDst;	//!< 指向预测样点区域首地址

  // Map the mode index to main prediction direction and angle
  assert( dirMode > 0 ); //!< no planar,保证不会出现Intra_Planar模式
  Bool modeDC        = dirMode < 2;	//!< DC mode,判断是否是DC预测模式
  Bool modeHor       = !modeDC && (dirMode < 18);		//!< mode 2 ~ 17,水平模式
  Bool modeVer       = !modeDC && !modeHor;		//!< mode 18 ~ 34,垂直模式
  Int intraPredAngle = modeVer ? (Int)dirMode - VER_IDX : modeHor ? -((Int)dirMode - HOR_IDX) : 0;
  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};		// Table 8-4
  //! 先乘以256,等计算后将最终结果除以256,用定点数实现浮点数计算,保证精度
  Int invAngTable[9] = {0, 4096, 1638, 910, 630, 482, 390, 315, 256}; // (256 * 32) / Angle, Table 8-5
  Int invAngle       = invAngTable[absAng];
  absAng             = angTable[absAng];
  intraPredAngle     = signAng * absAng;

  // Do the DC prediction
  if (modeDC)		// draft 8.4.4.2.5
  {
    Pel dcval = predIntraGetPredValDC(pSrc, srcStride, width, height, blkAboveAvailable, blkLeftAvailable);

    for (k=0;k<blkSize;k++)
    {
      for (l=0;l<blkSize;l++)
      {
        pDst[k*dstStride+l] = dcval;	
      }
    }
  }

  // Do angular predictions
  //! 程序的想法是,先把角度都当成垂直预测方向的角度来算,到最后再做个判断,如果实际方向是水平方向,则对预测值做个翻转,以此减少代码冗余
  else		//!< draft 8.4.4.2.6
  {
    Pel* refMain;	//!< main reference
    Pel* refSide;	//!< side reference
    Pel  refAbove[2*MAX_CU_SIZE+1];
    Pel  refLeft[2*MAX_CU_SIZE+1];

    //! Initialize the Main and Left reference array.
    if (intraPredAngle < 0)	//!< the intra prediction angle is negative 
    {
      for (k=0;k<blkSize+1;k++)		//!< 式子(8-40)
      {
        refAbove[k+blkSize-1] = pSrc[k-srcStride-1];	//!< AboveLeft + Above,blkSize+1个点
      }
      for (k=0;k<blkSize+1;k++)		// 式子(8-48)
      {
        refLeft[k+blkSize-1] = pSrc[(k-1)*srcStride-1];		//!< AboveLeft + Left,blkSize+1个点
      }
	  //! modeVer = true: refMain = refAbove + blkSize - 1; refSide = refLeft + blkSize - 1
	  //! modeVer = false: refMain = refLeft + blkSize - 1; refSide = refAbove + blkSize - 1
	  //! 加上blkSize-1这个偏移量是因为draft中ref[x]的x取值范围是-nT..2*nT,而实际的数组下标需从
	  //! 0开始,因此,为了与draft相对应,先将refMain指向refAbove(refLeft)[blkSize-1],即ref[0],
	  //! 这样保证了接下来代码中即使数组下标为负值也不会超出数组的内存范围
      refMain = (modeVer ? refAbove : refLeft) + (blkSize-1);
      refSide = (modeVer ? refLeft : refAbove) + (blkSize-1);

      // Extend the Main reference to the left.	//! 式子(8-41)复制side array样点到main array
      Int invAngleSum    = 128;       //!< rounding for (shift by 8)
      for (k=-1; k>blkSize*intraPredAngle>>5; k--)
      {
        invAngleSum += invAngle;	//!< 等价于k * invAngle,即求出在side reference上的截距
        refMain[k] = refSide[invAngleSum>>8];		//!< 根据计算出来的截距将side reference对应的样点值投影给main reference
      }
    }
    else	//!< intraPredAngle >= 0
    {
      for (k=0;k<2*blkSize+1;k++)		//!< 式子(8-42)
      {
        refAbove[k] = pSrc[k-srcStride-1];
      }
      for (k=0;k<2*blkSize+1;k++)		// 式子(8-50)
      {
        refLeft[k] = pSrc[(k-1)*srcStride-1];
      }
	  //! 角度为正时,对于垂直方向的角度预测来说,只参考上方邻点的样点值;对于水平方向的角度预测来说,只参考左方邻点的样点值
      refMain = modeVer ? refAbove : refLeft;
      refSide = modeVer ? refLeft  : refAbove;
    }

    if (intraPredAngle == 0)		//!< intraPredMode = 10 or 26
    {
      for (k=0;k<blkSize;k++)
      {
        for (l=0;l<blkSize;l++)
        {
          pDst[k*dstStride+l] = refMain[l+1];		//!< 对于Intra_Angular[10]或Intra_Angular[26]的情况,无需计算角度,直接对应点赋值即可,相当于H.264中的垂直预测、水平预测模式
        }
      }

      if ( bFilter )	//!< true for luma while false for chroma
      {
        for (k=0;k<blkSize;k++)	//!< 式子(8-47)
        {//! 这个计算针对Intra_Angular[26]的情况,用之前角度预测得到的样点值、当前PU左上角的样点值与左邻点的样点值的差值加权平均作为滤波结果,起到平滑的作用
          pDst[k*dstStride] = Clip ( pDst[k*dstStride] + (( refSide[k+1] - refSide[0] ) >> 1) );
        }
      }
    }
    else
    {
      Int deltaPos=0;		
      Int deltaInt;		//!< 坐标的整数部分
      Int deltaFract;	//!< 坐标的分数部分
      Int refMainIndex;

      for (k=0;k<blkSize;k++)
      {
        deltaPos += intraPredAngle;		//!< 式子(8-43),每次循环加intraPredAngle,相当于(y+1)*intraPredAngle, y=0..nT-1
        deltaInt   = deltaPos >> 5;
        deltaFract = deltaPos & (32 - 1);	//!< 式子(8-44)

        if (deltaFract)		//!< 非整像素
        {
          // Do linear filtering
          for (l=0;l<blkSize;l++)		//!< 式子(8-45)
          {
            refMainIndex        = l+deltaInt+1;		//!< 得到main reference中对应的整像素点位置
			//!< 用分数部分作为权值,对两个整像素点的样点值进行线性插值,作为预测值
            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++)		//!< 式子(8-46)
          {
            pDst[k*dstStride+l] = refMain[l+deltaInt+1];	//!< 对于整像素点,不需要线性插值,直接拷贝过来作为预测值
          }
        }
      }
    }

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

(转载请注明出处。)

你可能感兴趣的:(hm,HEVC,帧内预测,xPredIntraAng,角度预测)