H.266/VVC代码学习26:VTM5.0中IntraPrediction类及其相应全局函数

千呼万唤始出来,VTM5.0的rc1版本终于发布了。
基于前面VTM4.0中的内容,所有帧内预测的函数都已学习过。今天我们除了看一下VTM5.0中帧内预测的改变,也来关注一下跟C++更相关的东西:IntraPrediction类的定义。

文章目录

  • 1 IntraPrediction类
    • 1.1 private部分(VTM4.0)
      • VTM5.0改动:
    • 1.2 protected部分(VTM4.0)
      • VTM5.0改动:
    • 1.3 public部分(VTM4.0)
      • VTM5.0改动:
  • 2 初始化过程和析构过程
  • 3 全局函数(判断参考像素是否可用及可用数量)

1 IntraPrediction类

总体来说,除了新增加了几个帧内的技术,还添加了一个IntraPredParam类,用于存放帧内预测过程中的一些重要参数。所以新版本中,函数的参数数量有了一定的减少,也是代码更加精简的一种标志。

1.1 private部分(VTM4.0)

在C++中private中主要定义的是数据。这里我们可以看到,IntraPrediction类中的private里存放的都是像素缓存值。

private:

  Pel* m_piYuvExt[MAX_NUM_COMPONENT][NUM_PRED_BUF];//存预测值的位置,这个buffer存在三个通道的和是否滤波的
  int  m_iYuvExtSize;//预测值的长度

  Pel* m_yuvExt2[MAX_NUM_COMPONENT][4];
  int  m_yuvExtSize2;

  static const uint8_t m_aucIntraFilter[MAX_NUM_CHANNEL_TYPE][MAX_INTRA_FILTER_DEPTHS];//滤波强度?

  Pel* m_piTemp;//预测值缓存的临时存储位置
  Pel* m_pMdlmTemp; // for MDLM mode   MDLM预测值缓存的临时存储位置

VTM5.0改动:

private里,VTM5.0新增了如下两部分代码:1、帧内预测参数,2、ALWIP矩阵帧内预测(利用深度学习的方法训练得到一组矩阵参数,用他们进行预测)

  struct IntraPredParam //parameters of Intra Prediction 
  {
    bool refFilterFlag;	//是否使用滤波器
    bool applyPDPC;		//是否使用PDPC
    bool isModeVer;		//是否是垂直类型模式(模式号为35-66)
    int  multiRefIndex;	//多参考行索引
    int  whRatio;		//宽高比
    int  hwRatio;		//高宽比
    int  intraPredAngle;//帧内预测角度
    int  invAngle;		//预测角度
    bool interpolationFlag;

    IntraPredParam() : //构造函数,将IntraPredParam的对象进行默认初始化
      refFilterFlag     ( false                           ), 
      applyPDPC         ( false                           ), 
      isModeVer         ( false                           ), 
      multiRefIndex     ( -1                              ), 
      whRatio           ( 0                               ), 
      hwRatio           ( 0                               ), 
      intraPredAngle    ( std::numeric_limits<int>::max() ),
      invAngle          ( std::numeric_limits<int>::max() ), 
      interpolationFlag ( false                           ) {}
  };
  IntraPredParam m_ipaParam;//上面这个IntraPredParam类的对象
  
#if JVET_N0217_MATRIX_INTRAPRED
  MatrixIntraPrediction m_matrixIntraPred;//ALWIP
#endif

1.2 protected部分(VTM4.0)

protected对于子类来说是public的,没有任何限制。而对于其他的外部class,protected就变成private。

protected:

  ChromaFormat  m_currChromaFormat;//色彩格式

  int m_topRefLength;//上边长度
  int m_leftRefLength;//左边长度
  // prediction
  void xPredIntraPlanar           ( const CPelBuf &pSrc, PelBuf &pDst,                                                                                                         const SPS& sps );//PLANAR预测
  void xPredIntraDc               ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType,                                                                                          const bool enableBoundaryFilter = true );//DC预测
#if HEVC_USE_HOR_VER_PREDFILTERING
  void xPredIntraAng              ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const bool bEnableEdgeFilters, const SPS& sps
    , int multiRefIdx
    , const bool enableBoundaryFilter = true );
#else
#if JVET_M0102_INTRA_SUBPARTITIONS
  void 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 = false,
                                    const Size cuSize = Size( 0, 0 ) );//角度预测
#else
  void xPredIntraAng              ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const SPS& sps
    , int multiRefIdx
    , const bool useFilteredPredSamples );
