H.266/VVC代码学习:MPM列表建立(getIntraMPMs函数)

VVC具有67种角度模式,如果分别对每个PU的预测模式进行编码,则对于67种模式需要7比特来编码,因此VVC中也采用了构建最可能模式列表(most probable mode ,MPM)的方法。在图像和视频编码中,相邻块通常具有较强的相关性,因此相邻块的帧内预测模式相同或者相似的概率较大,因此MPM列表基于左相邻PU和上相邻PU的帧内预测模式构建的,在VVC中,其MPM列表的长度为6。在帧内编码中,MPM列表的主要在以下两个过程

  1. 进行多参考行模式的SATD粗选中(mrl_idx = 1、2),仅使用MPM列表中的6种帧内预测模式
  2. 在对MIP模式粗选结束后,遍历MPM列表中的预测模式是否在率失真候选列表中

具体构建步骤如下:

1、获得左下和右上相邻参考像素,分别记为A和B,如下图所示

在这里插入图片描述

2、获取相邻像素A和B所在PU的帧内预测模式,获取方法如下:

  • 如果以下条件之一成立,则相邻PU的帧内预测模式设置为Planar模式 
    • 其相邻PU不可用
    • 相邻PU的编码模式不是帧内编码模式
    • 相邻PU是MIP模式
    • 相邻PU和当前PU不是位于同一个CTU
  • 否则,获取其相邻PU的帧内预测模式

3、将相邻像素A、B所处PU的预测模式分别记为A、B,则MPM列表构建如下:

(1)A = B且A > B

MPM[0] Planar
MPM[1]

A

MPM[2]

 2 + ( ( A + 61 ) % 64 )

MPM[3] 2 + ( ( A − 1 ) % 64 )
MPM[4] 2 + ( ( A + 60 ) % 64 )
MPM[5] 2 + ( A  % 64 )

(2)A ≠ B,A > DC且B > DC

记MinAB = Min(A,B), MaxAB = max(A,B)

MPM[0] Planar
MPM[1]

A

MPM[2] B

  ①若maxAB - minAB = 1,则

MPM[3]

2 + ( ( minAB + 61 ) % 64 )

MPM[4] 2 + ( ( maxAB − 1 ) % 64 )
MPM[5] 2 + ( ( minAB + 60 ) % 64 )

  ②若maxAB - minAB >= 62,则

MPM[3]

2 + ( ( minAB − 1 ) % 64 )

MPM[4] 2 + ( ( maxAB + 61 ) % 64 )
MPM[5] 2 + ( minAB % 64 )

   ③若maxAB - minAB = 2,则

MPM[3]

2 + ( ( minAB − 1 ) % 64 )

MPM[4] 2 + ( ( minAB + 61 ) % 64 )
MPM[5] 2 + ( ( maxAB - 1 ) % 64 )

  ④否则

MPM[3]

2 + ( ( minAB + 61 ) % 64 )

MPM[4] 2 + ( ( minAB - 1 ) % 64 )
MPM[5] 2 + ( ( maxAB + 61 ) % 64 )

(3)A ≠ B,A > DC 或 B > DC

MPM[0] Planar
MPM[1]

maxAB

MPM[2] 2 + ( ( maxAB + 61 ) % 64 )
MPM[3] 2 + ( ( maxAB − 1 ) % 64 )
MPM[4] 2 + ( ( maxAB + 60 ) % 64 )
MPM[5] 2 + ( maxAB % 64 )

(4)否则,

MPM[0] Planar
MPM[1]

DC

MPM[2] VER
MPM[3] HOR
MPM[4] VER-4
MPM[5] VER+4

具体代码实现及注释如下:

