欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二、HEVC代码学习之帧内预测编码参考像素赋值函数fillReferenceSamples

程序员文章站 2022-06-07 14:14:56
...

fillReferenceSamples函数的作用:它的主要功能是在真正进行帧内预测之前。使用重建后的YUV图像对当前PU的相邻点进行赋值,为接下来的角度预测提供参考点的样值。

fillReferenceSamples函数的算法流程

  • 如果所有相邻点的均不可用(还没有被编码重建),则参考点的样值均被赋值为DC值;
  • 如果所有相邻点均可用,则参考样值都会被赋值为重建YUV图像中与参考点位置相同的样点值;
  • 如果上面两种情况不满足,则按照从左下往左上的,从左上往右上的顺序进行遍历,如果第一个点不可用,则使用下一个可用点对应的重建YUV样点值对其进行赋值;对于除第一个点外的其他相邻点,则使用它的前一个样点值进行赋值,第一个步骤就保证了前一个样点值一定存在。

提醒:如果想具体了解HEVC帧内编码的朋友们,可以参考我写的这篇博客:
HEVC帧内编码的具体原理详解

Void fillReferenceSamples( const Int bitDepth, //一个像素点用几位表示
#if O0043_BEST_EFFORT_DECODING
                           const Int bitDepthDelta, 
#endif
    //piRoiOrigin指向重建的YUV图像对应于当前PU所在位置的首地址
   //该piIntraTemp指针指向当前PU的参考点的位置,piIntraTemp[i]表示PU上方像素点的位置, piIntraTemp[i*uiWidth]指向PU左边像素点的位置
                           const Pel* piRoiOrigin,              
                                 Pel* piIntraTemp,
   //最左下方第一个位置可用标志
                           const Bool* bNeighborFlags,
   //iNumIntraNeighbor用于记录当前PU可用相邻参考点的个数
                           const Int iNumIntraNeighbor, 
 //一个单元的宽度的像素个数
                           const Int unitWidth, 
 //一个单元的高度的像素个数
                           const Int unitHeight, 
 //unitWidth用于记录当前PU左上方,上方,右上方需要填充的参考点的个数
                           const Int iAboveUnits, 
   //unitWidth用于记录当前PU左上方,左边,左下方需要填充的参考点的个数
                           const Int iLeftUnits,
                           const UInt uiWidth, 
                           const UInt uiHeight, 
 //iPicStride用于左边像素点遍历,如果是上方像素点遍历则直接更新指针的索引值i即可,简单来说,就是用于上下单元的切换
                           const Int iPicStride )
{
  //piRoiTemp用于指向所感兴趣的重建YUV的位置地址
  const Pel* piRoiTemp;
  Int  i, j;
  //计算出DC值,用整形变量iDCValues存储
  Int  iDCValue = 1 << (bitDepth - 1);    
  //计算所需参考点的总个数,左边的参考点个数+右边参考点个数,在加上一个左上角的像素点
  const Int iTotalUnits = iAboveUnits + iLeftUnits + 1; //+1 for top-left

  //当前PU的相邻像素的可用个数为0,则参考点像素用DC值进行填充
  if (iNumIntraNeighbor == 0)
  {
    // Fill border with DC value
    for (i=0; i<uiWidth; i++)
    {
      piIntraTemp[i] = iDCValue;
    }
    for (i=1; i<uiHeight; i++)
    {
      piIntraTemp[i*uiWidth] = iDCValue;
    }
  }
  //如果所有的相邻参考点像素值都可用
  else if (iNumIntraNeighbor == iTotalUnits)
  {
    // Fill top-left border and top and top right with rec. samples
    //对左上角的参考像素的样值、上方的参考点样值、右上方的参考点样值进行填充
    piRoiTemp = piRoiOrigin - iPicStride - 1; //该piRoiTemp指针指向左上位置,piRoiOrigin指针向上移动一个像素,载左移动一个像素,到达左上方位置

    for (i=0; i<uiWidth; i++)   //左上方、和上方、右上方位置填充
    {
#if O0043_BEST_EFFORT_DECODING
      piIntraTemp[i] = piRoiTemp[i] << bitDepthDelta;
#else
      piIntraTemp[i] = piRoiTemp[i];
#endif
    }

    // Fill left and below left border with rec. samples
    //填充左边和左下方参考点的像素值
    piRoiTemp = piRoiOrigin - 1;    //该指针指向重构像图像PU位置的左边第一个像素点

    for (i=1; i<uiHeight; i++)
    {
#if O0043_BEST_EFFORT_DECODING
      piIntraTemp[i*uiWidth] = (*(piRoiTemp)) << bitDepthDelta;
#else
      piIntraTemp[i*uiWidth] = *(piRoiTemp);
#endif
      piRoiTemp += iPicStride;  //往下移动一个像素
    }
  }
  //相邻参考点像素部分可用的情况
  else // reference samples are partially available
  {
    // all above units have "unitWidth" samples each, all left/below-left units have "unitHeight" samples each
    //iTotalSamples计算所有样值点的个数
    const Int  iTotalSamples = (iLeftUnits * unitHeight) + ((iAboveUnits + 1) * unitWidth);
    //定义一个数组,用于临时存储用于填充相邻像素的样点值
    Pel  piIntraLine[5 * MAX_CU_SIZE]; 
    Pel  *piIntraLineTemp;
    const Bool *pbNeighborFlags;


    // Initialize 初始化,先将所有的样点值赋值为DC值
    for (i=0; i<iTotalSamples; i++)
    {
      piIntraLine[i] = iDCValue;
    }

    // Fill top-left sample 填充左上角的样点值
    //指向参考帧中当前PU所在位置的左上角
    piRoiTemp = piRoiOrigin - iPicStride - 1;    
    //piIntraLine的扫描顺序为左下到左上,再从左到右上
    piIntraLineTemp = piIntraLine + (iLeftUnits * unitHeight);
    //相邻像素的可用标志首先位于左下方最后一个像素,把它移动到左上方,用于标志左上方像素是否可用
    pbNeighborFlags = bNeighborFlags + iLeftUnits;
    //如果左上方的样值可用
    if (*pbNeighborFlags)
    {
#if O0043_BEST_EFFORT_DECODING
      Pel topLeftVal=piRoiTemp[0] << bitDepthDelta;
#else
      Pel topLeftVal=piRoiTemp[0];
#endif
      for (i=0; i<unitWidth; i++)
      {
        piIntraLineTemp[i] = topLeftVal;
      }
    }

    // Fill left & below-left samples (downwards)
    //填充左边和左下方的像素值
      
    //piRoiTemp指向重建的YUV图像当前PU位置的左边界,从左上方向下移动一行
    piRoiTemp += iPicStride;
    //移动piIntraLineTemp指针至左边界
    piIntraLineTemp--;
    //移动pbNeighborFlags指针至左边界
    pbNeighborFlags--;

    //从左往左下开始扫描遍历,检查可用标志,并对相邻像素进行填充
    for (j=0; j<iLeftUnits; j++)
    {
      if (*pbNeighborFlags)
      {
        for (i=0; i<unitHeight; i++)
        {
#if O0043_BEST_EFFORT_DECODING
          piIntraLineTemp[-i] = piRoiTemp[i*iPicStride] << bitDepthDelta;
#else
          piIntraLineTemp[-i] = piRoiTemp[i*iPicStride];
#endif
        }
      }
      //piRoiTemp指针移动到下一行,以unitHeight为单为(4个像素点)实际上移动了4行
      piRoiTemp += unitHeight*iPicStride;  
      //piIntraLineTemp指针下移
      piIntraLineTemp -= unitHeight;
      //pbNeighborFlags指针下移
      pbNeighborFlags--;
    }

    // Fill above & above-right samples (left-to-right) (each unit has "unitWidth" samples)填充上方和左上方的样本值,每一个单元的长度为unitWidth像素
    //piRoiTemp指针指向参考图像PU位置的上方
    piRoiTemp = piRoiOrigin - iPicStride;
    // offset line buffer by iNumUints2*unitHeight (for left/below-left) + unitWidth (for above-left)偏移line缓存器piIntraLineTemp,步长为(iLeftUnits * unitHeight) + unitWidth,使之指向上边界
    piIntraLineTemp = piIntraLine + (iLeftUnits * unitHeight) + unitWidth;
    //pbNeighborFlags指向上边界第一个单元位置
    pbNeighborFlags = bNeighborFlags + iLeftUnits + 1;
    //从左至有开始扫描遍历
    for (j=0; j<iAboveUnits; j++)
    {
      if (*pbNeighborFlags)
      {
        for (i=0; i<unitWidth; i++)
        {
#if O0043_BEST_EFFORT_DECODING
          piIntraLineTemp[i] = piRoiTemp[i] << bitDepthDelta;
#else
          piIntraLineTemp[i] = piRoiTemp[i];
#endif
        }
      }
      piRoiTemp += unitWidth;  //piRoiTemp指针右移
      piIntraLineTemp += unitWidth; //piIntraLineTemp指针右移
      pbNeighborFlags++;  //pbNeighborFlags指针右移
    }

    // Pad reference samples when necessary必要时补齐参考样本
    Int iCurrJnit = 0;
    //piIntraLineCur指向左下角,纵坐标最大的那个位置即扫描起点
    Pel  *piIntraLineCur   = piIntraLine;
    const UInt piIntraLineTopRowOffset = iLeftUnits * (unitHeight - unitWidth);
	
    //检查最下方参考单元的可用性,不可用
    if (!bNeighborFlags[0])
    {
      // very bottom unit of bottom-left; at least one unit will be valid.左下方至少有一个单元可用
      {
        Int   iNext = 1;
        //找到第一个可用点
        while (iNext < iTotalUnits && !bNeighborFlags[iNext])
        {
          iNext++;
        }
        Pel *piIntraLineNext = piIntraLine + ((iNext < iLeftUnits) ? (iNext * unitHeight) : (piIntraLineTopRowOffset + (iNext * unitWidth)));
        const Pel refSample = *piIntraLineNext;  
        //保存该可用的样值点
        // Pad unavailable samples with new value用新的样值更新不可用的样值点
        Int iNextOrTop = std::min<Int>(iNext, iLeftUnits);
        // fill left column填充左边列
        while (iCurrJnit < iNextOrTop)
        {
          for (i=0; i<unitHeight; i++)
          {
            piIntraLineCur[i] = refSample;
          }
          piIntraLineCur += unitHeight;
          iCurrJnit++;
        }
        // fill top row填充上边行
        while (iCurrJnit < iNext)
        {
          for (i=0; i<unitWidth; i++)
          {
            piIntraLineCur[i] = refSample;
          }
          piIntraLineCur += unitWidth;
          iCurrJnit++;
        }
      }
    }

    // pad all other reference samples.其他点不可用的情况,使用该点前一个可用的样值点进行更新
    while (iCurrJnit < iTotalUnits)
    {
      if (!bNeighborFlags[iCurrJnit]) // samples not available
      {
        {
          const Int numSamplesInCurrUnit = (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;
          const Pel refSample = *(piIntraLineCur-1);
          for (i=0; i<numSamplesInCurrUnit; i++)
          {
            piIntraLineCur[i] = refSample;
          }
          piIntraLineCur += numSamplesInCurrUnit;
          iCurrJnit++;
        }
      }
      else //当前点可用,继续检查下一个点
      {
        piIntraLineCur += (iCurrJnit >= iLeftUnits) ? unitWidth : unitHeight;
        iCurrJnit++;
      }
    }

    // Copy processed samples

    piIntraLineTemp = piIntraLine + uiHeight + unitWidth - 2;
    // top left, top and top right samples将最终结果拷贝到当前PU左上、上、右上参考位置
    for (i=0; i<uiWidth; i++)
    {
      piIntraTemp[i] = piIntraLineTemp[i];
    }
	//将最终结果拷贝到当前PU左、左下方参考位置
    piIntraLineTemp = piIntraLine + uiHeight - 1;
    for (i=1; i<uiHeight; i++)
    {
      piIntraTemp[i*uiWidth] = piIntraLineTemp[-i];
    }
  }
}

更多关于视频编码的知识和资源,更精致的文章排版,欢迎关注博主微信公众号,一起交流、学习、进步!!!
二、HEVC代码学习之帧内预测编码参考像素赋值函数fillReferenceSamples

相关标签: HM代码学习笔记