#endif
#endif
  Pel  xGetPredValDc              ( const CPelBuf &pSrc, const Size &dstSize );//得到DC填充值

  void xFillReferenceSamples      ( const CPelBuf &recoBuf,      Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu );//填充参考像素
  void xFilterReferenceSamples    ( const Pel* refBufUnfiltered, Pel* refBufFiltered, const CompArea &area, const SPS &sps//滤波参考像素
    , int multiRefIdx
  );

#if HEVC_USE_DC_PREDFILTERING
  // dc filtering
  void xDCPredFiltering           ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType &channelType );
#endif
  static int getWideAngle         ( int width, int height, int predMode );//静态函数:处理静态数据,获取宽角度
  void setReferenceArrayLengths   ( const CompArea &area );//设置参考像素的长度

  void destroy                    ();//析构时使用

  void xFilterGroup               ( Pel* pMulDst[], int i, Pel const* const piSrc, int iRecStride, bool bAboveAvaillable, bool bLeftAvaillable);//没有用上
  void xGetLMParameters(const PredictionUnit &pu, const ComponentID compID, const CompArea& chromaArea, int& a, int& b, int& iShift);//获取LM的系数

VTM5.0改动:

protected里,VTM5.0中,对帧内预测的参数用一个类来进行管理,还增加了一种宽角度模式与滤波器之间统一的方法。

  void initPredIntraParams        ( const PredictionUnit & pu,  const CompArea compArea, const SPS& sps );//初始化帧内预测参数

#if JVET_N0435_WAIP_HARMONIZATION
  static bool isIntegerSlope(const int absAng) { return (0 == (absAng & 0x1F)); }//统一宽角度模式与滤波器
#else
  static bool isIntegerSlope      ( const int absAng ) { return (0 == (absAng & 0x1F)) && absAng <=32; }  //  integer-slope modes 2, DIA_IDX and VDIA_IDX.  "absAng <=32" restricts wide-angle integer modes 
#endif

#if JVET_N0413_RDPCM
  void xPredIntraBDPCM            ( const CPelBuf &pSrc, PelBuf &pDst, const uint32_t dirMode, const ClpRng& clpRng );//帧内RDPCM预测
#endif

1.3 public部分(VTM4.0)

public:
  IntraPrediction();//构造
  virtual ~IntraPrediction();//析构

  void init                       (ChromaFormat chromaFormatIDC, const unsigned bitDepthY);//初始化

  // Angular Intra
  void predIntraAng               ( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu, const bool useFilteredPredSamples );
#if JVET_M0043_LWIP_MEMOPT
  void predIntraLwip              ( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu );
#endif
  Pel*  getPredictorPtr           (const ComponentID compID, const bool bUseFilteredPredictions = false) { return m_piYuvExt[compID][bUseFilteredPredictions?PRED_BUF_FILTERED:PRED_BUF_UNFILTERED]; }//获取像素值要填充的位置
  // Cross-component Chroma
  void predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir);//LM预测
  void xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea);//获取LM下采样后亮度重建值
  /// set parameters from CU data for accessing intra data
  void initIntraPatternChType     (const CodingUnit &cu, const CompArea &area, const bool bFilterRefSamples = false );//初始化帧内预测

  static bool useFilteredIntraRefSamples( const ComponentID &compID, const PredictionUnit &pu, bool modeSpecific, const UnitArea &tuArea );//使用滤波的吗?
  static bool useDPCMForFirstPassIntraEstimation(const PredictionUnit &pu, const uint32_t &uiDirMode);

  void geneWeightedPred           (const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf);
  Pel* getPredictorPtr2           (const ComponentID compID, uint32_t idx) { return m_yuvExt2[compID][idx]; }
  void switchBuffer               (const PredictionUnit &pu, ComponentID compID, PelBuf srcBuff, Pel *dst);
  void geneIntrainterPred         (const CodingUnit &cu);

#if JVET_M0043_LWIP_MEMOPT
  void prepareBlockLwip           ( const PredictionUnit &pu );
#endif

VTM5.0改动:

public里,VTM5.0中主要增加了对ALWIP矩阵帧内预测的具体实现。

#if JVET_N0217_MATRIX_INTRAPRED
  // Matrix-based intra prediction
  void initIntraMip               (const PredictionUnit &pu);//初始化ALWIP
  void predIntraMip               (const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu);//进行矩阵帧内预测
#endif

#if HM_MDIS_AS_IN_JEM && JVET_N0193_LFNST
  static bool getPlanarMDISCondition( const UnitArea &tuArea ) { return abs( PLANAR_IDX - HOR_IDX ) > m_aucIntraFilter[ CHANNEL_TYPE_LUMA ][ ( ( g_aucLog2[ tuArea.Y().width ] + g_aucLog2[ tuArea.Y().height ] ) >> 1 ) ]; }
#endif

2 初始化过程和析构过程

// ====================================================================================================================
// Constructor / destructor / initialize
// ====================================================================================================================

