H.266/VVC代码学习:帧内预测之角度预测函数(predIntraAng、xPredIntraAng)

VTM7.0中,帧内预测的角度预测的入口函数为predIntraAng函数,该函数主要是用于进行传统的帧内预测(Planar、DC、角度预测),然后对Planar和DC模式使用PDPC(其余角度模式的PDPC在xPredIntraAng函数中进行)

void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu)
{
  const ComponentID    compID       = MAP_CHROMA( compId );
  const ChannelType    channelType  = toChannelType( compID );
  const int            iWidth       = piPred.width;
  const int            iHeight      = piPred.height;
#if JVET_P0641_REMOVE_2xN_CHROMA_INTRA
  CHECK(iWidth == 2, "Width of 2 is not supported");
#endif
#if JVET_P0059_CHROMA_BDPCM
  //获取预测模式
  const uint32_t       uiDirMode    = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : !isLuma(compId) && pu.cu->bdpcmModeChroma ? BDPCM_IDX : PU::getFinalIntraMode(pu, channelType);
#else
  const uint32_t       uiDirMode    = isLuma( compId ) && pu.cu->bdpcmMode ? BDPCM_IDX : PU::getFinalIntraMode( pu, channelType );
#endif

  CHECK( floorLog2(iWidth) < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );
  CHECK( floorLog2(iWidth) > 7, "Size not allowed" );

  const int srcStride  = m_refBufferStride[compID];
  const int srcHStride = 2;

  const CPelBuf & srcBuf = CPelBuf(getPredictorPtr(compID), srcStride, srcHStride);//参考像素
  const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));

  switch (uiDirMode)
  {
    case(PLANAR_IDX): xPredIntraPlanar(srcBuf, piPred); break;//Planar模式
    case(DC_IDX):     xPredIntraDc(srcBuf, piPred, channelType, false); break;//DC模式
#if JVET_P0059_CHROMA_BDPCM
    case(BDPCM_IDX):  xPredIntraBDPCM(srcBuf, piPred, isLuma(compID) ? pu.cu->bdpcmMode : pu.cu->bdpcmModeChroma, clpRng); break;
#else
    case(BDPCM_IDX):  xPredIntraBDPCM(srcBuf, piPred, pu.cu->bdpcmMode, clpRng); break;
#endif
    default:          xPredIntraAng(srcBuf, piPred, channelType, clpRng); break;
  }
  //PDPC,这里进对Planar和DC做,其余角度模式的PDPC在xPredIntraAng函数中进行
  if (m_ipaParam.applyPDPC)
  {
    PelBuf dstBuf = piPred;
    const int scale = ((floorLog2(iWidth) - 2 + floorLog2(iHeight) - 2 + 2) >> 2);
    CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");

    if (uiDirMode == PLANAR_IDX || uiDirMode == DC_IDX)//PLANAR和DC的PDPC
    {
      for (int y = 0; y < iHeight; y++)
      {
        const int wT   = 32 >> std::min(31, ((y << 1) >> scale)); //32是2的5次方。即超过5就会变为0,不再受上影响
        const Pel left = srcBuf.at(y + 1, 1);
        for (int x = 0; x < iWidth; x++)
        {
          const int wL    = 32 >> std::min(31, ((x << 1) >> scale));
          const Pel top   = srcBuf.at(x + 1, 0);
          const Pel val   = dstBuf.at(x, y);
          dstBuf.at(x, y) = val + ((wL * (left - val) + wT * (top - val) + 32) >> 6);
        }
      }
    }
  }
}

xPredIntraAng函数主要进行传统的角度预测,并对预测像素进行平滑滤波或者插值处理(和模式有关)。

将角度预测模式分为以下:

  • A 垂直或水平模式(HOR_IDX, VER_IDX)
  • B 对角线模式(2, DIA_IDX, VDIA_IDX)
  • C 其它

如果角度模式属于A,不进行滤波处理,直接将参考像素作为预测像素。

如果角度模式属于B,使用[1, 2, 1]滤波器对参考像素进行滤波,直接将滤波后的值作为预测像素不进行插值处理。

如果角度模式属于C,对落到非整数位置的参考像素进行插值处理(不再进行滤波)。

获得预测像素之后,在还需要对以下几种模式进行PDPC操作:

