VVC学习之五:帧内预测——67个模式预测信号生成 predIntraAng()

博客介绍帧内预测相关内容,帧内有67种预测模式,主要功能是调用相关函数生成预测信号,并对满足PDPC条件的模式进行矫正。生成预测信号分为DC、Planar和角度预测,还分别介绍了各模式下预测信号生成的调用情况及代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

帧内共有67种预测模式,包括 65种角度+DC+Planar 模式,VTM中调用predIntraAng()生成相应的预测信号。predIntraAng()主要功能是调用相关预测函数,生成67中模式的预测信号,并对满足 PDPC条件 的预测模式进行预测信号矫正。PDPC的计算公式如下:

p r e d ( x , y ) = [ w L × R − 1 , y + w T × R x , − 1 – w T L × R − 1 , − 1 + ( 64 – w L – w T + w T L ) × p r e d ( x , y ) + 32 ] > > 6 pred(x,y) = [ wL×R_{-1,y} + wT×R_{x,-1} – wTL ×R_{-1,-1} + (64 – wL – wT+wTL) × pred(x,y) + 32 ]>>6 pred(x,y)=[wL×R1,y+wT×Rx,1wTL×R1,1+(64wLwT+wTL)×pred(x,y)+32]>>6

其中 p r e d ( x , y ) pred(x,y) pred(x,y) 为最终预测值。 R x , − 1 R_{x,-1} Rx,1 R − 1 , y R_{-1,y} R1,y当前像素(x,y)上方和左侧的相邻参考像素, R − 1 , − 1 R_{-1,-1} R1,1表示当前块左上角的参考像素。PDPC的权重依赖于帧内预测模式,具体如下

预测模式wTwLwTL
右上对角线模式16 >> ( ( y’<<1 ) >> shift)16 >> ( ( x’<<1 ) >> shift)0
下左对角线模式16 >> ( ( y’<<1 ) >> shift )16 >> ( ( x’<<1 ) >> shift )0
右上对角线模式相邻角度32 >> ( ( y’<<1 ) >> shift )00
下左对角线模式相邻角度032 >> ( ( x’<<1 ) >> shift )0

其中生成预测信号时,主要分为DC、Planar和角度预测信号生成。其中角度预测需要注意多行预测以及是否为广角预测。预测信号生成调用情况如下:

  • DC模式:xPredIntraDc()
  • Planar模式:xPredIntraPlanar()
  • 其他角度模式:xPredIntraAng()

predIntraAng()

void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu, const bool useFilteredPredSamples )
{
  const ComponentID    compID       = MAP_CHROMA( compId );
  const ChannelType    channelType  = toChannelType( compID );
  const int            iWidth       = piPred.width;
  const int            iHeight      = piPred.height;
#if JVET_M0102_INTRA_SUBPARTITIONS
  const Size           cuSize       = Size( pu.cu->blocks[compId].width, pu.cu->blocks[compId].height );
#endif
  const uint32_t           uiDirMode    = PU::getFinalIntraMode( pu, channelType );  //如果是色度,进行预测模式转换:0~66


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

  const int  multiRefIdx = (compID == COMPONENT_Y) ? pu.multiRefIdx : 0;  //MRL参考行索引

  const bool useISP = pu.cu->ispMode && isLuma( compID );  //是否ISP预测
  const int whRatio = useISP ? std::max( unsigned( 1 ), cuSize.width / cuSize.height ) : std::max( 1, iWidth / iHeight );
  const int hwRatio = useISP ? std::max( unsigned( 1 ), cuSize.height / cuSize.width ) : std::max( 1, iHeight / iWidth );

  const int  srcStride  = m_topRefLength  + 1 + (whRatio + 1) * multiRefIdx;
  const int  srcHStride = m_leftRefLength + 1 + (hwRatio + 1) * multiRefIdx;

  Pel *ptrSrc = getPredictorPtr(compID, useFilteredPredSamples);  //帧内参考样本首地址
  const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));

  switch (uiDirMode)  // 生成预测信号
  {
    case(PLANAR_IDX): xPredIntraPlanar(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, *pu.cs->sps); break;
    case(DC_IDX):     xPredIntraDc(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, channelType, false); break;
    case(2):
    case(DIA_IDX):
    case(VDIA_IDX):
      if (getWideAngle(useISP ? cuSize.width : iWidth, useISP ? cuSize.height : iHeight, uiDirMode) == static_cast<int>(uiDirMode)) // check if uiDirMode is not wide-angle
      {
        xPredIntraAng(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, channelType, uiDirMode, clpRng, *pu.cs->sps, multiRefIdx, useFilteredPredSamples, useISP, cuSize );
        break;
      }
    default:          xPredIntraAng(CPelBuf(getPredictorPtr(compID, false), srcStride, srcHStride), piPred, channelType, uiDirMode, clpRng, *pu.cs->sps, multiRefIdx, useFilteredPredSamples, useISP, cuSize); break;
  }

  bool pdpcCondition = (uiDirMode == PLANAR_IDX || uiDirMode == DC_IDX || uiDirMode == HOR_IDX || uiDirMode == VER_IDX);