IntraPrediction::IntraPrediction()//构造函数,给指针设定初始位置nullptr
:
  m_currChromaFormat( NUM_CHROMA_FORMAT )//:构造函数的初始列,赋初值
{
  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)//遍历通道
  {
    for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)//遍历内存
    {
      m_piYuvExt[ch][buf] = nullptr;//设为nullptr
    }
  }
  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
  {
    for (uint32_t buf = 0; buf < 4; buf++)
    {
      m_yuvExt2[ch][buf] = nullptr;
    }
  }

  m_piTemp = nullptr;//暂存像素值指针
  m_pMdlmTemp = nullptr;//MDLM指针
}

IntraPrediction::~IntraPrediction()//析构函数,析构前调用destroy()函数
{
  destroy();
}

void IntraPrediction::destroy()//析构操作,这里是构造函数的反向操作
{
  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
  {
    for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)
    {
      delete[] m_piYuvExt[ch][buf];//delete[]配合new m_piYuvExt[],这里对应*m_piYuvExt
      m_piYuvExt[ch][buf] = nullptr;
    }
  }
  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
  {
    for (uint32_t buf = 0; buf < 4; buf++)
    {
      delete[] m_yuvExt2[ch][buf];
      m_yuvExt2[ch][buf] = nullptr;
    }
  }

  delete[] m_piTemp;
  m_piTemp = nullptr;
  delete[] m_pMdlmTemp;
  m_pMdlmTemp = nullptr;
  //可以看出,析构一个指针类型,需要用delete[]
}

void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY)//初始化
{
/************ 如果之前已初始化,但色度格式已更改,释放内存并重新开始 **************/
  // if it has been initialised before, but the chroma format has changed, release the memory and start again.
  if (m_piYuvExt[COMPONENT_Y][PRED_BUF_UNFILTERED] != nullptr && m_currChromaFormat != chromaFormatIDC)
  {
    destroy();
  }

  if (m_yuvExt2[COMPONENT_Y][0] != nullptr && m_currChromaFormat != chromaFormatIDC)
  {
    destroy();
  }

  m_currChromaFormat = chromaFormatIDC;

  /********************* 进行构造,初始化内存及长度 ************************/
  if (m_piYuvExt[COMPONENT_Y][PRED_BUF_UNFILTERED] == nullptr) // check if first is null (in which case, nothing initialised yet)
  {
    m_iYuvExtSize = (MAX_CU_SIZE * 2 + 1 + MAX_REF_LINE_IDX * 33) * (MAX_CU_SIZE * 2 + 1 + MAX_REF_LINE_IDX * 33);

    for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
    {
      for (uint32_t buf = 0; buf < NUM_PRED_BUF; buf++)
      {
        m_piYuvExt[ch][buf] = new Pel[m_iYuvExtSize];
      }
    }
  }

  if (m_yuvExt2[COMPONENT_Y][0] == nullptr) // check if first is null (in which case, nothing initialised yet)
  {
    m_yuvExtSize2 = (MAX_CU_SIZE) * (MAX_CU_SIZE);

    for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
    {
      for (uint32_t buf = 0; buf < 4; buf++)
      {
        m_yuvExt2[ch][buf] = new Pel[m_yuvExtSize2];
      }
    }
  }

  if (m_piTemp == nullptr)
  {
    m_piTemp = new Pel[(MAX_CU_SIZE + 1) * (MAX_CU_SIZE + 1)];
  }
  if (m_pMdlmTemp == nullptr)
  {
    m_pMdlmTemp = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];//MDLM will use top-above and left-below samples.
  }
}

3 全局函数(判断参考像素是否可用及可用数量)

全局函数是整个程序中共用的,这些函数在整个程序的任何地方都允许被调用,也就是说不单单属于IntraPrediction类。
这里有五个,都是获取参考像素时用的,为isAboveLeftAvailable、isLeftAvailable、isLeftAvailable、isAboveRightAvailable、isBelowLeftAvailable。

/****************** 左上角是否可用 ***************/
bool isAboveLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT)
{
  const CodingStructure& cs = *cu.cs;
  const Position refPos = posLT.offset(-1, -1);//得到左上角像素的左上位置
  const CodingUnit* pcCUAboveLeft = cs.isDecomp( refPos, chType ) ? cs.getCURestricted( refPos, cu, chType ) : nullptr;//这个像素点是否存在且可用,可以则指向其CU,否则设为空指针
  const bool isConstrained = cs.pps->getConstrainedIntraPred();//是否限制了帧内预测
  bool bAboveLeftFlag;

  if (isConstrained)//若限制了:左上角可用且是帧内预测出来的,才可以
  {
    bAboveLeftFlag = pcCUAboveLeft && CU::isIntra(*pcCUAboveLeft);
  }
  else//无限制:左上角可用则可用
  {
    bAboveLeftFlag = (pcCUAboveLeft ? true : false);
  }

  return bAboveLeftFlag;//返回是否可用
}