int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
{
  const int numMPMs = NUM_MOST_PROBABLE_MODES;
  {
    CHECK(channelType != CHANNEL_TYPE_LUMA, "Not harmonized yet");
    int numCand      = -1;
    int leftIntraDir = PLANAR_IDX, aboveIntraDir = PLANAR_IDX;//将左相邻块和上相邻块预测模式设置为Planar模式

    const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));
    const Position posRT = area.topRight();//当前块的左上角
    const Position posLB = area.bottomLeft();//当前块的右下角

    // Get intra direction of left PU
    // 获得左相邻PU
    const PredictionUnit *puLeft = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, channelType);
    if (puLeft && CU::isIntra(*puLeft->cu))
    {
      leftIntraDir = PU::getIntraDirLuma( *puLeft );//获得左相邻PU的预测模式
    }

    // Get intra direction of above PU
    // 获得上相邻PU
    const PredictionUnit *puAbove = pu.cs->getPURestricted(posRT.offset(0, -1), pu, channelType);
    if (puAbove && CU::isIntra(*puAbove->cu) && CU::isSameCtu(*pu.cu, *puAbove->cu))
    {
      aboveIntraDir = PU::getIntraDirLuma( *puAbove );//获得左相邻PU的预测模式
    }

    CHECK(2 >= numMPMs, "Invalid number of most probable modes");

    const int offset = (int)NUM_LUMA_MODE - 6;//61
    const int mod = offset + 3;//64

    {
      mpm[0] = PLANAR_IDX;//Planar
      mpm[1] = DC_IDX;//DC
      mpm[2] = VER_IDX;//50
      mpm[3] = HOR_IDX;//18
      mpm[4] = VER_IDX - 4;//46
      mpm[5] = VER_IDX + 4;//54

      if (leftIntraDir == aboveIntraDir)
      {
        numCand = 1;
        if (leftIntraDir > DC_IDX)
        {
          mpm[0] = PLANAR_IDX;
          mpm[1] = leftIntraDir;
          mpm[2] = ((leftIntraDir + offset) % mod) + 2;
          mpm[3] = ((leftIntraDir - 1) % mod) + 2;
          mpm[4] = ((leftIntraDir + offset - 1) % mod) + 2;
          mpm[5] = ( leftIntraDir               % mod) + 2;
        }
      }
      else //L!=A
      {
        numCand = 2;
        int  maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;

        if ((leftIntraDir > DC_IDX) && (aboveIntraDir > DC_IDX))
        {
          mpm[0] = PLANAR_IDX;
          mpm[1] = leftIntraDir;
          mpm[2] = aboveIntraDir;
          maxCandModeIdx = mpm[1] > mpm[2] ? 1 : 2;
          int minCandModeIdx = mpm[1] > mpm[2] ? 2 : 1;
          if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1)
          {
            mpm[3] = ((mpm[minCandModeIdx] + offset)     % mod) + 2;
            mpm[4] = ((mpm[maxCandModeIdx] - 1)          % mod) + 2;
            mpm[5] = ((mpm[minCandModeIdx] + offset - 1) % mod) + 2;
          }
          else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62)
          {
            mpm[3] = ((mpm[minCandModeIdx] - 1)      % mod) + 2;
            mpm[4] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
            mpm[5] = ( mpm[minCandModeIdx]           % mod) + 2;
          }
          else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2)
          {
            mpm[3] = ((mpm[minCandModeIdx] - 1)      % mod) + 2;
            mpm[4] = ((mpm[minCandModeIdx] + offset) % mod) + 2;
            mpm[5] = ((mpm[maxCandModeIdx] - 1)      % mod) + 2;
          }
          else
          {
            mpm[3] = ((mpm[minCandModeIdx] + offset) % mod) + 2;
            mpm[4] = ((mpm[minCandModeIdx] - 1)      % mod) + 2;
            mpm[5] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
          }
        }
        else if (leftIntraDir + aboveIntraDir >= 2)
        {
          mpm[0] = PLANAR_IDX;
          mpm[1] = (leftIntraDir < aboveIntraDir) ? aboveIntraDir : leftIntraDir;
          maxCandModeIdx = 1;
          mpm[2] = ((mpm[maxCandModeIdx] + offset)     % mod) + 2;
          mpm[3] = ((mpm[maxCandModeIdx] - 1)          % mod) + 2;
          mpm[4] = ((mpm[maxCandModeIdx] + offset - 1) % mod) + 2;
          mpm[5] = ( mpm[maxCandModeIdx]               % mod) + 2;
        }
      }
    }
    for (int i = 0; i < numMPMs; i++)
    {
      CHECK(mpm[i] >= NUM_LUMA_MODE, "Invalid MPM");
    }
    CHECK(numCand == 0, "No candidates found");
    return numCand;
  }
}

getPURestricted函数主要是根据位置获取PU

const PredictionUnit* CodingStructure::getPURestricted( const Position &pos, const PredictionUnit& curPu, const ChannelType _chType ) const
{
  const PredictionUnit* pu = getPU( pos, _chType );
  // exists       same slice and tile                  pu precedes curPu in encoding order
  //                                                  (thus, is either from parent CS in RD-search or its index is lower)
  //存在 相同slice和Tile    PU在编码顺序中先于当前PU
  //(因此,在RD搜索中是来自父CS的,或者其索引较低)
  const bool wavefrontsEnabled = curPu.cu->slice->getSPS()->getEntropyCodingSyncEnabledFlag();
  int ctuSizeBit = floorLog2(curPu.cs->sps->getMaxCUWidth());
  int xNbY  = pos.x << getChannelTypeScaleX( _chType, curPu.chromaFormat );//相邻PU的左上角位置x
  int xCurr = curPu.blocks[_chType].x << getChannelTypeScaleX( _chType, curPu.chromaFormat );//当前PU的左上角位置x
  //判断相邻PU和当前PU是否在同一CTU中
  bool addCheck = (wavefrontsEnabled && (xNbY >> ctuSizeBit) >= (xCurr >> ctuSizeBit) + 1 ) ? false : true;
  if( pu && CU::isSameSliceAndTile( *pu->cu, *curPu.cu ) && ( pu->cs != curPu.cs || pu->idx <= curPu.idx ) && addCheck )
  {
    return pu;
  }
  else
  {
    return nullptr;
  }
}

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/106598707