当前位置: 首页 > 工具软件 > mpm > 使用案例 >

VVC学习之五:帧内预测——MPM列表建立

归德厚
2023-12-01

getIntraMPMs()为命名空间PU下的函数,用于获取帧内预测MPM,一共三类:传统预测MPM,多参考行预测MPM,ISP预测MPM,代码和相关注释如下

int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
{
  const int numMPMs = NUM_MOST_PROBABLE_MODES;  // 6个MPM
  const int extendRefLine = (channelType == CHANNEL_TYPE_LUMA) ? pu.multiRefIdx : 0;  // 多行预测参考行索引
#if JVET_M0102_INTRA_SUBPARTITIONS
  const ISPType ispType = isLuma( channelType ) ? ISPType( pu.cu->ispMode ) : NOT_INTRA_SUBPARTITIONS;  // isp划分类型
  const bool isHorSplit = ispType == HOR_INTRA_SUBPARTITIONS;  // 是否为水平ISP
#endif
  {
    int numCand      = -1;  // 记录有效相邻单元模式
    int leftIntraDir = PLANAR_IDX, aboveIntraDir = PLANAR_IDX; // 左PU和上PU帧内预测模式,如果pu不存在,默认为Planar模式

    const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));
    const Position posRT = area.topRight();  // 右上坐标
    const Position posLB = area.bottomLeft();  // 左下

    // Get intra direction of left PU 
    const PredictionUnit *puLeft = pu.cs->getPURestricted(posLB.offset(-1, 0), pu, channelType);
    if (puLeft && CU::isIntra(*puLeft->cu))
    {
      leftIntraDir = puLeft->intraDir[channelType];
    }

    // Get intra direction of above 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 = puAbove->intraDir[channelType];
    }

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

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

    if (extendRefLine) //#### 多行预测MPM获取 ####
    {
      int modeIdx = 0; // 左、上pu不同角度模式个数
      int angularMode[2] = { 0, 0 }; // 记录相应角度模式
      if (leftIntraDir > DC_IDX)
      {
        angularMode[modeIdx++] = leftIntraDir;
      }
      if (aboveIntraDir > DC_IDX && aboveIntraDir != leftIntraDir)
      {
        angularMode[modeIdx++] = aboveIntraDir;
      }
      
      if (modeIdx == 0) // 如果相邻块没有角度模式,填充默认:垂直、水平、对角线(34)、负对角线(2,66),26
      {
        mpm[0] = VER_IDX;
        mpm[1] = HOR_IDX;
        mpm[2] = 2;
        mpm[3] = DIA_IDX;
        mpm[4] = VDIA_IDX;
        mpm[5] = 26;
      }
      else if (modeIdx == 1) // 如果有一个角度模式A
      {
        mpm[0] = angularMode[0];                            // A
        mpm[1] = ((angularMode[0] + offset) % mod) + 2;     // A-1  (2->65)
        mpm[2] = ((angularMode[0] - 1) % mod) + 2;          // A+1  (65->2,66->3)
        mpm[3] = ((angularMode[0] + offset - 1) % mod) + 2; // A-2 (2->64,3->65)
        mpm[4] = (angularMode[0] % mod) + 2;                // A+2 (64->2, 65->3,66->4)
        mpm[5] = ((angularMode[0] + offset - 2) % mod) + 2; // A-3 (2->63,3->64,4->65)
      }
      else // 如果两个模式都为角度,且各不相同: A 和 B
      {
        mpm[0] = angularMode[0]; // A
        mpm[1] = angularMode[1]; // B
        int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1; // 角度模式索引:大
        int minCandModeIdx = 1 - maxCandModeIdx;      // 角度模式索引:小
        if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1) //> 如果两个角度模式向差1
        {
          mpm[2] = ((angularMode[minCandModeIdx] + offset) % mod) + 2; // 小-1
          mpm[3] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2;      // 大+1
          mpm[4] = ((angularMode[minCandModeIdx] + offset - 1) % mod) + 2;  // 小-2
          mpm[5] = ( angularMode[maxCandModeIdx] % mod) + 2;                // 大+2
        }
        else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62) //> 两个角度模式相差大于等于62
        {
          mpm[2] = ((angularMode[minCandModeIdx] - 1) % mod) + 2;      // 小+1
          mpm[3] = ((angularMode[maxCandModeIdx] + offset) % mod) + 2; // 大-1
          mpm[4] = ((angularMode[minCandModeIdx]) % mod) + 2; 		   // 小+2
          mpm[5] = ((angularMode[maxCandModeIdx] + offset - 1) % mod) + 2; // 大-2
        }
        else if (mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2) //> 两个角度模式相差2
        {
          mpm[2] = ((angularMode[minCandModeIdx] - 1) % mod) + 2; 			// 小+1
          mpm[3] = ((angularMode[minCandModeIdx] + offset) % mod) + 2; 		// 小-1
          mpm[4] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2;          // 大+1
          mpm[5] = ((angularMode[minCandModeIdx] + offset - 1) % mod) + 2; // 大-1
        }
        else // 其余情况(A±1,B±1)
        {
          mpm[2] = ((angularMode[minCandModeIdx] + offset) % mod) + 2; 
          mpm[3] = ((angularMode[minCandModeIdx] - 1) % mod) + 2;
          mpm[4] = ((angularMode[maxCandModeIdx] + offset) % mod) + 2;
          mpm[5] = ((angularMode[maxCandModeIdx] - 1) % mod) + 2;
        }
      }
    }
