H266/VVC 帧内预测中 ISP 技术

帧内子划分 ISP

  1. ISP 技术是在 JVET-2002-v3 提案中详细介绍其原理,在 VTM8 中完整展示算法。ISP是线基内预测(LIP)模式的更新版本,它改善了原始方法在编码增益和复杂度之间的权衡,ISP 算法的核心原理就是利用较近的像素点作为参考像素。
  2. 帧内子分区(ISP)根据块的大小,将亮度帧内预测块垂直或水平分割成2或4个子分区。例如,ISP的最小块大小是4x8(或8x4)。如果块大小大于4x8(或8x4),那么相应的块被4个子分区分割。
  3. 值得注意的是,M × 128(M ≤ 64)和128 × N(N ≤ 64)的 ISP 块可能会与64 × 64的 VDPU 产生潜在问题。例如,在单树情况下,一个M × 128的CU 包含一个 M x128 的亮度块 TB 和两个M/2 × 64的色度TB。如果CU 使用 ISP,那么亮度TB将被分割成四个M × 32的TB(只有水平分割是可能的),每个块都小于64 × 64。然而,在ISP色度块的当前设计中并没有被分割。因此,两个色度分量的尺寸都会大于32 × 32的块。类似地,使用ISP的128 × N的CU也可能会遇到类似的情况。因此,这两种情况对于64 × 64的 VDPU 来说是个问题。出于这个原因,能够使用ISP的 CU 尺寸被限制在最大64 × 64。图19展示了这两种可能性的例子。所有的子分区都满足至少有16个样本的条件。
  4. 在ISP中,不允许 1xN/2xN 子块预测依赖于之前解码的编码块的 1xN/2xN子 块的重建值,这样子块的最小预测宽度就变成了四个样本。例如,一个8xN(N > 4)的编码块,如果使用ISP进行垂直分割编码,将被分割成两个预测区域,每个区域的大小为4xN,并且有四个2xN的变换。此外,一个使用ISP进行垂直分割编码的4xN编码块,将使用完整的4xN块进行预测;四个1xN的变换被使用。尽管允许1xN和2xN的变换尺寸,但断言这些块在4xN区域内的变换可以并行执行。例如,当一个4xN预测区域包含四个1xN变换时,水平方向没有变换;垂直方向的变换可以作为单个4xN变换在垂直方向执行。同样地,当一个4xN预测区域包含两个2xN变换块时,两个2xN块在每个方向(水平和垂直)上的变换操作可以并行进行。因此,处理这些较小的块与处理4x4常规编码的帧内块相比,不会增加延迟。
  5. 对于每个子分区,通过将残差信号加到预测信号上来获得重建样本。这里,残差信号是通过熵解码、逆量化和逆变换等过程生成的。因此,重建样本值可用于生成下一个子分区的预测,每个子分区都会重复处理。此外,要处理的第一个子分区是包含CU左上角样本的子分区,然后继续向下(水平分割)或向右(垂直分割)。因此,用于生成子分区预测信号的参考样本仅位于行的左侧和上方。所有子分区共享相同的帧内模式。
  6. ISP 技术与其他编码工具的交互:
    • 多重参考线(MRL):如果一个块的MRL索引不是0,那么ISP编码模式将被推断为0,因此ISP模式信息不会发送到解码器。
    • 熵编码系数组大小:熵编码子块的大小已经被修改,以便在所有可能的情况下它们都有16个样本,如表3-6所示。请注意,新的大小只影响由ISP产生的块,其中一个维度小于4个样本。在所有其他情况下,系数组保持4x4的尺寸。
    • CBF编码:假设至少有一个子分区具有非零的CBF。因此,如果n是子分区的数量,前n-1个子分区产生了零CBF,那么第n个子分区的CBF被推断为1。
    • MPM使用:在ISP模式编码的块中,MPM标志将被推断为1,MPM列表被修改以排除DC模式,并优先考虑ISP水平分割的水平内模式和垂直分割的垂直内模式。
    • 变换大小限制:所有ISP变换,如果长度大于16点,使用DCT-II。
    • PDPC:当CU使用ISP编码模式时,PDPC滤波器不会应用于生成的子分区。
    • MTS标志:如果CU使用ISP编码模式,MTS CU标志将被设置为0,并且不会发送到解码器。因此,编码器不会对每个生成的子分区执行不同可用变换的RD测试。ISP模式的变换选择将根据内模式、处理顺序和使用的块大小固定和选择。因此,不需要信号。例如,让tH和tV分别是为 w×h 子分区选择的水平和垂直变换,其中w是宽度,h是高度。然后根据以下规则选择变换:
      • 如果 w=1或 h=1,则没有水平或垂直变换。
      • 如果 w=2 或 w>32,tH=DCT−II。
      • 如果 h=2 或 h>32,tV=DCT−II。
      • 否则,变换如表3-7所示选择。
  7. 在ISP模式下,允许所有67种内模式。如果相应的宽度和高度至少为4个样本,则应用PDPC。此外,内插滤波器选择的条件不再存在,并且在ISP模式下,对于分数位置插值,总是应用Cubic(DCT-IF)滤波器。
  8. 在论文 [ AN INTRA SUBPARTITION CODING MODE FOR VVC ] 中使用 VTM-3.0 官方参考代码中测试 ISP 技术的性能收益如下表:
  9. 在 VVenC 编码器中 UnitTools.cpp 文件中canUseISP 函数来判断是否可以使用 ISP 技术。
