VVC帧内预测参考像素获取及滤波--帧内编码学习(三)

 在进行角度预测之前,需要先得到参考像素以及参考像素滤波,完成这个功能的函数就是:

initIntraPatternChType( cu, pu.Y(), IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu ) );//useFilteredIntraRefSamples用于判断是否需要滤波

 其中

IntraPrediction::useFilteredIntraRefSamples( COMPONENT_Y, pu, false, pu )

用于判断是否满足参考像素滤波的条件。 

initIntraPatternChType中参考像素的获取由

xFillReferenceSamples( cs.picture->getRecoBuf( area ), refBufUnfiltered, area, cu );//得到未滤波的参考像素

完成。 参考像素滤波由

xFilterReferenceSamples( refBufUnfiltered, refBufFiltered, area, *cs.sps
      , cu.firstPU->multiRefIdx
    );//参考像素滤波

完成。

1、参考像素获取,如下图是JVET-M1002中MRL的示意图。

    1)首先按照单元的大小(通常为4,ISP下可以小于4),判断Segment D、E和B中各个部分的参考单元是否可用

    2)均不可用,填充DC值。

    3)可用,按照顺序填充即可。

   4)部分可用。先将可用的单元进行填充,再以近邻像素填充不可用参考像素。

   5)SegmentA和F的参考像素直接由B和E最近像素填充

 

 VVC帧内预测参考像素获取及滤波--帧内编码学习(三)_第1张图片

xFillReferenceSamples函数具体注释如下

void IntraPrediction::xFillReferenceSamples( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu )
{
  const ChannelType      chType = toChannelType( area.compID );
  const CodingStructure &cs     = *cu.cs;
  const SPS             &sps    = *cs.sps;
  const PreCalcValues   &pcv    = *cs.pcv;
  const int multiRefIdx         = (area.compID == COMPONENT_Y) ? cu.firstPU->multiRefIdx : 0;//MRL索引
  const int  tuWidth            = area.width;//
  const int  tuHeight           = area.height;
  const int  predSize           = m_topRefLength;
  const int  predHSize          = m_leftRefLength;
#if JVET_M0102_INTRA_SUBPARTITIONS
  const int  cuWidth            = cu.blocks[area.compID].width;
  const int  cuHeight           = cu.blocks[area.compID].height;
  const int  whRatio            = cu.ispMode && isLuma(area.compID) ? std::max(1, cuWidth / cuHeight) : std::max(1, tuWidth / tuHeight);
  const int  hwRatio            = cu.ispMode && isLuma(area.compID) ? std::max(1, cuHeight / cuWidth) : std::max(1, tuHeight / tuWidth);
#else
  int whRatio                   = std::max(1, tuWidth / tuHeight);
  int hwRatio                   = std::max(1, tuHeight / tuWidth);
#endif
  const int  predStride         = predSize + 1 + (whRatio + 1) * multiRefIdx;
  const bool noShift            = pcv.noChroma2x2 && area.width == 4; // don't shift on the lowest level (chroma not-split)
#if JVET_M0102_INTRA_SUBPARTITIONS
  const int  unitWidth          = tuWidth  <= 2 && cu.ispMode && isLuma(area.compID) ? tuWidth  : pcv.minCUWidth  >> (noShift ? 0 : getComponentScaleX(area.compID, sps.getChromaFormatIdc()));
  const int  unitHeight         = tuHeight <= 2 && cu.ispMode && isLuma(area.compID) ? tuHeight : pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY(area.compID, sps.getChromaFormatIdc()));
#else
  const int  unitWidth          = pcv.minCUWidth  >> (noShift ? 0 : getComponentScaleX( area.compID, sps.getChromaFormatIdc() ));
  const int  unitHeight         = pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY( area.compID, sps.getChromaFormatIdc() ));