#if JVET_M0102_INTRA_SUBPARTITIONS
    else if( ispType != NOT_INTRA_SUBPARTITIONS ) //#### ISP MPM列表 ####  原则:DC模式不进行ISP
    {
      //default case 首先快速建立默认模式列表(如果,左、上模式都不是角度模式,MPM最终为默认填充的列表)
      mpm[0] = PLANAR_IDX;   // Planar
      if( isHorSplit ) //> 水平划分默认列表顺序:Planar, 水平, 25, 10 ,65, 垂直
      {
        mpm[1] = HOR_IDX;
        mpm[2] = 25;
        mpm[3] = 10;
        mpm[4] = 65;
        mpm[5] = VER_IDX;
      }
      else      //> 垂直划分默认列表顺序:Planar, 垂直, 43, 60, 3, 水平
      {
        mpm[1] = VER_IDX;
        mpm[2] = 43;
        mpm[3] = 60;
        mpm[4] = 3;
        mpm[5] = HOR_IDX;
      }
      int canonicalMode = mpm[1];
      if( leftIntraDir == aboveIntraDir ) //L=A 相邻块只有一个模式
      {
        numCand = 1;
        if( leftIntraDir > DC_IDX ) // 该模式为角度模式 AL(优先AL上下文模式)
        {
          mpm[0] =     leftIntraDir;      					// AL
          mpm[1] = ( ( leftIntraDir + offset ) % mod ) + 2; // AL-1
          mpm[2] = ( ( leftIntraDir - 1 ) % mod ) + 2;      // AL+1
          if( ( isHorSplit && leftIntraDir < DIA_IDX ) || ( !isHorSplit && leftIntraDir >= DIA_IDX ) ) 
          // 水平划分相邻模式也是水平模式,或垂直划分相邻模式是垂直模式
          {
            mpm[3] = ( ( leftIntraDir + offset - 1 ) % mod ) + 2; // AL-2
            mpm[4] =   ( leftIntraDir                % mod ) + 2; // AL+2
            mpm[5] = ( ( leftIntraDir + offset - 2 ) % mod ) + 2; // AL-3
          }
          else // 其他情况
          {
            if( isHorSplit ) // 水平划分:水平和5
            {
              mpm[3] = HOR_IDX;
              mpm[4] = 5;
            }
            else // 垂直划分:垂直和63
            {
              mpm[3] = VER_IDX;
              mpm[4] = VDIA_IDX - 3;
            }
            mpm[5] = PLANAR_IDX; // Planar
          }
        }
      }
      else //L!=A 相邻块模式不同
      {
        numCand = 2;
        if( ( leftIntraDir > DC_IDX ) && ( aboveIntraDir > DC_IDX ) ) // ***左、上都是角度模式***
        {
          // 依据左、上模式与相关ISP划分方式的模式(水平划分对瓶水平模式、垂直划分对应垂直模式)距离,距离近的排在前面
          int distLeftToCanonicalMode  = abs( leftIntraDir - canonicalMode );
          int distAboveToCanonicalMode = abs( aboveIntraDir - canonicalMode );
          mpm[0] = aboveIntraDir;
          mpm[1] = leftIntraDir;
          if( distLeftToCanonicalMode <= distAboveToCanonicalMode )
          {
            mpm[0] = leftIntraDir;
            mpm[1] = aboveIntraDir;
          }
          // --- 后面的模式,根据左上模式的数值大小,进行上下偏移,和多行预测类似---
          int maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;
          int minCandModeIdx = 1 - maxCandModeIdx;
          if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 1 )
          {
            mpm[2] = ( ( mpm[minCandModeIdx] + offset )     % mod ) + 2;
            mpm[3] = ( ( mpm[maxCandModeIdx] - 1 )          % mod ) + 2;
            mpm[4] = ( ( mpm[minCandModeIdx] + offset - 1 ) % mod ) + 2;
            mpm[5] =   ( mpm[maxCandModeIdx]                % mod ) + 2;
          }
          else if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] >= 62 )
          {
            mpm[2] = ( ( mpm[minCandModeIdx] - 1 )          % mod ) + 2;
            mpm[3] = ( ( mpm[maxCandModeIdx] + offset )     % mod ) + 2;
            mpm[4] = ( ( mpm[minCandModeIdx] )              % mod ) + 2;
            mpm[5] = ( ( mpm[maxCandModeIdx] + offset - 1 ) % mod ) + 2;
          }
          else if( mpm[maxCandModeIdx] - mpm[minCandModeIdx] == 2 )
          {
            mpm[2] = ( ( mpm[minCandModeIdx] - 1 )          % mod ) + 2;
            mpm[3] = ( ( mpm[minCandModeIdx] + offset )     % mod ) + 2;
            mpm[4] = ( ( mpm[maxCandModeIdx] - 1 )          % mod ) + 2;
            mpm[5] = ( ( mpm[minCandModeIdx] + offset - 1 ) % mod ) + 2;
          }
          else
          {
            mpm[2] = ( ( mpm[minCandModeIdx] + offset )     % mod ) + 2;
            mpm[3] = ( ( mpm[minCandModeIdx] - 1 )          % mod ) + 2;
            mpm[4] = ( ( mpm[maxCandModeIdx] + offset )     % mod ) + 2;
            mpm[5] = ( ( mpm[maxCandModeIdx] - 1 )          % mod ) + 2;
          }
        }
        else if( leftIntraDir + aboveIntraDir > 2 ) // *** 如果左、上只有一个是角度模式**dir** (Planar, dir, dir±1, dir±2)
        {
          //mpm[0] = PLANAR_IDX;
          int angMode = leftIntraDir > DC_IDX ? leftIntraDir : aboveIntraDir;
          mpm[1] = angMode;
          mpm[2] = ( ( angMode + offset )     % mod ) + 2;
          mpm[3] = ( ( angMode - 1 )          % mod ) + 2;
          mpm[4] = ( ( angMode + offset - 1 ) % mod ) + 2;
          mpm[5] = ( ( angMode )              % mod ) + 2;
        }
      }
    }
