H.266/VVC-VTM代码学习-帧内预测13-ISP模式在estIntraPredLumaQT中的设定(1)

H.266/VVC专栏传送

上一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数
下一篇:H.266/VVC-VTM代码学习-帧内预测14-ISP模式在estIntraPredLumaQT中的设定(2)

前言

VTM是H.266/VVC视频编码标准的参考软件,研究VTM代码给研究人员解释了VVC编码标准的详细标准规范与细节。

本文是笔者对VTM代码的一点学习记录,成文于笔者刚开始接触VVC期间,期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大视频编码学习者沟通交流、共同进步。

VTM代码的下载及编译请参考博文:
【视频编码学习】H.266/VVC参考软件VTM配置运行(VTM-6.0版本)

本文涉及的代码主要存在于工程下的Lib\EncoderLib\IntraSearch.cpp文件中。

一、编码端确定ISP模式的相关代码

1.estIntraPredLumaQT函数中确定是否测试ISP
  //是否可用ISP(SPS确定使用ISP,且MTSFLAG为0(当前正在检查DCT-2变换),且lfnstIdx == 0(没有检查LFNST),且CU可以使用ISP(宽和高相乘要不小于16)
  bool ispCanBeUsed   = sps.getUseISP() && cu.mtsFlag == 0 && cu.lfnstIdx == 0 && CU::canUseISP(width, height, cu.cs->sps->getMaxTbSize());
  //为ISP保存数据(使用ISP,且不启用ACT变换或者是第一种颜色空间)
  bool saveDataForISP = ispCanBeUsed && (!colorTransformIsEnabled || isFirstColorSpace);
  //测试ISP(使用ISP,且不启用ACT变换)
  bool testISP        = ispCanBeUsed && (!colorTransformIsEnabled || !cu.colorTransform);

① 只有当SPS中指定使用ISP,且mtsflag为0(即当前正在检查一次变换为DCT-2变换),且LFNST索引为0(即不使用LFNST),且CU可以使用ISP(当前块w*h>16,且尺寸不大于TU最大尺寸)时才能使用ISP。

② ISP可以使用,且不开启ACT变换或当前检查first color space则为ISP保存数据。

③ ISP可以使用,且不开启ACT变换或当前块不使用ACT变换时测试ISP。

2.estIntraPredLumaQT函数中初始化参数时确定ISP划分数量
//若测试ISP
  if( testISP )
  {
    //reset the variables used for the tests
    //重置ISP测试需要用到的变量
    //用于存放选择的常规帧内模式
    m_regIntraRDListWithCosts.clear();
    //水平方向分块的总数
    int numTotalPartsHor = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
    //垂直方向分块的总数
    int numTotalPartsVer = (int)width  >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));
    //初始化m_ispTestedModes[0],即不使用LFNST时的ISP
    //载入分块情况
    m_ispTestedModes[0].init( numTotalPartsHor, numTotalPartsVer );
    //the total number of subpartitions is modified to take into account the cases where LFNST cannot be combined with ISP due to size restrictions
    //考虑到由于尺寸限制无法将LFNST与ISP组合的情况,修改子分区的总数
    //即分块后块的尺寸应大于等于4
    //水平分区数
    numTotalPartsHor = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
    //垂直分区数
    numTotalPartsVer = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;
    //NUM_LFNST_NUM_PER_SET = 3
    //初始化m_ispTestedModes[1]和m_ispTestedModes[2],即使用LFNST时的ISP
    //载入分块情况
    for (int j = 1; j < NUM_LFNST_NUM_PER_SET; j++)
    {
      m_ispTestedModes[j].init(numTotalPartsHor, numTotalPartsVer);
    }
  }

代码中分别对不使用LFNST和使用LFNST的情况进行了区别处理(lfnstflag==0 or lfnstflag!=0)

① lfnstindex == 0,即不使用LFNST时:
    //水平方向分块的总数
    int numTotalPartsHor = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
    //垂直方向分块的总数
    int numTotalPartsVer = (int)width  >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));

代码中确定水平和垂直分区数的过程是先通过getISPSplitDim获得分块在对应维度上的尺寸,再用高或宽除以分块在对应维度的尺寸得到对应维度的分块数量。

其中,getISPSplitDim函数获得分块在对应维度上的尺寸。

uint32_t CU::getISPSplitDim( const int width, const int height, const PartSplit ispType )
{
  //是否TU划分成多行
  bool divideTuInRows = ispType == TU_1D_HORZ_SPLIT;
  uint32_t splitDimensionSize, nonSplitDimensionSize, partitionSize, divShift = 2;

  //划分成多行
  if( divideTuInRows )
  {
    //划分方向的尺寸
    splitDimensionSize    = height;
    //不划分方向的尺寸
    nonSplitDimensionSize = width;
  }
  //划分成多列
  else
  {
    //划分方向的尺寸
    splitDimensionSize    = width;
    //不划分方向的尺寸
    nonSplitDimensionSize = height;
  }

  //CU最小采样点数为16
  const int minNumberOfSamplesPerCu = 1 << ( ( floorLog2(MIN_TB_SIZEY) << 1 ) );
  //不划分侧尺寸小于16时为 16/不划分侧尺寸 ,否则为1
  //此变量为满足最小采样点数的最少划分方向尺寸
  const int factorToMinSamples = nonSplitDimensionSize < minNumberOfSamplesPerCu ? minNumberOfSamplesPerCu >> floorLog2(nonSplitDimensionSize) : 1;
  //分块尺寸选择 splitDimensionSize/4(划分方向划分为4份) 和 factorToMinSamples 中较大者
  //即最多在划分方向划分四份
  partitionSize = ( splitDimensionSize >> divShift ) < factorToMinSamples ? factorToMinSamples : ( splitDimensionSize >> divShift );
  
  CHECK( floorLog2(partitionSize) + floorLog2(nonSplitDimensionSize) < floorLog2(minNumberOfSamplesPerCu), "A partition has less than the minimum amount of samples!" );
  return partitionSize;
}

