VVC帧内预测 xPredIntraPlanar(),xPredIntraDc()函数

void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst )
{
  //块的宽高
  const uint32_t width  = pDst.width;
  const uint32_t height = pDst.height;

  const uint32_t log2W = floorLog2( width );
  const uint32_t log2H = floorLog2( height );
  // 存储数据
  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
  CHECK(width > MAX_CU_SIZE, "width greater than limit");
  //上方参考像素
  for( int k = 0; k < width + 1; k++ )
  {
    topRow[k] = pSrc.at( k + 1, 0 );
  }

  CHECK(height > MAX_CU_SIZE, "height greater than limit");
  //左列参考像素
  for( int k = 0; k < height + 1; k++ )
  {
    leftColumn[k] = pSrc.at(k + 1, 1);
  }

  // Prepare intermediate variables used in interpolation
  //准备插值中使用的中间变量
  int bottomLeft = leftColumn[height];
  int topRight = topRow[width];

 

这是正式标准的算法

 以8x8的块为例

 参考像素为蓝色(x = −1, y = −1..nTbH)和粉色(x = 0..nTbW, y = −1

注意: pSrc.at( k + 1, 0 )这里K+1就是为了不把左上角参考像素存进数组,以64X64为例,一共存入65个值。topRow[64]即第65个值

bottomLeft:就是a

topRight:是c

a和c的值不会变

然后再根据当前预测像素的位置,结合b,d得出像素预测值

 //
  for( int k = 0; k < width; k++ )
  {
    bottomRow[k] = bottomLeft - topRow[k];
    topRow[k]    = topRow[k] << log2H;
  }

  for( int k = 0; k < height; k++ )
  {
    rightColumn[k] = topRight - leftColumn[k];
    leftColumn[k]  = leftColumn[k] << log2W;
  }

  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];
      topRow[x] += bottomRow[x];

      int vertPred = topRow[x];
      pred[x]      = ( ( horPred << log2H ) + ( vertPred << log2W ) + offset ) >> finalShift;
    }
  }
}

具体计算公式: 

bottomRow[],rightColumn[]:a,c通过与leftColumn,topRow相互计算,得出的值填充并形成块右,块下参考像素 

 finalShift:就是Log2 ( nTbW ) + Log2 ( nTbH ) + 1

offset:就是nTbW * nTbH

leftColumn[k]' = leftColumn[k] *width:这一步没看懂什么意思,好像是什么线性滤波器的效果,之后再看

 pred += stride:因为预测值是一行一行,一个个元素存进去的。所以每存完一行像素的预测值就需要加上这一行所有像素的地址。然后再开始新的一行新的首地址来存储预测值,pred[x],x在变,存的一行中一个个像素的预测值,pred首地址在变,每一个首地址代表一行像素的预测值。用stride是因为可能到了后几个CTU,会进行填充才能划分出一个CTU,所以用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];//垂直方向预测值

horPred:用来存水平预测值。在x循环时的horPred才是最终值,在y循环时只是先建立这个变量

代码实现效果:horPred =  这一行前面的像素水平预测值之和与右侧参考像素相加所得值

                         vertPred = 这一列前面的像素垂直预测值之和与下边参考像素值相加所得值

horPred最终可简化为:

leftColumn[k]'  = leftColumn[k] *width
rightColumn[k] = topRight - leftColumn[k]

x=0    horPred =horPred+ rightColumn[y]
                        =leftColumn[y] *width+topRight - leftColumn[y]

x=1      horPred =leftColumn[y] *width+topRight - leftColumn[y]+topRight - leftColumn[y]

x=2      horPred =leftColumn[y] *width+topRight - leftColumn[y]+topRight - leftColumn[y]+topRight - leftColumn[y]

            horPred = leftColumn[y] *width+(x+1)(topRight - leftColumn[y])

 vertPred,因为每一个y的循环里,topRow[x]都在累加,然后赋给vertPred,所以可简化为:

 topRow[k] '   = topRow[k] *height
 bottomRow[k] = bottomLeft - topRow[k]

y=0              vertPred = topRow[x] + bottomRow[x]
                               = topRow[x] *height + bottomLeft - topRow[x]

y=1              vertPred = topRow[x] *height + bottomLeft - topRow[x]+ bottomLeft - topRow[x]
  
y=3              vertPred = topRow[x] *height + bottomLeft - topRow[x]+ bottomLeft - topRow[x]+ bottomLeft - topRow[x]
        
                    vertPred = topRow[x] *height +(y+1)(bottomLeft - topRow[x])

与算法描述完全一致 

2.DC模式

void IntraPrediction::xPredIntraDc( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter )
{
  const Pel dcval = xGetPredValDc( pSrc, pDst );
  pDst.fill( dcval );
}

从函数中得到dcval值,然后填充到pDst里 

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; //DC值
  const int width  = dstSize.width;
  const int height = dstSize.height;
  //若width == height,则为2*width
  //若width != height,则为max(width, height)
  const auto denom     = (width == height) ? (width << 1) : std::max(width,height);
  //宽高不相等时,divShift 要小一倍
  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;
}

divShift:=( Log2( nTbW ) + 1 )

multiRefIndex :这个与MRL有关,但一般默认关闭

宽大于高的情况,即上参考像素之和。高大于宽也是相同道理:

width >= height

idx = 0    sum = pSrc.at( 1 + idx, 0)

idx = 1    sum = pSrc.at( 1 , 0) + pSrc.at( 1 + idx, 0)

idx = 2    sum = pSrc.at( 1 , 0) + pSrc.at( 2, 0) + pSrc.at( 1 + idx, 0)

而块相等的时候,因为if条件都满足,所以是两个sum的和 。

这里以64X64的块为例,与算法一致

denom =128;divShift = 7 ;divOffset = 64

dcVal = (sum + 64) >> 7;

最后返回 dcVal值

注意the neighbouring samples p[ x ][ y ], with x = −1 − refIdx, y = 0..nTbH − 1 and x = 0..nTbW − 1, y = −1 − refIdx

这里显示没有像planar模式一样,用到左下角,右上方的参考像素

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值