cpp 复制代码
bool CU::canUseISP( const CodingUnit &cu, const ComponentID compID )
{
  const int width     = cu.blocks[compID].width;
  const int height    = cu.blocks[compID].height;
  const int maxTrSize = cu.cs->sps->getMaxTbSize();
  return CU::canUseISP( width, height, maxTrSize );
}

bool CU::canUseISP( const int width, const int height, const int maxTrSize )
{
  bool  notEnoughSamplesToSplit = ( Log2(width) + Log2(height) <= ( MIN_TB_LOG2_SIZEY << 1 ) );
  bool  cuSizeLargerThanMaxTrSize = width > maxTrSize || height > maxTrSize;
  if ( notEnoughSamplesToSplit || cuSizeLargerThanMaxTrSize )
  {
    return false;
  }
  return true;
}
  1. 在 VVenC 编码器中 IntraSearch.cpp 文件中estIntraPredLumaQT 函数中进行 ISP 初始化和遍历模式计算开启 ISP 和不开启 ISP 的代价,选择代价最小预测方式。
cpp 复制代码
bool IntraSearch::estIntraPredLumaQT(CodingUnit &cu, Partitioner &partitioner, double bestCost)
{
  CodingStructure       &cs           = *cu.cs;
  const int             width         = partitioner.currArea().lwidth();
  const int             height        = partitioner.currArea().lheight();

  //===== loop over partitions =====

  const TempCtx ctxStart           ( m_CtxCache, m_CABACEstimator->getCtx() );

  // variables for saving fast intra modes scan results across multiple LFNST passes
  double costInterCU = xFindInterCUCost( cu );

  bool validReturn = false;

  //===== determine set of modes to be tested (using prediction signal only) =====
  int numModesAvailable = NUM_LUMA_MODE; // total number of Intra modes
  static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> RdModeList;
  static_vector<ModeInfo, FAST_UDI_MAX_RDMODE_NUM> HadModeList;
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandCostList;
  static_vector<double, FAST_UDI_MAX_RDMODE_NUM> CandHadList;

  int numModesForFullRD = g_aucIntraModeNumFast_UseMPM_2D[Log2(width) - MIN_CU_LOG2][Log2(height) - MIN_CU_LOG2];
  if (m_pcEncCfg->m_numIntraModesFullRD > 0)
    numModesForFullRD=m_pcEncCfg->m_numIntraModesFullRD;

#if INTRA_FULL_SEARCH
  numModesForFullRD = numModesAvailable;
#endif
  const SPS& sps = *cu.cs->sps;
  const bool mipAllowed = sps.MIP && cu.lwidth() <= sps.getMaxTbSize() && cu.lheight() <= sps.getMaxTbSize() && ((cu.lfnstIdx == 0) || allowLfnstWithMip(cu.lumaSize()));
  const int SizeThr     = 8 >> std::max( 0, m_pcEncCfg->m_useFastMIP - 1 );
  const bool testMip    = mipAllowed && ( cu.lwidth() <= ( SizeThr * cu.lheight() ) && cu.lheight() <= ( SizeThr * cu.lwidth() ) ) && ( cu.lwidth() <= MIP_MAX_WIDTH && cu.lheight() <= MIP_MAX_HEIGHT );
  bool testISP = sps.ISP && CU::canUseISP(width, height, cu.cs->sps->getMaxTbSize());
  if (testISP)
  {
    int numTotalPartsHor = (int)width >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_VERT_SPLIT));
    int numTotalPartsVer = (int)height >> floorLog2(CU::getISPSplitDim(width, height, TU_1D_HORZ_SPLIT));
    m_ispTestedModes[0].init(numTotalPartsHor, numTotalPartsVer, 0);
    // the total number of subpartitions is modified to take into account the cases where LFNST cannot be combined with
    // ISP due to size restrictions
    numTotalPartsHor = sps.LFNST && CU::canUseLfnstWithISP(cu.Y(), HOR_INTRA_SUBPARTITIONS) ? numTotalPartsHor : 0;
    numTotalPartsVer = sps.LFNST && CU::canUseLfnstWithISP(cu.Y(), VER_INTRA_SUBPARTITIONS) ? numTotalPartsVer : 0;
    for (int j = 1; j < NUM_LFNST_NUM_PER_SET; j++)
    {
      m_ispTestedModes[j].init(numTotalPartsHor, numTotalPartsVer, 0);
    }
    testISP = m_ispTestedModes[0].numTotalParts[0];
  }
  else
  {
    m_ispTestedModes[0].init(0, 0, 0);
  }

  xEstimateLumaRdModeList(numModesForFullRD, RdModeList, HadModeList, CandCostList, CandHadList, cu, testMip);

  CHECK( (size_t)numModesForFullRD != RdModeList.size(), "Inconsistent state!" );

  // after this point, don't use numModesForFullRD
  if( m_pcEncCfg->m_usePbIntraFast && !cs.slice->isIntra() && RdModeList.size() < numModesAvailable )
  {
    double pbintraRatio = m_pcEncCfg->m_usePbIntraFast == 1 && ( cs.area.lwidth() >= 16 && cs.area.lheight() >= 16 ) ? 1.2 : PBINTRA_RATIO;

    int maxSize = -1;
    ModeInfo bestMipMode;
    int bestMipIdx = -1;
    for( int idx = 0; idx < RdModeList.size(); idx++ )
    {
      if( RdModeList[idx].mipFlg )
      {
        bestMipMode = RdModeList[idx];
        bestMipIdx = idx;
        break;
      }
    }
    const int numHadCand = 3;
    for (int k = numHadCand - 1; k >= 0; k--)
    {
      if (CandHadList.size() < (k + 1) || CandHadList[k] > cs.interHad * pbintraRatio) { maxSize = k; }
    }
    if (maxSize > 0)
    {
      RdModeList.resize(std::min<size_t>(RdModeList.size(), maxSize));
      if( bestMipIdx >= 0 )
      {
        if( RdModeList.size() <= bestMipIdx )
        {
          RdModeList.push_back(bestMipMode);
          m_SortedPelUnitBufs->swap( maxSize, bestMipIdx );
        }
      }
    }
    if (maxSize == 0)
    {
      cs.dist = MAX_DISTORTION;
      cs.interHad = 0;
      return false;
    }
  }

  //===== check modes (using r-d costs) =====
  ModeInfo bestPUMode;

  CodingStructure *csTemp = m_pTempCS;
  CodingStructure *csBest = m_pBestCS;

  csTemp->slice   = csBest->slice   = cs.slice;
  csTemp->picture = csBest->picture = cs.picture;
  csTemp->compactResize( cu );
  csBest->compactResize( cu );
  csTemp->initStructData();
  csBest->initStructData();

  int   bestLfnstIdx  = 0;
  const bool useBDPCM = cs.picture->useBDPCM;
  int   NumBDPCMCand  = (useBDPCM && sps.BDPCM && CU::bdpcmAllowed(cu, ComponentID(partitioner.chType))) ? 2 : 0;
  int   bestbdpcmMode = 0;
  int   bestISP       = 0;
  int   bestMrl       = 0;
  bool  bestMip       = 0;
  int   EndMode       = (int)RdModeList.size();
  bool  useISPlfnst   = testISP && sps.LFNST;
  bool  noLFNST_ts    = false;
  double bestCostIsp[2] = { MAX_DOUBLE, MAX_DOUBLE };
  bool disableMTS = false;
  bool disableLFNST = false;
  bool disableDCT2test = false;
  if (m_pcEncCfg->m_FastIntraTools)
  {
    int speedIntra = 0;
    xSpeedUpIntra(bestCost, EndMode, speedIntra, cu);
    disableMTS = (speedIntra >> 2 ) & 0x1;
    disableLFNST = (speedIntra >> 1) & 0x1;
    disableDCT2test = speedIntra>>3;
    if (disableLFNST)
    {
      noLFNST_ts = true;
      useISPlfnst = false;
    }
    if (speedIntra & 0x1)
    {
      testISP = false;
    }
  }

  for (int mode_cur = 0; mode_cur < EndMode + NumBDPCMCand; mode_cur++)
  {
    int mode = mode_cur;
    if (mode_cur >= EndMode)
    {
      mode = mode_cur - EndMode ? -1 : -2;
      testISP = false;
    }
    // set CU/PU to luma prediction mode
    ModeInfo testMode;
    int noISP = 0;
    int endISP = testISP ? 2 : 0;
    bool noLFNST = false || noLFNST_ts;
    if (mode && useISPlfnst)
    {
      noLFNST |= (bestCostIsp[0] > (bestCostIsp[1] * 1.4));
      if (mode > 2)
      {
        endISP = 0;
        testISP = false;
      }
    }
    if (testISP)
    {
      xSpeedUpISP(1, testISP, mode, noISP, endISP, cu, RdModeList, bestPUMode, bestISP, bestLfnstIdx);
    }
    int startISP = 0;
    if (disableDCT2test && mode && bestISP)
    {
      startISP = endISP ? 1 : 0;
    }
    for (int ispM = startISP; ispM <= endISP; ispM++)
    {
      if (ispM && (ispM == noISP))
      {
        continue;
      }

      if (mode < 0)
      {
        cu.bdpcmM[CH_L] = -mode;
        testMode = ModeInfo(false, false, 0, NOT_INTRA_SUBPARTITIONS, cu.bdpcmM[CH_L] == 2 ? VER_IDX : HOR_IDX);
      }
      else
      {
        testMode = RdModeList[mode];
        cu.bdpcmM[CH_L] = 0;
      }

      cu.ispMode = ispM;
      cu.mipFlag = testMode.mipFlg;
      cu.mipTransposedFlag = testMode.mipTrFlg;
      cu.multiRefIdx = testMode.mRefId;
      cu.intraDir[CH_L] = testMode.modeId;
      if (cu.ispMode && xSpeedUpISP(0, testISP, mode, noISP, endISP, cu, RdModeList, bestPUMode, bestISP, 0) )
      {
        continue;
      }
      if (m_pcEncCfg->m_FastIntraTools && (cu.ispMode || sps.LFNST || sps.MTS))
      {
        m_ispTestedModes[0].intraWasTested = true;
      }
      CHECK(cu.mipFlag && cu.multiRefIdx, "Error: combination of MIP and MRL not supported");
      CHECK(cu.multiRefIdx && (cu.intraDir[0] == PLANAR_IDX), "Error: combination of MRL and Planar mode not supported");
      CHECK(cu.ispMode && cu.mipFlag, "Error: combination of ISP and MIP not supported");
      CHECK(cu.ispMode && cu.multiRefIdx, "Error: combination of ISP and MRL not supported");

      // determine residual for partition
      cs.initSubStructure(*csTemp, partitioner.chType, cs.area, true);
      int doISP = (((cu.ispMode == 0) && noLFNST) || (useISPlfnst && mode && cu.ispMode && (bestLfnstIdx == 0)) || disableLFNST) ? -mode : mode;
      xIntraCodingLumaQT(*csTemp, partitioner, m_SortedPelUnitBufs->getBufFromSortedList(mode), bestCost, doISP, disableMTS);

      DTRACE(g_trace_ctx, D_INTRA_COST, "IntraCost T [x=%d,y=%d,w=%d,h=%d] %f (%d,%d,%d,%d,%d,%d) \n", cu.blocks[0].x,
        cu.blocks[0].y, width, height, csTemp->cost, testMode.modeId, testMode.ispMod,
        cu.multiRefIdx, cu.mipFlag, cu.lfnstIdx, cu.mtsFlag);

      if (cu.ispMode && !csTemp->cus[0]->firstTU->cbf[COMP_Y])
      {
        csTemp->cost = MAX_DOUBLE;
        csTemp->costDbOffset = 0;
      }
      if (useISPlfnst)
      {
        int n = (cu.ispMode == 0) ? 0 : 1;
        bestCostIsp[n] = csTemp->cost < bestCostIsp[n] ? csTemp->cost : bestCostIsp[n];
      }

      // check r-d cost
      if (csTemp->cost < csBest->cost)
      {
        validReturn   = true;
        std::swap(csTemp, csBest);
        bestPUMode    = testMode;
        bestLfnstIdx  = csBest->cus[0]->lfnstIdx;
        bestISP       = csBest->cus[0]->ispMode;
        bestMip       = csBest->cus[0]->mipFlag;
        bestMrl       = csBest->cus[0]->multiRefIdx;
        bestbdpcmMode = cu.bdpcmM[CH_L];
        m_ispTestedModes[bestLfnstIdx].bestSplitSoFar = ISPType(bestISP);
        if (csBest->cost < bestCost)
        {
          bestCost = csBest->cost;
        }
        if ((csBest->getTU(partitioner.chType)->mtsIdx[COMP_Y] == MTS_SKIP) && ( floorLog2(csBest->getTU(partitioner.chType)->blocks[COMP_Y].area()) >= 6 ))
        {
          noLFNST_ts = 1;
        }
      }

      // reset context models
      m_CABACEstimator->getCtx() = ctxStart;

      csTemp->releaseIntermediateData();

      if (m_pcEncCfg->m_fastLocalDualTreeMode && CU::isConsIntra(cu) && !cu.slice->isIntra() && csBest->cost != MAX_DOUBLE && costInterCU != COST_UNKNOWN && mode >= 0)
      {
        if( (m_pcEncCfg->m_fastLocalDualTreeMode == 2) || (csBest->cost > costInterCU * 1.5))
        {
          //Note: only try one intra mode, which is especially useful to reduce EncT for LDB case (around 4%)
          EndMode = 0;
          break;
        }
      }
    }
  } // Mode loop

  if (m_pcEncCfg->m_FastIntraTools && (sps.ISP|| sps.LFNST || sps.MTS))
  {
    int bestMode = csBest->getTU(partitioner.chType)->mtsIdx[COMP_Y] ? 4 : 0;
    bestMode |= bestLfnstIdx ? 2 : 0;
    bestMode |= bestISP ? 1 : 0;
    m_ispTestedModes[0].bestIntraMode = bestMode;
  }
  cu.ispMode = bestISP;
  if( validReturn )
  {
    cs.useSubStructure( *csBest, partitioner.chType, TREE_D, cu.singleChan( CH_L ), true );
    const ReshapeData& reshapeData = cs.picture->reshapeData;
    if (cs.picHeader->lmcsEnabled && reshapeData.getCTUFlag())
    {
      cs.getRspRecoBuf().copyFrom(csBest->getRspRecoBuf());
    }

    //=== update PU data ====
    cu.lfnstIdx           = bestLfnstIdx;
    cu.mipTransposedFlag  = bestPUMode.mipTrFlg;
    cu.intraDir[CH_L]     = bestPUMode.modeId;
    cu.bdpcmM[CH_L]       = bestbdpcmMode;
    cu.mipFlag            = bestMip;
    cu.multiRefIdx        = bestMrl;
  }
  else
  {
    THROW("fix this");
  }

  csBest->releaseIntermediateData();

  return validReturn;
}
相关推荐
聊天QQ:4877392781 天前
探索 L4 无人车自动驾驶系统方案:无代码的蓝图魅力
视频编解码
简鹿视频1 天前
mp4视频转换成avi格式攻略
ffmpeg·音视频·视频编解码·格式工厂
小咖自动剪辑3 天前
提升电商素材剪辑效率:多场景内容自动生成流程解析
人工智能·实时互动·音视频·语音识别·视频编解码
霜雪i3 天前
PU::getAffineMergeCand
视频编解码·h.266
AndrewHZ4 天前
【ISP算法精粹】RAW域两帧输入去噪算法介绍
图像处理·算法·计算机视觉·isp·图像去噪·图像信号处理·raw域
赖small强7 天前
【音视频开发】Linux 平台图像处理与视频录制全流程指南 (Ingenic T41)
linux·图像处理·音视频·isp·视频录制
霜雪i8 天前
SbTMVP
视频编解码
屿行屿行8 天前
【Linux】音视频处理(gstreamer和ffmpeg的实际应用)
linux·ffmpeg·音视频·视频编解码
watersink8 天前
基于英伟达PyNvVideoCodec视频编解码
视频编解码
赖small强8 天前
【音视频开发】图像处理单元 (IPU) 深度技术解析指南
图像处理·音视频·isp·ipu