#endif

  const int  totalAboveUnits    = (predSize + (unitWidth - 1)) / unitWidth;//上边所需参考单元数
  const int  totalLeftUnits     = (predHSize + (unitHeight - 1)) / unitHeight;//左边所需参考单元
  const int  totalUnits         = totalAboveUnits + totalLeftUnits + 1; //+1 for top-left
  const int  numAboveUnits      = std::max( tuWidth / unitWidth, 1 );//tu上边单元数
  const int  numLeftUnits       = std::max( tuHeight / unitHeight, 1 );//tu左边单元数
  const int  numAboveRightUnits = totalAboveUnits - numAboveUnits;//tu右上单元数
  const int  numLeftBelowUnits  = totalLeftUnits - numLeftUnits;//tu左下单元数

  CHECK( numAboveUnits <= 0 || numLeftUnits <= 0 || numAboveRightUnits <= 0 || numLeftBelowUnits <= 0, "Size not supported" );

  // ----- Step 1: analyze neighborhood -----
  //相邻块是否可用
  const Position posLT          = area;//TU左上坐标
  const Position posRT          = area.topRight();//TU右上坐标
  const Position posLB          = area.bottomLeft();//TU左下坐标

  bool  neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];//相邻单元可用标记,通常大小为4*4,isp模式下可能为1或2
  int   numIntraNeighbor = 0;//相邻单元可用数量

  memset( neighborFlags, 0, totalUnits );//初始化为均不可用

  neighborFlags[totalLeftUnits] = isAboveLeftAvailable( cu, chType, posLT );//左上是否可用
  numIntraNeighbor += neighborFlags[totalLeftUnits] ? 1 : 0;
  numIntraNeighbor += isAboveAvailable     ( cu, chType, posLT, numAboveUnits,      unitWidth,  (neighborFlags + totalLeftUnits + 1) );//上
  numIntraNeighbor += isAboveRightAvailable( cu, chType, posRT, numAboveRightUnits, unitWidth,  (neighborFlags + totalLeftUnits + 1 + numAboveUnits) );//右上
  numIntraNeighbor += isLeftAvailable      ( cu, chType, posLT, numLeftUnits,       unitHeight, (neighborFlags + totalLeftUnits - 1) );//左
  numIntraNeighbor += isBelowLeftAvailable ( cu, chType, posLB, numLeftBelowUnits,  unitHeight, (neighborFlags + totalLeftUnits - 1 - numLeftUnits) );//左下

  // ----- Step 2: fill reference samples (depending on neighborhood) -----
  //参考样本填充
  CHECK((predHSize + 1) * predStride > m_iYuvExtSize, "Reference sample area not supported");

  const Pel*  srcBuf    = recoBuf.buf;
  const int   srcStride = recoBuf.stride;
        Pel*  ptrDst    = refBufUnfiltered;
  const Pel*  ptrSrc;
  const Pel   valueDC   = 1 << (sps.getBitDepth( chType ) - 1);


  if( numIntraNeighbor == 0 )//无可用参考
  {
    // Fill border with DC value
    for (int j = 0; j <= predSize + multiRefIdx; j++) { ptrDst[j] = valueDC; }//用DC值填充上参考
    for (int i = 1; i <= predHSize + multiRefIdx; i++) { ptrDst[i*predStride] = valueDC; }//用DC值填充左参考
  }
  else if( numIntraNeighbor == totalUnits )//所以参考均可
  {
    // Fill top-left border and top and top right with rec. samples
    ptrSrc = srcBuf - (1 + multiRefIdx) * srcStride - (1 + multiRefIdx);//指向左上角
    for (int j = 0; j <= predSize + multiRefIdx; j++) { ptrDst[j] = ptrSrc[j]; }//填充上参考
    ptrSrc = srcBuf - multiRefIdx * srcStride - (1 + multiRefIdx);//指向左
    for (int i = 1; i <= predHSize + multiRefIdx; i++) { ptrDst[i*predStride] = *(ptrSrc); ptrSrc += srcStride; }//填充左参考
  }
  else // reference samples are partially available//部分可用
  {
    // Fill top-left sample(s) if available
    ptrSrc = srcBuf - (1 + multiRefIdx) * srcStride - (1 + multiRefIdx);
    ptrDst = refBufUnfiltered;
    if (neighborFlags[totalLeftUnits])//左上可用
    {
      ptrDst[0] = ptrSrc[0];//最左上
      for (int i = 1; i <= multiRefIdx; i++)
      {
        ptrDst[i] = ptrSrc[i];//MRL左上-行
        ptrDst[i*predStride] = ptrSrc[i*srcStride];//MRL左上-列
      }
    }

    // Fill left & below-left samples if available (downwards)//左和左下
    ptrSrc += (1 + multiRefIdx) * srcStride;//指向原始左
    ptrDst += (1 + multiRefIdx) * predStride;//指向参考左
    for (int unitIdx = totalLeftUnits - 1; unitIdx > 0; unitIdx--)//逐个扫描左边参考单元--从上到下
    {
      if (neighborFlags[unitIdx])//该参考可用
      {
        for (int i = 0; i < unitHeight; i++)
        {
          ptrDst[i*predStride] = ptrSrc[i*srcStride];//填充对应参考
        }
      }
      ptrSrc += unitHeight * srcStride;//指向下一单元
      ptrDst += unitHeight * predStride;
    }
    // Fill last below-left sample(s)
    if (neighborFlags[0])//最左下单元
    {
      int lastSample = (predHSize % unitHeight == 0) ? unitHeight : predHSize % unitHeight;
      for (int i = 0; i < lastSample; i++)
      {
        ptrDst[i*predStride] = ptrSrc[i*srcStride];
      }
    }

    // Fill above & above-right samples if available (left-to-right)//上和右上
    ptrSrc = srcBuf - srcStride * (1 + multiRefIdx);//指向原始上
    ptrDst = refBufUnfiltered + 1 + multiRefIdx;//指向目的上
    for (int unitIdx = totalLeftUnits + 1; unitIdx < totalUnits - 1; unitIdx++)//从左往右扫描可用单元
    {
      if (neighborFlags[unitIdx])//
      {
        for (int j = 0; j < unitWidth; j++)
        {
          ptrDst[j] = ptrSrc[j];
        }
      }
      ptrSrc += unitWidth;
      ptrDst += unitWidth;
    }
    // Fill last above-right sample(s)
    if (neighborFlags[totalUnits - 1])//最右上
    {
      int lastSample = (predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth;
      for (int j = 0; j < lastSample; j++)
      {
        ptrDst[j] = ptrSrc[j];
      }
    }

    // pad from first available down to the last below-left
    ptrDst = refBufUnfiltered;
    int lastAvailUnit = 0;//上一个可用单元
    if (!neighborFlags[0])//最左下不可用
    {
      int firstAvailUnit = 1;
      while (firstAvailUnit < totalUnits && !neighborFlags[firstAvailUnit])//寻找第一个可用单元
      {
        firstAvailUnit++;
      }

      // first available sample
      int firstAvailRow = 0;
      int firstAvailCol = 0;
      if (firstAvailUnit < totalLeftUnits)//第一个可用单元在左边
      {
        firstAvailRow = (totalLeftUnits - firstAvailUnit) * unitHeight + multiRefIdx;//算出第一个可用单元行
      }
      else if (firstAvailUnit == totalLeftUnits)//第一个可用单元为左上
      {
        firstAvailRow = multiRefIdx;
      }
      else//否则在上边
      {
        firstAvailCol = (firstAvailUnit - totalLeftUnits - 1) * unitWidth + 1 + multiRefIdx;//算出第一个可用单元列
      }
      const Pel firstAvailSample = ptrDst[firstAvailCol + firstAvailRow * predStride];//第一个可用下或左样本

      // last sample below-left (n.a.)
      int lastRow = predHSize + multiRefIdx;//最后一行

      // fill left column
      for (int i = lastRow; i > firstAvailRow; i--)//
      {
        ptrDst[i*predStride] = firstAvailSample;//填充不存在的左参考列
      }
      // fill top row
      if (firstAvailCol > 0)//列大于0说明前面均没有
      {
        for (int j = 0; j < firstAvailCol; j++)
        {
          ptrDst[j] = firstAvailSample;//填充上参考行
        }
      }
      lastAvailUnit = firstAvailUnit;
    }

    // pad all other reference samples.
    int currUnit = lastAvailUnit + 1;
    while (currUnit < totalUnits)//遍历每一个单元
    {
      if (!neighborFlags[currUnit]) // samples not available//当前单元不可用
      {
        // last available sample//上一次可使用单元右或上样本
        int lastAvailRow = 0;
        int lastAvailCol = 0;
        if (lastAvailUnit < totalLeftUnits)//左边填充未结束
        {
          lastAvailRow = (totalLeftUnits - lastAvailUnit - 1) * unitHeight + multiRefIdx + 1;
        }
        else if (lastAvailUnit == totalLeftUnits)
        {
          lastAvailCol = multiRefIdx;
        }
        else
        {
          lastAvailCol = (lastAvailUnit - totalLeftUnits) * unitWidth + multiRefIdx;
        }
        const Pel lastAvailSample = ptrDst[lastAvailCol + lastAvailRow * predStride];

        // fill current unit with last available sample
        if (currUnit < totalLeftUnits)//填充当前单元--左侧
        {
          for (int i = lastAvailRow - 1; i >= lastAvailRow - unitHeight; i--)
          {
            ptrDst[i*predStride] = lastAvailSample;
          }
        }
        else if (currUnit == totalLeftUnits)//填充当前单元--左上角
        {
          for (int i = 1; i < multiRefIdx + 1; i++)
          {
            ptrDst[i*predStride] = lastAvailSample;//列
          }
          for (int j = 0; j < multiRefIdx + 1; j++)
          {
            ptrDst[j] = lastAvailSample;//行
          }
        }
        else//填充当前单元--右侧
        {
          int numSamplesInUnit = (currUnit == totalUnits - 1) ? ((predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth) : unitWidth;//最后一个单元防止溢出
          for (int j = lastAvailCol + 1; j <= lastAvailCol + numSamplesInUnit; j++)
          {
            ptrDst[j] = lastAvailSample;
          }
        }
      }
      lastAvailUnit = currUnit;
      currUnit++;
    }
}
  // padding of extended samples above right with the last sample
  int lastSample = multiRefIdx + predSize;
  for (int j = 1; j <= whRatio * multiRefIdx; j++) { ptrDst[lastSample + j] = ptrDst[lastSample]; }//MRL上参考扩展
  // padding of extended samples below left with the last sample
  lastSample = multiRefIdx + predHSize;
  for (int i = 1; i <= hwRatio * multiRefIdx; i++) { ptrDst[(lastSample + i)*predStride] = ptrDst[lastSample*predStride]; }//MRL左参考扩展
}

2、参考像素滤波

相当于参考像素从左下到右上排成一列,采用[1 2 1]的平滑滤波系数对参考像素进行滤波,最边缘不进行处理。

void IntraPrediction::xFilterReferenceSamples( const Pel* refBufUnfiltered, Pel* refBufFiltered, const CompArea &area, const SPS &sps
  , int multiRefIdx
)
{
  if (area.compID != COMPONENT_Y)
  {
    multiRefIdx = 0;
  }
  int whRatio          = std::max(1, int(area.width  / area.height));
  int hwRatio          = std::max(1, int(area.height / area.width));
  const int  predSize  = m_topRefLength  + (whRatio + 1) * multiRefIdx;
  const int  predHSize = m_leftRefLength + (hwRatio + 1) * multiRefIdx;
  const int  predStride = predSize + 1;


#if HEVC_USE_INTRA_SMOOTHING_T32 || HEVC_USE_INTRA_SMOOTHING_T64
  // Strong intra smoothing
  ChannelType chType = toChannelType( area.compID );
  if( sps.getUseStrongIntraSmoothing() && isLuma( chType ) )
  {
    const Pel bottomLeft = refBufUnfiltered[predStride * predHSize];
    const Pel topLeft    = refBufUnfiltered[0];
    const Pel topRight   = refBufUnfiltered[predSize];

    const int  threshold     = 1 << (sps.getBitDepth( chType ) - 5);
    const bool bilinearLeft  = abs( (bottomLeft + topLeft)  - (2 * refBufUnfiltered[predStride * tuHeight]) ) < threshold; //difference between the
    const bool bilinearAbove = abs( (topLeft    + topRight) - (2 * refBufUnfiltered[             tuWidth ]) ) < threshold; //ends and the middle

    if( tuWidth >= 32 && tuHeight >= 32 && bilinearLeft && bilinearAbove )
#if !HEVC_USE_INTRA_SMOOTHING_T32
    if( tuWidth > 32 && tuHeight > 32 )
#endif
#if !HEVC_USE_INTRA_SMOOTHING_T64
    if( tuWidth < 64 && tuHeight < 64 )
#endif
    {
      Pel *piDestPtr = refBufFiltered + (predStride * predHSize); // bottom left

      // apply strong intra smoothing
      for (int i = 0; i < predHSize; i++, piDestPtr -= predStride) //left column (bottom to top)
      {
        *piDestPtr = (((predHSize - i) * bottomLeft) + (i * topLeft) + predHSize / 2) / predHSize;
      }
      for( uint32_t i = 0; i <= predSize; i++, piDestPtr++ )            //full top row (left-to-right)
      {
        *piDestPtr = (((predSize - i) * topLeft) + (i * topRight) + predSize / 2) / predSize;
      }

      return;
    }
  }
#endif

  // Regular reference sample filter
  const Pel *piSrcPtr  = refBufUnfiltered + (predStride * predHSize); // bottom left
        Pel *piDestPtr = refBufFiltered   + (predStride * predHSize); // bottom left

  // bottom left (not filtered)
  *piDestPtr = *piSrcPtr;//左下不滤波
  piDestPtr -= predStride;//目标指针上移
  piSrcPtr  -= predStride;//原始指针上移
  //left column (bottom to top)
  for( int i = 1; i < predHSize; i++, piDestPtr -= predStride, piSrcPtr -= predStride)
  {
    *piDestPtr = (piSrcPtr[predStride] + 2 * piSrcPtr[0] + piSrcPtr[-predStride] + 2) >> 2;//[1 2 1]平滑滤波
  }
  //top-left
  *piDestPtr = (piSrcPtr[predStride] + 2 * piSrcPtr[0] + piSrcPtr[1] + 2) >> 2;//左上角参考像素滤波
  piDestPtr++;//指向上参考,从左到右第一个像素
  piSrcPtr++;
  //top row (left-to-right)
  for( uint32_t i=1; i < predSize; i++, piDestPtr++, piSrcPtr++ )
  {
    *piDestPtr = (piSrcPtr[1] + 2 * piSrcPtr[0] + piSrcPtr[-1] + 2) >> 2;//上边参考像素滤波
  }
  // top right (not filtered)
  *piDestPtr=*piSrcPtr;
}

 

 

你可能感兴趣的:(VVC学习笔记)