由getISPSplitDim函数可见,ISP分块最多分4块,且必须保证每个分块的采样点数至少为16

② lfnstindex != 0,即使用LFNST时:
    //水平分区数
    numTotalPartsHor = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
    //垂直分区数
    numTotalPartsVer = sps.getUseLFNST() && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;

使用LFNST时,需要重新判断ISP分块后的子块是否满足LFNST的尺寸要求,若使用LFNST且在ISP后满足LFNST的尺寸要求,则此时的ISP分块与不使用LFNST时相同。否则,此时ISP分块为0。

canUseLfnstWithISP用于判断ISP分块后子块是否满足LFNST尺寸要求。

bool CU::canUseLfnstWithISP( const CompArea& cuArea, const ISPType ispSplitType )
{
  if( ispSplitType == NOT_INTRA_SUBPARTITIONS )
  {
    return false;
  }
  //ISP后子块尺寸
  Size tuSize = ( ispSplitType == HOR_INTRA_SUBPARTITIONS ) ? Size( cuArea.width, CU::getISPSplitDim( cuArea.width, cuArea.height, TU_1D_HORZ_SPLIT ) ) :
    Size( CU::getISPSplitDim( cuArea.width, cuArea.height, TU_1D_VERT_SPLIT ), cuArea.height );

  //若分块后不满足宽高均大于4,则不可使用LFNST
  if( !( tuSize.width >= MIN_TB_SIZEY && tuSize.height >= MIN_TB_SIZEY ) )
  {
    return false;
  }
  return true;
}

由canUseLfnstWithISP函数可见,若ISP分区后宽、高均大于4,则满足LFNST条件,将对应分区载入lfnstindex为1和2的情况。

3.estIntraPredLumaQT函数中第二轮角度模式选择后为ISP保存模式候选
            //为之后测试ISP保存粗选出的传统帧内模式
            if (saveDataForISP)
            {
              // we save the regular intra modes list
              //保存常规帧内模式列表
              m_ispCandListHor = uiRdModeList;
            }

编码端第二轮角度模式检查后,若为ISP保存数据的标志位有效,则保存候选模式列表到ISP候选模式列表中。

4.estIntraPredLumaQT函数中在fastUDI对MPM的检查时将存在于MPM中的模式保存
            //为ISP保存数据
            if (saveDataForISP)
            {
              //we add the MPMs to the list that contains only regular intra modes
              //将MPMs添加到仅包含常规帧内模式的列表中
              //循环遍历MPM
              for (int j = 0; j < numCand; j++)
              {
                bool     mostProbableModeIncluded = false;
                ModeInfo mostProbableMode(false, false, 0, NOT_INTRA_SUBPARTITIONS, uiPreds[j]);

                //遍历候选列表检查最可能模式是否包含
                for (int i = 0; i < m_ispCandListHor.size(); i++)
                {
                  mostProbableModeIncluded |= (mostProbableMode == m_ispCandListHor[i]);
                }
                //若当前最可能模式未被候选列表包含,则添加到候选列表中
                if (!mostProbableModeIncluded)
                {
                  m_ispCandListHor.push_back(mostProbableMode);
                }
              }
            }
5.estIntraPredLumaQT函数中在细选RDcost前根据HAD候选列表长度对ISP候选模式长度缩减
          if (saveDataForISP)//如果为ISP保存数据
          {
            //ISP候选列表长度设置为原长度和Had列表最大尺寸中的较小值
            m_ispCandListHor.resize(std::min<size_t>(m_ispCandListHor.size(), maxSize));
          }
6.estIntraPredLumaQT函数中在细选RDcost前记录非ISP模式数量并为ISP模式在候选模式列表预留位置
    //非ISP模式的数量
    int numNonISPModes = (int)uiRdModeList.size();

    //若测试ISP
    if ( testISP )
    {
      // we reserve positions for ISP in the common full RD list
      //为ISP在通用的完整RD列表中保留位置
      //ISP在RD列表中的最大模式数量(使用LFNST时:16*3,不使用LFNST:16)
      const int maxNumRDModesISP = sps.getUseLFNST() ? 16 * NUM_LFNST_NUM_PER_SET : 16;
      m_curIspLfnstIdx = 0;
      //将ISP模式预留加入候选模式列表
      for (int i = 0; i < maxNumRDModesISP; i++)
      {
        uiRdModeList.push_back( ModeInfo( false, false, 0, INTRA_SUBPARTITIONS_RESERVED, 0 ) );
      }
    }

上一篇:H.266/VVC-VTM代码学习-帧内预测12-编码端获取MPM列表getIntraMPMs函数
下一篇:H.266/VVC-VTM代码学习-帧内预测14-ISP模式在estIntraPredLumaQT中的设定(2)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值