H.266/VVC代码学习:DC模式和Planar模式

Planar模式和DC模式时两种特殊的角度模式,分别对应于模式号0和1。

一、DC模式

DC模式适用于大面积平坦区域,其预测值是通过计算左边和(或)上边参考像素的平均值获得的。

1.1、计算

DC模式预测值的计算和块的形状有关:

  • 当宽等于高时,用左侧参考像素和上边参考像素的平均值作为预测值填充整个块
  • 当宽大于高时,用上边参考像素的平均值作为预测值填充整个块
  • 当宽小于高时,用左侧参考像素的平均值作为预测值填充整个块

1.2、代码

xPredIntraDc函数是DC模式的入口函数,主要功能是调用xGetPredValDc函数生成DC值,然后将DC值填充整个块

void IntraPrediction::xPredIntraDc( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter )
{
  //DC模式适用于大面积平坦区域,其预测值由其左侧和(或)上侧参考像素值得到。
  const Pel dcval = xGetPredValDc( pSrc, pDst );//生成DC值
  pDst.fill( dcval );//填充
}

xGetPredValDc函数用来生成DC值

// Function for calculating DC value of the reference samples used in Intra prediction
//NOTE: Bit-Limit - 25-bit source
Pel IntraPrediction::xGetPredValDc( const CPelBuf &pSrc, const Size &dstSize )
{
  CHECK( dstSize.width == 0 || dstSize.height == 0, "Empty area provided" );

  int idx, sum = 0;
  Pel dcVal;
  const int width  = dstSize.width;
  const int height = dstSize.height;
  const auto denom     = (width == height) ? (width << 1) : std::max(width,height);
  const auto divShift  = floorLog2(denom);
  const auto divOffset = (denom >> 1);

  if ( width >= height )//宽大于高时,只用预测像素宽的平均值
  {
    for( idx = 0; idx < width; idx++ )
    {
      sum += pSrc.at(m_ipaParam.multiRefIndex + 1 + idx, 0);
    }
  }
  if ( width <= height )//宽小于高时,只用预测像素高的平均值
  {
    for( idx = 0; idx < height; idx++ )
    {
      sum += pSrc.at(m_ipaParam.multiRefIndex + 1 + idx, 1);
    }
  }

  dcVal = (sum + divOffset) >> divShift;//四舍五入求平均值
  return dcVal;
}

二、Planar模式

Planar模式适用于像素渐变的情况,即适用于像素值缓慢变换的区域。

2.1、计算

计算方法:根据a,b,c,d求加权平均,权重与距离有关;

具体计算公式如下:

横向预测值为 pred_h(x,y) = (width-x-1)*b + (x+1)*a

纵向预测值为 pred_v(x,y) = (height-y-1)*d + (y+1)*c

最终预测值为 pred(x,y) = (pred_h(x,y) + pred_v(x,y) + offset) >> shift

其中,x=0,1···width-1;y=0,1···height-1

2.2、代码

/** Function for deriving planar intra prediction. This function derives the prediction samples for planar mode (intra coding).
 */

//NOTE: Bit-Limit - 24-bit source
void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst )
{
  const uint32_t width  = pDst.width;
  const uint32_t height = pDst.height;

#if JVET_P0329_PLANAR_SIMPLIFICATION
  const uint32_t log2W = floorLog2( width );
  const uint32_t log2H = floorLog2( height );
#else
  const uint32_t log2W  = floorLog2(width  < 2 ? 2 : width);
  const uint32_t log2H  = floorLog2(height < 2 ? 2 : height);
#endif

  int leftColumn[MAX_CU_SIZE + 1], topRow[MAX_CU_SIZE + 1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
  const uint32_t offset = 1 << (log2W + log2H);//后面用于四舍五入

  // Get left and above reference column and row
  // 获得参考像素
  for( int k = 0; k < width + 1; k++ )
  {
    topRow[k] = pSrc.at( k + 1, 0 );//获取上一行的像素值
  }

  for( int k = 0; k < height + 1; k++ )
  {
    leftColumn[k] = pSrc.at(k + 1, 1);//获取左一列的像素值
  }
  /**
  * Planar模式横向的预测值为 pred_h(x,y) = (width-x-1)*leftcolumn(y) + (x+1)*topRight
  * Planar模式纵向的预测值为 pred_v(x,y) = (height-y-1)*topRow(x) + (y+1)*bottomLeft
  * 最终预测值为:pred(x,y) = (pred_h + pred_v + offset) >> shift
  *
  * 代码里将上式调整为:pred_h(x,y) = width*leftcolumn(y) + (x+1)*[topRight-leftcolumn(y)]
  *                     pred_v(x,y) = height*topRow(x) + (y+1)*[bottomLeft-topRow(x)]
  **/

  // Prepare intermediate variables used in interpolation
  // 准备用于插值的中间变量
  int bottomLeft = leftColumn[height];//左边界下一个
  int topRight = topRow[width];//上边界右一位
  //填充四个边缘的值
  for( int k = 0; k < width; k++ )
  {
    bottomRow[k] = bottomLeft - topRow[k];
    topRow[k]    = topRow[k] << log2H;//实现height*topRow(x) 
  }

  for( int k = 0; k < height; k++ )
  {
    rightColumn[k] = topRight - leftColumn[k];
    leftColumn[k]  = leftColumn[k] << log2W;//实现width*leftcolumn(y)
  }
  //填充Planar模式CU内部的像素值
  const uint32_t finalShift = 1 + log2W + log2H;
  const uint32_t stride     = pDst.stride;
  Pel*       pred       = pDst.buf;//预测像素的首地址

  for( int y = 0; y < height; y++, pred += stride )
  {
    int horPred = leftColumn[y];//水平方向预测值

    for( int x = 0; x < width; x++ )
    {
      horPred += rightColumn[y];//累加,通过循环实现(x+1)*[topRight-leftcolumn(y)]
      topRow[x] += bottomRow[x];//累加,通过循环实现(y+1)*[bottomLeft-topRow(x)]

      int vertPred = topRow[x];//垂直方向预测值
      //水平方向预测像素的权重和高度有关,垂直方向的预测像素的权重和宽度有关
      pred[x]      = ( ( horPred << log2H ) + ( vertPred << log2W ) + offset ) >> finalShift;//加权后求平均值,获得最终预测像素
    }
  }
}

 

发布了95 篇原创文章 · 获赞 138 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/104433037