垂直模式(模式50),水平模式(模式18),左下角对角线模式和与其相邻的8个模式(模式2~10),右上角对角线模式和与其相邻的8个模式(模式58~66)。

/** Function for deriving the simplified angular intra predictions.
*
* 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.
*/
//NOTE: Bit-Limit - 25-bit source
// pSrc参考像素 pDsit预测像素
void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const ClpRng& clpRng)
{
  int width =int(pDst.width);
  int height=int(pDst.height);

  const bool bIsModeVer     = m_ipaParam.isModeVer;//predMode >= DIA_IDX
  const int  multiRefIdx    = m_ipaParam.multiRefIndex;//多参考行索引
  const int  intraPredAngle = m_ipaParam.intraPredAngle;//偏移值
  const int  invAngle       = m_ipaParam.invAngle;

  Pel* refMain;
  Pel* refSide;

  Pel  refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//上侧参考像素
  Pel  refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];//左侧参考像素

  // Initialize the Main and Left reference array.
  // 初始化参考像素
  // 偏移值offset小于0,即模式为19~49
  // 对于垂直类模式(34~49),需要将左侧参考行映射到上方参考行
  // 对于水平类模式(18~33),需要将上方参考行映射到左侧参考行
  if (intraPredAngle < 0)
  {
    for (int x = 0; x <= width + 1 + multiRefIdx; x++)
    {
      refAbove[x + height] = pSrc.at(x, 0);
    }
    for (int y = 0; y <= height + 1 + multiRefIdx; y++)
    {
      refLeft[y + width] = pSrc.at(y, 1);
    }
    // 如果是垂直类模式(34~49),则主要侧为正上方参考行,次要侧为正左侧参考行
    // 如果是水平类模式(19~33),则主要侧为正左侧参考行,次要侧为正上方参考行
    refMain = bIsModeVer ? refAbove + height : refLeft + width;//主要侧
    refSide = bIsModeVer ? refLeft + width : refAbove + height;//次要侧

    // Extend the Main reference to the left. 
    // 将次要侧参考行投影到主要侧参考行
    int sizeSide = bIsModeVer ? height : width;
    for (int k = -sizeSide; k <= -1; k++)
    {
      refMain[k] = refSide[std::min((-k * invAngle + 256) >> 9, sizeSide)];
    }
  }
  else// 偏移值大于等于0,只需要用到上方参考像素或者左侧参考像素
  {
    for (int x = 0; x <= m_topRefLength + multiRefIdx; x++)
    {
      refAbove[x] = pSrc.at(x, 0);
    }
    for (int y = 0; y <= m_leftRefLength + multiRefIdx; y++)
    {
      refLeft[y] = pSrc.at(y, 1);
    }
    // 垂直类模式,主参考行为上方参考行
    // 水平类模式,主参考行为左侧参考行
    refMain = bIsModeVer ? refAbove : refLeft;
    refSide = bIsModeVer ? refLeft : refAbove;

    // Extend main reference to right using replication
    // 使用复制将主参考行向右扩展,即填充Segment A和Segment F
    const int log2Ratio = floorLog2(width) - floorLog2(height);
    const int s         = std::max<int>(0, bIsModeVer ? log2Ratio : -log2Ratio);
    const int maxIndex  = (multiRefIdx << s) + 2;
    const int refLength = bIsModeVer ? m_topRefLength : m_leftRefLength;
    const Pel val       = refMain[refLength + multiRefIdx];
    for (int z = 1; z <= maxIndex; z++)
    {
      refMain[refLength + multiRefIdx + z] = val;
    }
  }

  // swap width/height if we are doing a horizontal mode:
  // 如果是水平类模式则交换width和height
  if (!bIsModeVer)
  {
    std::swap(width, height);
  }
  Pel       tempArray[MAX_CU_SIZE * MAX_CU_SIZE];
  const int dstStride = bIsModeVer ? pDst.stride : width;
  Pel *     pDstBuf   = bIsModeVer ? pDst.buf : tempArray;

  // compensate for line offset in reference line buffers
  // 补充多参考行中的偏移
  refMain += multiRefIdx;
  refSide += multiRefIdx;

  Pel *pDsty = pDstBuf;

  if( intraPredAngle == 0 )  // pure vertical or pure horizontal 水平模式和垂直模式(18和50)
  {
    for( int y = 0; y < height; y++ )
    {
      for( int x = 0; x < width; x++ )
      {
        pDsty[x] = refMain[x + 1];//直接投影
      }
      //使用PDPC技术
      if (m_ipaParam.applyPDPC)
      {
        const int scale   = (floorLog2(width) + floorLog2(height) - 2) >> 2;
        const Pel topLeft = refMain[0];
        const Pel left    = refSide[1 + y];
        for (int x = 0; x < std::min(3 << scale, width); x++)
        {
          const int wL  = 32 >> (2 * x >> scale);
          const Pel val = pDsty[x];
          pDsty[x]      = ClipPel(val + ((wL * (left - topLeft) + 32) >> 6), clpRng);
        }
      }

      pDsty += dstStride;
    }
  }
  else//对于其余模式
  {
    for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride)
    {
      const int deltaInt   = deltaPos >> 5;
      const int deltaFract = deltaPos & 31;
      //对于除水平、垂直、模式2、34、66之外的模式需进行使用高斯插值滤波器进行插值处理
      if ( !isIntegerSlope( abs(intraPredAngle) ) )
      {
        if( isLuma(channelType) )//亮度块
        {
          const bool useCubicFilter = !m_ipaParam.interpolationFlag;

#if JVET_P0599_INTRA_SMOOTHING_INTERP_FILT
          const TFilterCoeff        intraSmoothingFilter[4] = {TFilterCoeff(16 - (deltaFract >> 1)), TFilterCoeff(32 - (deltaFract >> 1)), TFilterCoeff(16 + (deltaFract >> 1)), TFilterCoeff(deltaFract >> 1)};
          const TFilterCoeff* const f                       = (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : intraSmoothingFilter;
#else //!JVET_P0599_INTRA_SMOOTHING_INTERP_FILT
          const TFilterCoeff *const f =
            (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : g_intraGaussFilter[deltaFract];
#endif //JVET_P0599_INTRA_SMOOTHING_INTERP_FILT

          for (int x = 0; x < width; x++)
          {
            Pel p[4];

            p[0] = refMain[deltaInt + x];
            p[1] = refMain[deltaInt + x + 1];
            p[2] = refMain[deltaInt + x + 2];
            p[3] = refMain[deltaInt + x + 3];

            Pel val = (f[0] * p[0] + f[1] * p[1] + f[2] * p[2] + f[3] * p[3] + 32) >> 6;

            pDsty[x] = ClipPel(val, clpRng);   // always clip even though not always needed
          }
        }
        else //色度块
        {
          // Do linear filtering
          // 线性滤波
          for (int x = 0; x < width; x++)
          {
            Pel p[2];

            p[0] = refMain[deltaInt + x + 1];
            p[1] = refMain[deltaInt + x + 2];

            pDsty[x] = p[0] + ((deltaFract * (p[1] - p[0]) + 16) >> 5);
          }
        }
      }//if ( !isIntegerSlope( abs(intraPredAngle) ) )
      else
      {
        // 对于模式2, DIA_IDX, VDIA_IDX,直接将滤波后的参考像素作为预测像素
        // Just copy the integer samples
        for( int x = 0; x < width; x++ )
        {
          pDsty[x] = refMain[x + deltaInt + 1];
        }
      }
      if (m_ipaParam.applyPDPC)//使用PDPC技术
      {
        const int scale       = m_ipaParam.angularScale;
        int       invAngleSum = 256;

        for (int x = 0; x < std::min(3 << scale, width); x++)
        {
          invAngleSum += invAngle;

          int wL   = 32 >> (2 * x >> scale);
          Pel left = refSide[y + (invAngleSum >> 9) + 1];
          pDsty[x] = pDsty[x] + ((wL * (left - pDsty[x]) + 32) >> 6);
        }
      }
    }
  }

  // Flip the block if this is the horizontal mode
  // 如果这是水平模式,翻转块
  if( !bIsModeVer )
  {
    for( int y = 0; y < height; y++ )
    {
      for( int x = 0; x < width; x++ )
      {
        pDst.at( y, x ) = pDstBuf[x];
      }
      pDstBuf += dstStride;
    }
  }
}
发布了95 篇原创文章 · 获赞 138 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/104521096
今日推荐