Combined inter and intra prediction (CIIP)

Megre模式是HEVC编码标准引入的一项帧间预测编码技术,在VVC编码标准中对Merge模式进行了扩展。包括对候选列表的扩展,增加了HMVP和 Pair-wise average candidates。编码工具新增CIIP (combined inter-intra prediction)、MMVD(merge mode with MV difference)、GEO(geometric partitioning mode)。VVC新提出的affine也存在merge模式,不过affine merge的候选推导和上面几个编码工具是不一样的。本文主要讨论CIIP技术。

1 原理

在VVC中,当一个CU在merge模式下编码时,如果CU包含至少64个亮度样本(即CU宽度乘以CU高度等于或大于64),并且如果CU宽度和CU高度都小于 128 ,可以选择使用CIIP模式,当前CU是否使用CIIP会被写入码流中。

图1

顾名思义,CIIP 就是将将帧间预测信号与帧内预测信号相结合。 CIIP模式下的帧间预测信号\(P_{inter}\)是使用与Nomal Megre模式(不是MMVD和Affine merge)相同的帧间预测过程导出的;并且帧内预测信号\(P_{intra}\)是按照Planar模式的常规帧内预测过程导出的。然后,使用加权平均来组合帧内和帧间预测信号,其中根据上方和左侧相邻块的编码模式(如图1所示)计算权重值,如下所示:

  • 如果上方相邻CU可用且预测模式为帧内预测,则将 isIntraTop 设置为 1,否则将 isIntraTop 设置为 0;
  • 如果左侧相邻CU可用且预测模式为帧内预测,则将 isIntraLeft 设置为 1,否则将 isIntraLeft 设置为 0;
  • 如果 (isIntraLeft + isIntraTop) = 2,则 wt = 3;
  • 否则,如果 (isIntraLeft + isIntraTop) = 1,则 wt = 2;
  • 否则,将 wt = 1。

CIIP加权预测公式如下:

\(P_{CIIP}=((4-wt)*P_{inter}+wt*P_{intra}+2)>>2\)

一般来说,Nomal merge有最多有6个候选,CIIP也最多有6个候选,MMVD最多有64个候选,Affine merge最多有5个候选,如果这些merge模式每个都做RDO的话,时间复杂度肯定极高,所以一般编码器的解决方案是和Intra预测类似,先使用SATD cost进行一遍粗选,选择几个最优的候选最后才做RDO,这样可以节省很多的时间。CIIP的粗选过程可以放在Nomal merge粗选的后面,只选择其中几个比较好的候选来check CIIP的候选,也可以节省一定的时间。

值得注意的是,在编码标准中,CIIP的merge是不可以判断为skip模式的,也就是说选择CIIP模式,残差系数一定不为0,遇到CIIP模式且cbf = 0可以根据需求决定是否要丢掉这个CIIP候选。

2 VTM代码

2.1 CIIP粗选

if (isIntrainterEnabled)
{
  // prepare for Intra bits calculation
  pu.ciipFlag = true;  // 先将ciip flage设置成true,用来计算bits

  // save the to-be-tested merge candidates
  uint32_t CiipMergeCand[NUM_MRG_SATD_CAND];
  for (uint32_t mergeCnt = 0; mergeCnt < std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand); mergeCnt++)
  {
    CiipMergeCand[mergeCnt] = RdModeList[mergeCnt].mergeCand;
  }
  for (uint32_t mergeCnt = 0; mergeCnt < std::min(std::min(NUM_MRG_SATD_CAND, (const int)mergeCtx.numValidMergeCand), 4); mergeCnt++)
  {
    uint32_t mergeCand = CiipMergeCand[mergeCnt];
    acMergeTmpBuffer[mergeCand] = m_acMergeTmpBuffer[mergeCand].getBuf(localUnitArea); // 复用nomal merge的插值buffer

    // estimate merge bits
    mergeCtx.setMergeInfo(pu, mergeCand); 

    // first round
    pu.intraDir[0] = PLANAR_IDX;
    uint32_t intraCnt = 0;
    // generate intrainter Y prediction
    if (mergeCnt == 0)
    {
      m_pcIntraSearch->initIntraPatternChType(*pu.cu, pu.Y()); // 获取参考像素
      m_pcIntraSearch->predIntraAng(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu); // Planar预测
      m_pcIntraSearch->switchBuffer(pu, COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt));
    }
    pu.cs->getPredBuf(pu).copyFrom(acMergeTmpBuffer[mergeCand]);
    if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
    {
      pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
    }
    m_pcIntraSearch->geneWeightedPred(COMPONENT_Y, pu.cs->getPredBuf(pu).Y(), pu, m_pcIntraSearch->getPredictorPtr2(COMPONENT_Y, intraCnt)); //加权组合

    // calculate cost
    if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
    {
      pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getInvLUT());
    }
    distParam.cur = pu.cs->getPredBuf(pu).Y();
    Distortion sadValue = distParam.distFunc(distParam); // 计算 dist
    if (pu.cs->slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
    {
      pu.cs->getPredBuf(pu).Y().rspSignal(m_pcReshape->getFwdLUT());
    }
    m_CABACEstimator->getCtx() = ctxStart;
    pu.regularMergeFlag = false;
    uint64_t fracBits = m_pcInterSearch->xCalcPuMeBits(pu); //计算 bits
    double cost = (double)sadValue + (double)fracBits * sqrtLambdaForFirstPassIntra; // 计算 cost

    insertPos = -1;
    updateCandList(ModeInfo(mergeCand, false, false, true), cost, RdModeList, candCostList, uiNumMrgSATDCand, &insertPos); // 更新候选排序列表
    if (insertPos != -1)
    {
      for (int i = int(RdModeList.size()) - 1; i > insertPos; i--)
      {
        swap(acMergeTempBuffer[i - 1], acMergeTempBuffer[i]);
      }
      swap(singleMergeTempBuffer, acMergeTempBuffer[insertPos]);
    }
  }
  pu.ciipFlag = false;
}

2.2 组合预测

这一块的代码涉及到像素的加权组合,因此可以使用SIMD来实现,VTM这里是使用C++实现的。

void IntraPrediction::geneWeightedPred(const ComponentID compId, PelBuf &pred, const PredictionUnit &pu, Pel *srcBuf)
{
  const int width = pred.width;
  CHECK(width == 2, "Width of 2 is not supported");
  const int height    = pred.height;
  const int srcStride = width;
  const int dstStride = pred.stride;

  Pel *dstBuf = pred.buf;
  int wIntra, wMerge;

  const Position posBL = pu.Y().bottomLeft();
  const Position posTR = pu.Y().topRight();
  const PredictionUnit *neigh0 = pu.cs->getPURestricted(posBL.offset(-1, 0), pu, CHANNEL_TYPE_LUMA);
  const PredictionUnit *neigh1 = pu.cs->getPURestricted(posTR.offset(0, -1), pu, CHANNEL_TYPE_LUMA);
  bool isNeigh0Intra = neigh0 && (CU::isIntra(*neigh0->cu));
  bool isNeigh1Intra = neigh1 && (CU::isIntra(*neigh1->cu));

  if (isNeigh0Intra && isNeigh1Intra)
  {
    wIntra = 3; wMerge = 1;
  }
  else
  {
    if (!isNeigh0Intra && !isNeigh1Intra)
    {
      wIntra = 1; wMerge = 3;
    }
    else
    {
      wIntra = 2; wMerge = 2;
    }
  }

  for (int y = 0; y < height; y++)
  {
    for (int x = 0; x < width; x++)
    {
      dstBuf[y*dstStride + x] = (wMerge * dstBuf[y*dstStride + x] + wIntra * srcBuf[y*srcStride + x] + 2) >> 2;
    }
  }
}

2.3 encoder

首选判断是否ciipAvailable,CIIP和GEO都不是regular merge,如果有一个是true,则需要编是否为regular merge,如果不是,则还需要编一个flag来判断是CIIP还是GEO。

const bool ciipAvailable = pu.cs->sps->getUseCiip() && !pu.cu->skip && pu.cu->lwidth() < MAX_CU_SIZE && pu.cu->lheight() < MAX_CU_SIZE && pu.cu->lwidth() * pu.cu->lheight() >= 64;
  const bool geoAvailable = pu.cu->cs->slice->getSPS()->getUseGeo() && pu.cu->cs->slice->isInterB() &&
    pu.cs->sps->getMaxNumGeoCand() > 1
                                                                    && pu.cu->lwidth() >= GEO_MIN_CU_SIZE && pu.cu->lheight() >= GEO_MIN_CU_SIZE
                                                                    && pu.cu->lwidth() <= GEO_MAX_CU_SIZE && pu.cu->lheight() <= GEO_MAX_CU_SIZE
                                                                    && pu.cu->lwidth() < 8 * pu.cu->lheight() && pu.cu->lheight() < 8 * pu.cu->lwidth();
  if (geoAvailable || ciipAvailable)
  {
    m_BinEncoder.encodeBin(pu.regularMergeFlag, Ctx::RegularMergeFlag(pu.cu->skip ? 0 : 1));
  }
  if (pu.regularMergeFlag)
  {
    if (pu.cs->sps->getUseMMVD())
    {
      m_BinEncoder.encodeBin(pu.mmvdMergeFlag, Ctx::MmvdFlag(0));
      DTRACE(g_trace_ctx, D_SYNTAX, "mmvd_merge_flag() mmvd_merge=%d pos=(%d,%d) size=%dx%d\n", pu.mmvdMergeFlag ? 1 : 0, pu.lumaPos().x, pu.lumaPos().y, pu.lumaSize().width, pu.lumaSize().height);
    }
    if (pu.mmvdMergeFlag || pu.cu->mmvdSkip)
    {
      mmvd_merge_idx(pu);
    }
    else
    {
      merge_idx(pu);
    }
  }
  else
  {
    if (geoAvailable && ciipAvailable)
    {
      Ciip_flag(pu);
    }
    merge_idx(pu);
  }

《Combined inter and intra prediction (CIIP)》有一个想法

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注