/****************** 上面有几个可用的点 ***************/
int isAboveAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags)
{
  const CodingStructure& cs = *cu.cs;
  const bool isConstrained = cs.pps->getConstrainedIntraPred();//是否限制了帧内预测
  bool *pbValidFlags = bValidFlags;//该像素点是否可用
  int iNumIntra = 0;//数量
  int maxDx = uiNumUnitsInPU * unitWidth;

  for (uint32_t dx = 0; dx < maxDx; dx += unitWidth)
  {
    const Position refPos = posLT.offset(dx, -1);//得到上一排像素

    const CodingUnit* pcCUAbove = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;//这个像素点是否存在且可用,可以则指向其CU,否则设为空指针

    if( pcCUAbove && ( ( isConstrained && CU::isIntra( *pcCUAbove ) ) || !isConstrained ) )//如果存在且满足限制条件,个数+1,可用的标志设为true
    {
      iNumIntra++;//个数+1
      *pbValidFlags = true;//该像素点存在且可用
    }
    else if( !pcCUAbove )//如果不存在,计数停止,直接返回
    {
      return iNumIntra;
    }

    pbValidFlags++;//指向下一个位置(右边一个)
  }
  return iNumIntra;//返回可用参考像素的个数
}

/***以下同上***/
/****************** 左面有几个可用的点 ***************/
int isLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags)
{
  const CodingStructure& cs = *cu.cs;
  const bool isConstrained = cs.pps->getConstrainedIntraPred();
  bool *pbValidFlags = bValidFlags;
  int iNumIntra = 0;
  int maxDy = uiNumUnitsInPU * unitHeight;

  for (uint32_t dy = 0; dy < maxDy; dy += unitHeight)
  {
    const Position refPos = posLT.offset(-1, dy);

    const CodingUnit* pcCULeft = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;

    if( pcCULeft && ( ( isConstrained && CU::isIntra( *pcCULeft ) ) || !isConstrained ) )
    {
      iNumIntra++;
      *pbValidFlags = true;
    }
    else if( !pcCULeft )
    {
      return iNumIntra;
    }

    pbValidFlags--; // opposite direction 
  }

  return iNumIntra;
}

/****************** 右上有几个可用的点 ***************/
int isAboveRightAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posRT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags )
{
  const CodingStructure& cs = *cu.cs;
  const bool isConstrained = cs.pps->getConstrainedIntraPred();
  bool *pbValidFlags = bValidFlags;
  int iNumIntra = 0;

  uint32_t maxDx = uiNumUnitsInPU * unitWidth;

  for (uint32_t dx = 0; dx < maxDx; dx += unitWidth)
  {
    const Position refPos = posRT.offset(unitWidth + dx, -1);

    const CodingUnit* pcCUAbove = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;

    if( pcCUAbove && ( ( isConstrained && CU::isIntra( *pcCUAbove ) ) || !isConstrained ) )
    {
      iNumIntra++;
      *pbValidFlags = true;
    }
    else if( !pcCUAbove )
    {
      return iNumIntra;
    }

    pbValidFlags++;
  }

  return iNumIntra;
}

/****************** 左下有几个可用的点 ***************/
int isBelowLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const Position &posLB, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags )
{
  const CodingStructure& cs = *cu.cs;
  const bool isConstrained = cs.pps->getConstrainedIntraPred();
  bool *pbValidFlags = bValidFlags;
  int iNumIntra = 0;
  int maxDy = uiNumUnitsInPU * unitHeight;

  for (uint32_t dy = 0; dy < maxDy; dy += unitHeight)
  {
    const Position refPos = posLB.offset(-1, unitHeight + dy);

    const CodingUnit* pcCULeft = cs.isDecomp(refPos, chType) ? cs.getCURestricted(refPos, cu, chType) : nullptr;

    if( pcCULeft && ( ( isConstrained && CU::isIntra( *pcCULeft ) ) || !isConstrained ) )
    {
      iNumIntra++;
      *pbValidFlags = true;
    }
    else if ( !pcCULeft )
    {
      return iNumIntra;
    }

    pbValidFlags--; // opposite direction
  }

  return iNumIntra;
}

通过对这个类的总结,对前面C++学习1-10可以说有了进一步的巩固,当然更重要的是后面的工作将在VTM5.0上开展,新技术的了解和代码的学习又是一道道难关,革命尚未成功,同志仍需努力。。。

你可能感兴趣的:(H.266/VVC视频编码)