#if JVET_M0102_INTRA_SUBPARTITIONS
  if( pdpcCondition && multiRefIdx == 0 && !useISP ) // 满足PDPC角度模式,且参考行不是扩展行,同时也不是ISP预测时,进行PDPC矫正
#else
  if (pdpcCondition && multiRefIdx == 0)
#endif
  {
    const CPelBuf srcBuf = CPelBuf(ptrSrc, srcStride, srcStride);
    PelBuf dstBuf = piPred;
    const int scale = ((g_aucLog2[iWidth] - 2 + g_aucLog2[iHeight] - 2 + 2) >> 2);
    CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");

    if (uiDirMode == PLANAR_IDX)
    {
      for (int y = 0; y < iHeight; y++)
      {
        int wT = 32 >> std::min(31, ((y << 1) >> scale));
        const Pel left = srcBuf.at(0, y + 1);
        for (int x = 0; x < iWidth; x++)
        {
          const Pel top = srcBuf.at(x + 1, 0);
          int wL = 32 >> std::min(31, ((x << 1) >> scale));
          dstBuf.at(x, y) = ClipPel((wL * left + wT * top + (64 - wL - wT) * dstBuf.at(x, y) + 32) >> 6, clpRng);
        }
      }
    }
    else if (uiDirMode == DC_IDX)
    {
      const Pel topLeft = srcBuf.at(0, 0);
      for (int y = 0; y < iHeight; y++)
      {
        int wT = 32 >> std::min(31, ((y << 1) >> scale));
        const Pel left = srcBuf.at(0, y + 1);
        for (int x = 0; x < iWidth; x++)
        {
          const Pel top = srcBuf.at(x + 1, 0);
          int wL = 32 >> std::min(31, ((x << 1) >> scale));
          int wTL = (wL >> 4) + (wT >> 4);
          dstBuf.at(x, y) = ClipPel((wL * left + wT * top - wTL * topLeft + (64 - wL - wT + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
        }
      }
    }
    else if (uiDirMode == HOR_IDX)
    {
      const Pel topLeft = srcBuf.at(0, 0);
      for (int y = 0; y < iHeight; y++)
      {
        int wT = 32 >> std::min(31, ((y << 1) >> scale));
        for (int x = 0; x < iWidth; x++)
        {
          const Pel top = srcBuf.at(x + 1, 0);
          int wTL = wT;
          dstBuf.at(x, y) = ClipPel((wT * top - wTL * topLeft + (64 - wT + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
        }
      }
    }
    else if (uiDirMode == VER_IDX)
    {
      const Pel topLeft = srcBuf.at(0, 0);
      for (int y = 0; y < iHeight; y++)
      {
        const Pel left = srcBuf.at(0, y + 1);
        for (int x = 0; x < iWidth; x++)
        {
          int wL = 32 >> std::min(31, ((x << 1) >> scale));
          int wTL = wL;
          dstBuf.at(x, y) = ClipPel((wL * left - wTL * topLeft + (64 - wL + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
        }
      }
    }
  }
}

xPredIntraDc()

其中DC模式的预测信号调用xPredIntraDc(),代码如下:

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

#if HEVC_USE_DC_PREDFILTERING // 默认关闭
  if( enableBoundaryFilter )
  {
    xDCPredFiltering( pSrc, pDst, channelType );
  }
#endif
}

DC值生成,需要注意对于非正方形预测单元,只用一侧的参考像素计算DC(正上方或者正左侧),代码如下

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); // 用来计算DC值,所用参考样点个数
  const auto divShift  = g_aucLog2[denom];
  const auto divOffset = (denom >> 1);

  if ( width >= height ) // 宽大于高,只用上方参考
  {
    for( idx = 0; idx < width; idx++ )
    {
      sum += pSrc.at( 1 + idx, 0 );
    }
  }
  if ( width <= height )// 宽小于高,只用左侧参考
  {
    for( idx = 0; idx < height; idx++ )
    {
      sum += pSrc.at( 0, 1 + idx );
    }
  }

  dcVal = (sum + divOffset) >> divShift;
  return dcVal;
}

xPredIntraPlanar()

Planar 模式预测信号,Planar图像渐变预测,使用右上填充右边一列参考像素,使用左下填充下一列参考像素,然后四个像素和的平均作为预测值。代码如下:

/**
* pSrc : 参考像素缓存区
* pDst : 重建像素缓存区
**/

void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst, const SPS& sps )
{
  const uint32_t width  = pDst.width;  //重建宽
  const uint32_t height = pDst.height; //重建高
#if JVET_M0102_INTRA_SUBPARTITIONS
  const uint32_t log2W  = g_aucLog2[width  < 2 ? 2 : width];
  const uint32_t log2H  = g_aucLog2[height < 2 ? 2 : height];
#else
  const uint32_t log2W  = g_aucLog2[ width ];
  const uint32_t log2H  = g_aucLog2[ height ];
#endif

  // 左列、上行、右列、下行
  int leftColumn[MAX_CU_SIZE + 1], topRow[MAX_CU_SIZE + 1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
#if JVET_M0102_INTRA_SUBPARTITIONS
  const uint32_t offset = 1 << (log2W + log2H);
#else
  const uint32_t offset = width * height;
#endif

  // Get left and above reference column and row
  for( int k = 0; k < width + 1; k++ ) //上边界参考像素,长度width+1:p[1][0],p[2][0],p[3][0]...p[width+1][0]
  {
    topRow[k] = pSrc.at( k + 1, 0 );
  }

  for( int k = 0; k < height + 1; k++ )//左边界参考像素,长度height+1,p[0][1],p[0][2],p[0][3]...p[0][height+1]
  {
    leftColumn[k] = pSrc.at( 0, k + 1 );
  }

  /**
  * Planar模式横向的预测值为 pred_h(x,y) = (width-x)*leftcolumn(y) + x*topRight
  * Planar模式宗向的预测值为 pred_v(x,y) = (height-y)*topRow(x) + y*bottomLeft
  * 最终预测值为:pred(x,y) = (pred_h + pred_v + offset) >> shift
  * 
  * 代码里将上式调整为:pred_h(x,y) = width*leftcolumn(y) + x*[topRight-leftcolumn(y)]
  *                   pred_v(x,y) = height*topRow(x) + y*[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;
  }

  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;
    }
  }
}

xPredIntraAng()

角度预测之前,首先需要判断是否为广角预测模式,广角模式的获取方式代码如下:

int IntraPrediction::getWideAngle( int width, int height, int predMode )
  {
    if ( predMode > DC_IDX && predMode <= VDIA_IDX )
    {
      int modeShift[] = { 0, 6, 10, 12, 14, 15 };
      int deltaSize = abs(g_aucLog2[width] - g_aucLog2[height]); // log2(width/height) 获取偏移量
      if (width > height && predMode < 2 + modeShift[deltaSize])
      {
        predMode += (VDIA_IDX - 1);
      }
      else if (height > width && predMode > VDIA_IDX - modeShift[deltaSize])
      {
        predMode -= (VDIA_IDX - 1);
      }
    }
    return predMode;
  }

即,如果getWideAngle()返回模式与预测模式相符,则为广角预测,否则为正常角度预测。

角度预测信号生成,代码如下

/** 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.
*/

void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const SPS& sps,
                                           int      multiRefIdx,
                                     const bool     useFilteredPredSamples ,
                                     const bool     useISP,
                                     const Size     cuSize )
{
  int width =int(pDst.width);  // 待预测宽
  int height=int(pDst.height); // 待预测长

  CHECK( !( dirMode > DC_IDX && dirMode < NUM_LUMA_MODE ), "Invalid intra dir" );

  int              predMode           = useISP ? getWideAngle( cuSize.width, cuSize.height, dirMode ) : getWideAngle( width, height, dirMode ); // 模式映射为广角之后的模式

  const bool       bIsModeVer         = predMode >= DIA_IDX; // 垂直34~66,水平2~33
  const int        intraPredAngleMode = (bIsModeVer) ? predMode - VER_IDX : -(predMode - HOR_IDX);
  const int        absAngMode         = abs(intraPredAngleMode);
  const int        signAng            = intraPredAngleMode < 0 ? -1 : 1;

  // Set bitshifts and scale the angle parameter to block size
  // 先乘以256(左移8位),运算出结果再右移8位,以定点实现浮点运算精度
  static const int angTable[32]    = { 0,    1,    2,    3,    4,    6,     8,   10,   12,   14,   16,   18,   20,   23,   26,   29,   32,   35,   39,  45,  51,  57,  64,  73,  86, 102, 128, 171, 256, 341, 512, 1024 };
  static const int invAngTable[32] = { 0, 8192, 4096, 2731, 2048, 1365,  1024,  819,  683,  585,  512,  455,  410,  356,  315,  282,  256,  234,  210, 182, 160, 144, 128, 112,  95,  80,  64,  48,  32,  24,  16,    8 }; // (256 * 32) / Angle

  int invAngle                    = invAngTable[absAngMode];
  int absAng                      = angTable   [absAngMode];
  int intraPredAngle              = signAng * absAng; // 预测模式转化为预测角度

  /**
  * 为了减少代码量,所有角度当做垂直计算,如果是水平预测模式,在最后做翻转 
  **/

  Pel* refMain;  // main referecnes
  Pel* refSide;  // side references

  Pel  refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];
  Pel  refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];

  const int whRatio = useISP ? std::max( unsigned( 1 ), cuSize.width / cuSize.height ) : std::max( 1, width / height );
  const int hwRatio = useISP ? std::max( unsigned( 1 ), cuSize.height / cuSize.width ) : std::max( 1, height / width );

  // Initialize the Main and Left reference array. 初始化参考像素
  if (intraPredAngle < 0)  // 预测角度为负
  {
    auto width    = int(pDst.width) +1;
    auto height   = int(pDst.height)+1;
    auto lastIdx  = (bIsModeVer ? width : height) + multiRefIdx;
    auto firstIdx = ( ((bIsModeVer ? height : width) -1) * intraPredAngle ) >> 5;
    for (int x = 0; x < width + 1 + multiRefIdx; x++) // multiref + aboveleft + above = width+1+multiref个点(多一个点是什么?)
    {
      refAbove[x + height - 1] = pSrc.at( x, 0 );
    }
    for (int y = 0; y < height + 1 + multiRefIdx; y++) // multiref + aboveleft + left = height+1+multiref个点
    {
      refLeft[y + width - 1] = pSrc.at( 0, y );
    }
    // modeVer = true: refMain = refAbove + height- 1; refSide = refLeft + width - 1
    // modeVer = false: refMain = refLeft + width- 1; refSide = refAbove + height- 1
    // refmain 和 refSide 添加偏移量,是指针指向ref[0]
    refMain = (bIsModeVer ? refAbove + height : refLeft  + width ) - 1;
    refSide = (bIsModeVer ? refLeft  + width  : refAbove + height) - 1;

    // Extend the Main reference to the left. // 复制side中的references到main references
    int invAngleSum    = 128;       // rounding for (shift by 8) ---- draft 8-131
    for( int k = -1; k > firstIdx; k-- ) // draft 8-131: ref[ x ] = p[ −1 − refIdx ][ −1 − refIdx + ( ( x * invAngle + 128 )  >>  8 ) ], with x = −1..( nTbH * intraPredAngle )  >>  5
    {
      invAngleSum += invAngle;
      refMain[k] = refSide[invAngleSum>>8];
    }
    refMain[lastIdx] = refMain[lastIdx-1];   // draft 8-133: ref[ nTbW + 1 + refIdx ] = ref[ nTbW + refIdx ]
    refMain[firstIdx] = refMain[firstIdx+1];  // draft 8-132: ref[ ( ( nTbH * intraPredAngle )  >>  5 ) − 1 ] = ref[ ( nTbH * intraPredAngle )  >>  5 ]
  }
  else // 角度为正式,垂直预测只参考上方参考样点,水平预测只参考左边参考样点
  {
    for (int x = 0; x < m_topRefLength + 1 + (whRatio + 1) * multiRefIdx; x++)
    {
      refAbove[x+1] = pSrc.at(x, 0);
    }
    for (int y = 0; y < m_leftRefLength + 1 + (hwRatio + 1) * multiRefIdx; y++)
    {
      refLeft[y+1]  = pSrc.at(0, y);
    }
    refMain = bIsModeVer ? refAbove : refLeft ;
    refSide = bIsModeVer ? refLeft  : refAbove;

    refMain++;
    refSide++;
    refMain[-1] = refMain[0];
    auto lastIdx = 1 + ((bIsModeVer) ? m_topRefLength + (whRatio + 1) * multiRefIdx : m_leftRefLength +  (hwRatio + 1) * multiRefIdx);
    refMain[lastIdx] = refMain[lastIdx-1];
  }

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

  // compensate for line offset in reference line buffers
  refMain += multiRefIdx;
  refSide += multiRefIdx;

  if( intraPredAngle == 0 )  // pure vertical or pure horizontal 水平垂直预测,直接copy赋值
  {
    for( int y = 0; y < height; y++ )
    {
      for( int x = 0; x < width; x++ )
      {
        pDstBuf[y*dstStride + x] = refMain[x + 1];
      }
    }
  }
  else
  {
    Pel *pDsty=pDstBuf;
    for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride)
    {
      const int deltaInt   = deltaPos >> 5;        // 整数坐标
      const int deltaFract = deltaPos & (32 - 1);  // 分数坐标

      if (absAng != 0 && absAng != 32)
      {
        if( isLuma(channelType) ) // 亮度-四抽头插值滤波
        {
          Pel                        p[4];

          const bool                 useCubicFilter = useISP ? ( width <= 8 ) : ( !useFilteredPredSamples || multiRefIdx > 0 );
          
          TFilterCoeff const * const f              = (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : g_intraGaussFilter[deltaFract];

          int         refMainIndex   = deltaInt + 1;

          for( int x = 0; x < width; x++, refMainIndex++ )
          {
            p[0] = refMain[refMainIndex - 1];
            p[1] = refMain[refMainIndex];
            p[2] = refMain[refMainIndex + 1];
            p[3] = f[3] != 0 ? refMain[refMainIndex + 2] : 0;

            pDstBuf[y*dstStride + x] = static_cast<Pel>((static_cast<int>(f[0] * p[0]) + static_cast<int>(f[1] * p[1]) + static_cast<int>(f[2] * p[2]) + static_cast<int>(f[3] * p[3]) + 32) >> 6);

            if( useCubicFilter ) // only cubic filter has negative coefficients and requires clipping
            {
              pDstBuf[y*dstStride + x] = ClipPel( pDstBuf[y*dstStride + x], clpRng );
            }
          }
        }
        else  // 色度-线性滤波
        {
          // Do linear filtering
          const Pel *pRM = refMain + deltaInt + 1;
          int lastRefMainPel = *pRM++;
          for( int x = 0; x < width; pRM++, x++ )
          {
            int thisRefMainPel = *pRM;
            pDsty[x + 0] = ( Pel ) ( ( ( 32 - deltaFract )*lastRefMainPel + deltaFract*thisRefMainPel + 16 ) >> 5 );
            lastRefMainPel = thisRefMainPel;
          }
        }
      }
      else
      {
      	// 整数点直接copy,不需要进行插值
        // Just copy the integer samples
        for( int x = 0; x < width; x++ )
        {
          pDsty[x] = refMain[x + deltaInt + 1];
        }
      }
      const int numModes = 8;
      const int scale = ((g_aucLog2[width] - 2 + g_aucLog2[height] - 2 + 2) >> 2);
      CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");
#if JVET_M0102_INTRA_SUBPARTITIONS
      if( !useISP )  // 对于非ISP模式,且非MRL, 对负对角线,及其8个相邻角度进行PDPC
      {
#endif
      if ((predMode == 2 || predMode == VDIA_IDX) && multiRefIdx == 0)
      {
        int wT = 16 >> std::min(31, ((y << 1) >> scale));

        for (int x = 0; x < width; x++)
        {
          int wL = 16 >> std::min(31, ((x << 1) >> scale));
          if (wT + wL == 0) break;

          int c = x + y + 1;
          if (c >= 2 * height) { wL = 0; }
          if (c >= 2 * width)  { wT = 0; }
          const Pel left = (wL != 0) ? refSide[c + 1] : 0;
          const Pel top  = (wT != 0) ? refMain[c + 1] : 0;

          pDsty[x] = ClipPel((wL * left + wT * top + (64 - wL - wT) * pDsty[x] + 32) >> 6, clpRng);
        }
      }
      else if (((predMode >= VDIA_IDX - numModes && predMode != VDIA_IDX) || (predMode != 2 && predMode <= (2 + numModes))) && multiRefIdx == 0)
      {
        int invAngleSum0 = 2;
        for (int x = 0; x < width; x++)
        {
          invAngleSum0 += invAngle;
          int deltaPos0 = invAngleSum0 >> 2;
          int deltaFrac0 = deltaPos0 & 63;
          int deltaInt0 = deltaPos0 >> 6;

          int deltay = y + deltaInt0 + 1;
          if (deltay >(bIsModeVer ? m_leftRefLength : m_topRefLength) - 1) break;

          int wL = 32 >> std::min(31, ((x << 1) >> scale));
          if (wL == 0) break;
          Pel *p = refSide + deltay;

#if JVET_M0238_PDPC_NO_INTERPOLATION // yes
          Pel left = p[deltaFrac0 >> 5];
#else
          Pel left = (((64 - deltaFrac0) * p[0] + deltaFrac0 * p[1] + 32) >> 6);
#endif
          pDsty[x] = ClipPel((wL * left + (64 - wL) * pDsty[x] + 32) >> 6, clpRng);
        }
      }
#if JVET_M0102_INTRA_SUBPARTITIONS
      }
#endif
    }
  }

  // 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;
    }
  }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值