#endif
    else  // #### 传统帧内MPM(参考行0)####
    {
     // 填充默认列表,如果左上模式相同,且不为角度模式,MPM为默认填充列表
      mpm[0] = leftIntraDir;
      mpm[1] = (mpm[0] == PLANAR_IDX) ? DC_IDX : PLANAR_IDX;
      mpm[2] = VER_IDX;
      mpm[3] = HOR_IDX;
      mpm[4] = VER_IDX - 4;
      mpm[5] = VER_IDX + 4;

      if (leftIntraDir == aboveIntraDir)
      {
        numCand = 1;
        if (leftIntraDir > DC_IDX) // 左、上相同,为角度,MPM顺序:LA,Planar,DC,LA-1,LA+1,LA-2
        {
          mpm[0] = leftIntraDir;
          mpm[1] = PLANAR_IDX;
          mpm[2] = DC_IDX;
          mpm[3] = ((leftIntraDir + offset) % mod) + 2;
          mpm[4] = ((leftIntraDir - 1) % mod) + 2;
          mpm[5] = ((leftIntraDir + offset - 1) % mod) + 2;
        }
      }
      else //L!=A  左、上不相同,
      // MPM顺序:前两个为左,上模式。后面如果L、A中有非角度模式,则填充另外一个;如果没有,则填充Planar,DC,然后填充角度偏移模式
      {
        numCand = 2;
        mpm[0] = leftIntraDir;
        mpm[1] = aboveIntraDir;
        bool maxCandModeIdx = mpm[0] > mpm[1] ? 0 : 1;

        if ((leftIntraDir > DC_IDX) && (aboveIntraDir > DC_IDX))
        {
          mpm[2] = PLANAR_IDX;
          mpm[3] = DC_IDX;
          if ((mpm[maxCandModeIdx] - mpm[!maxCandModeIdx] < 63) && (mpm[maxCandModeIdx] - mpm[!maxCandModeIdx] > 1))
          {
            mpm[4] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
            mpm[5] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
          }
          else
          {
            mpm[4] = ((mpm[maxCandModeIdx] + offset - 1) % mod) + 2;
            mpm[5] = ((mpm[maxCandModeIdx]) % mod) + 2;
          }
        }
        else if (leftIntraDir + aboveIntraDir >= 2)
        {
          mpm[2] = (mpm[!maxCandModeIdx] == PLANAR_IDX) ? DC_IDX : PLANAR_IDX;
          mpm[3] = ((mpm[maxCandModeIdx] + offset) % mod) + 2;
          mpm[4] = ((mpm[maxCandModeIdx] - 1) % mod) + 2;
          mpm[5] = ((mpm[maxCandModeIdx] + offset - 1) % 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;
  }
}
 类似资料: