【VTM10.0】帧内之ISP技术

ISP(Intra Sub-Partitions,帧内子区域划分)

依据CU的亮度块尺寸,将其沿水平或垂直方向划分成2个或者4个尺寸相同的子块,然后逐个子区域进行预测和重建。

进行帧内ISP的最小CU尺寸为4 x 8或8 x 4(即4 x 4的CU不使用ISP),4 x 8或8 x 4的CU会被分成2个子区域,其他尺寸则被分成4个子区域,即每个子区域最少需要有16个像素。
在这里插入图片描述
在这里插入图片描述

代码意思见注释

void IntraPrediction::initIntraPatternChTypeISP(const CodingUnit& cu, const CompArea& area, PelBuf& recBuf, const bool forceRefFilterFlag)
{
  const CodingStructure& cs = *cu.cs;

  if (!forceRefFilterFlag)
  {//帧内预测参数初始化
    initPredIntraParams(*cu.firstPU, area, *cs.sps);
  }

  const Position posLT = area;
  //左侧CU可用,且已解码
  bool           isLeftAvail  = (cs.getCURestricted(posLT.offset(-1, 0), cu, CHANNEL_TYPE_LUMA) != NULL) && cs.isDecomp(posLT.offset(-1, 0), CHANNEL_TYPE_LUMA);
  //上侧CU可用,且已解码
  bool           isAboveAvail = (cs.getCURestricted(posLT.offset(0, -1), cu, CHANNEL_TYPE_LUMA) != NULL) && cs.isDecomp(posLT.offset(0, -1), CHANNEL_TYPE_LUMA);
  // ----- Step 1: unfiltered reference samples -----
  if (cu.blocks[area.compID].x == area.x && cu.blocks[area.compID].y == area.y)
  {//如果当前cu的起始位置与area的起始位置相同,也就是该子块是CU中的第一个块
    Pel *refBufUnfiltered = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];//当前块的未滤波参考缓存
    // With the first subpartition all the CU reference samples are fetched at once in a single call to xFillReferenceSamples
    if (cu.ispMode == HOR_INTRA_SUBPARTITIONS)
    {//如果是水平划分的,那左边的参考像素长度就是cu长度乘2,上边参考像素的长度就是当前Cu的宽然后加上这个area的宽
      m_leftRefLength = cu.Y().height << 1;
      m_topRefLength = cu.Y().width + area.width;//在ISP水平划分模式下,area子块的跨百度好像就是cu的宽,所以这里是2倍的cu宽
    }
    else //if (cu.ispMode == VER_INTRA_SUBPARTITIONS)
    {//垂直划分
      m_leftRefLength = cu.Y().height + area.height;
      m_topRefLength = cu.Y().width << 1;
    }

    //调用xFillReferenceSamples,得到参考像素,该CU中其他的子块就不用再调用了。
    xFillReferenceSamples(cs.picture->getRecoBuf(cu.Y()), refBufUnfiltered, cu.Y(), cu);
    //xFillReferenceSamples函数结束时,已经把所有的参考像素放进了refBufUnfiltered中。

    // After having retrieved all the CU reference samples, the number of reference samples is now adjusted for the current subpartition
   //检索完所有的CU参考采样点后,现在针对当前子块调整参考采样点的数量
    //划分侧参考长度为 CU在对应侧尺寸 + 预测区域对应侧尺寸
    //不划分测参考长度为 2*CU在对应侧尺寸
    m_topRefLength = cu.blocks[area.compID].width + area.width;
    m_leftRefLength = cu.blocks[area.compID].height + area.height;
  }
  else
  {//如果当前cu的其实位置与area的起始位置不同
  
 //左侧和上侧的参考长度都为CUsize + areasize
    m_topRefLength = cu.blocks[area.compID].width + area.width;
    m_leftRefLength = cu.blocks[area.compID].height + area.height;

    const int predSizeHor = m_topRefLength;
    const int predSizeVer = m_leftRefLength;
    if (cu.ispMode == HOR_INTRA_SUBPARTITIONS)
    {
    //src中存放当前area上一行的第一个像素
      Pel* src = recBuf.bufAt(0, -1);

      //水平方向分割,不是第一次调用时,参考像素要加上m_refBufferStride[area.compID]
      //m_refBufferStride[area.compID]在xFillReferenceSamples中赋值,m_refBufferStride[area.compID] = predStride;
      //const int predStride = predSize + 1 + multiRefIdx;
      //const int  predSize  = m_topRefLength;
      Pel *ref = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + m_refBufferStride[area.compID];
      if (isLeftAvail)
      {
        for (int i = 0; i <= 2 * cu.blocks[area.compID].height - area.height; i++)
        {//因为之前已经得到了所有的参考像素,所以这里就只加上area的高,就可以得到它的左侧第一个参考像素
          ref[i] = ref[i + area.height];
        }
      }
      else
      {//如果左侧不可用,则参考像素等于上方子块(这个块上一行的第一个点)的重建值,recBuf.bufAt(0, -1)
        for (int i = 0; i <= predSizeVer; i++)
        {
          ref[i] = src[0];
        }
      }

      //上方参考
      Pel *dst = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + 1;
      dst[-1]  = ref[0];
      for (int i = 0; i < area.width; i++)
      {//使用上面一个子块的重建填充area的宽度的参考像素
        dst[i] = src[i];
      }
      Pel sample = src[area.width - 1];
      dst += area.width;
      for (int i = 0; i < predSizeHor - area.width; i++)
      {//上方右侧的参考像素由上方最后一个点填充
        dst[i] = sample;
      }
    }
    else//垂直分割
    {
      Pel* src = recBuf.bufAt(-1, 0);
      Pel *ref = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
      if (isAboveAvail)
      {
        for (int i = 0; i <= 2 * cu.blocks[area.compID].width - area.width; i++)
        {
          ref[i] = ref[i + area.width];
        }
      }
      else
      {
        for (int i = 0; i <= predSizeHor; i++)
        {
          ref[i] = src[0];
        }
      }
      Pel *dst = m_refBuffer[area.compID][PRED_BUF_UNFILTERED] + m_refBufferStride[area.compID] + 1;
      dst[-1]  = ref[0];
      for (int i = 0; i < area.height; i++)
      {
        *dst = *src;
        src += recBuf.stride;
        dst++;
      }
      Pel sample = src[-recBuf.stride];
      for (int i = 0; i < predSizeVer - area.height; i++)
      {
        *dst = sample;
        dst++;
      }
    }
  }
  // ----- Step 2: filtered reference samples -----进行参考像素的滤波
  if (m_ipaParam.refFilterFlag || forceRefFilterFlag)
  {
    Pel *refBufUnfiltered = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
    Pel *refBufFiltered   = m_refBuffer[area.compID][PRED_BUF_FILTERED];
    xFilterReferenceSamples(refBufUnfiltered, refBufFiltered, area, *cs.sps, cu.firstPU->multiRefIdx);
  }
}

猜你喜欢

转载自blog.csdn.net/zzy_pphz/article/details/116541535