VVC帧间预测(九)帧间预测三角划分模式TPM

VTM5中支持对于大于等于8x8的CU在进行帧间预测时进行三角划分( triangle partition mode) 。三角划分模式如下,将CU沿对角线或反对角线方向划分。

当CU使用三角划分模式时,CU被均匀的划分为两个部分,每个部分分别独立的进行帧间预测但是只能进行单向帧间预测,每个部分都有一个MV和一个参考图像。

如果当前CU使用三角划分模式,则需要传输一个标志位表示划分方向(对角线或反对角线)和两个merge索引(每部分各一个)。

当每部分都完成预测之后,对角线或反对角线上的预测值有两部分自适应加权得到。得到完整的预测CU后,在整个CU上进行变换和量化操作而不是分开处理

三角划分模式不和SBT同时使用[2],当使用三角划分模式时不需要传输cu_sbt_flag 直接默认为0。

单向预测候选列表建立

对于三角划分的每个部分,其单向候选列表的建立由扩展的merge模式中候选列表得到。假设n是单向候选列表中第n个候选项的索引。单向候选列表中第n个候选项等于LX中第n个候选项,其中X等于n的奇偶位。如下图所示,第0个候选项等于L0的第0个候选项,第1个候选项等于L1的第1个候选项,第2个候选项等于L0的第2个候选项,第3个候选项等于L1的第3个候选项,第4个候选项等于L0的第4个候选项。如果LX的对应候选项不存在则用L(1-X)对应候选项代替。

对角线上预测值生成

当两个部分分别完成预测后,由于对角线上像素在每个部分都预测过一次,所以需要对对角线上的两个预测值进行加权得到最终预测值。

  • 亮度分量权值:{7/8, 6/8, 5/8, 4/8, 3/8, 2/8, 1/8}

  • 色度分量权值:{6/8, 4/8, 2/8}

其中亮度分量每行有7个像素需要加权,色度分量每行有3个像素需要加权,权值分别如上。注意:需要加权的像素可能不在块内,加权时需要去掉这部分像素。

亮度分量加权示意图如下:

相关代码如下:

void InterPrediction::xWeightedTriangleBlk( const PredictionUnit &pu, const uint32_t width, const uint32_t height, const ComponentID compIdx, const bool splitDir, PelUnitBuf& predDst, PelUnitBuf& predSrc0, PelUnitBuf& predSrc1 )
{
  Pel*    dst        = predDst .get(compIdx).buf;
  Pel*    src0       = predSrc0.get(compIdx).buf;
  Pel*    src1       = predSrc1.get(compIdx).buf;
  int32_t strideDst  = predDst .get(compIdx).stride  - width;
  int32_t strideSrc0 = predSrc0.get(compIdx).stride  - width;
  int32_t strideSrc1 = predSrc1.get(compIdx).stride  - width;

  const char    log2WeightBase    = 3;
  const ClpRng  clipRng           = pu.cu->slice->clpRngs().comp[compIdx];
  const int32_t clipbd            = clipRng.bd;
  const int32_t shiftDefault      = std::max<int>(2, (IF_INTERNAL_PREC - clipbd));
  const int32_t offsetDefault     = (1<<(shiftDefault-1)) + IF_INTERNAL_OFFS;
  const int32_t shiftWeighted     = std::max<int>(2, (IF_INTERNAL_PREC - clipbd)) + log2WeightBase;
  const int32_t offsetWeighted    = (1 << (shiftWeighted - 1)) + (IF_INTERNAL_OFFS << log2WeightBase);

  const int32_t ratioWH           = (width > height) ? (width / height) : 1;
  const int32_t ratioHW           = (width > height) ? 1 : (height / width);

#if JVET_N0671_INTRA_TPM_ALIGNWITH420
  const bool    longWeight        = (compIdx == COMPONENT_Y);
#else
  const bool    longWeight        = (compIdx == COMPONENT_Y) || ( predDst.chromaFormat == CHROMA_444 );
#endif
  const int32_t weightedLength    = longWeight ? 7 : 3;	//!<每行要加权处理的长度
        int32_t weightedStartPos  = ( splitDir == 0 ) ? ( 0 - (weightedLength >> 1) * ratioWH ) : ( width - ((weightedLength + 1) >> 1) * ratioWH ); //!<可为负
        int32_t weightedEndPos    = weightedStartPos + weightedLength * ratioWH - 1;
        int32_t weightedPosoffset =( splitDir == 0 ) ? ratioWH : -ratioWH;

        Pel     tmpPelWeighted;
        int32_t weightIdx;
        int32_t x, y, tmpX, tmpY, tmpWeightedStart, tmpWeightedEnd;

  for( y = 0; y < height; y+= ratioHW )
  {
    for( tmpY = ratioHW; tmpY > 0; tmpY-- )
    {
      for( x = 0; x < weightedStartPos; x++ )
      {//!<加权之前直接复制
        *dst++ = ClipPel( rightShift( (splitDir == 0 ? *src1 : *src0) + offsetDefault, shiftDefault), clipRng );
        src0++;
        src1++;
      }

      tmpWeightedStart = std::max((int32_t)0, weightedStartPos);
      tmpWeightedEnd   = std::min(weightedEndPos, (int32_t)(width - 1));
      weightIdx        = 1;
      if( weightedStartPos < 0 )
      {//!<起始位置可能在块外部
        weightIdx     += abs(weightedStartPos) / ratioWH;
      }
      for( x = tmpWeightedStart; x <= tmpWeightedEnd; x+= ratioWH )
      {
        for( tmpX = ratioWH; tmpX > 0; tmpX-- )
        {//!<加权处理
          tmpPelWeighted = Clip3( 1, 7, longWeight ? weightIdx : (weightIdx * 2));
          tmpPelWeighted = splitDir ? ( 8 - tmpPelWeighted ) : tmpPelWeighted;
          *dst++         = ClipPel( rightShift( (tmpPelWeighted*(*src0++) + ((8 - tmpPelWeighted) * (*src1++)) + offsetWeighted), shiftWeighted ), clipRng );
        }
        weightIdx ++;
      }

      for( x = weightedEndPos + 1; x < width; x++ )
      {//!<加权之后直接复制
        *dst++ = ClipPel( rightShift( (splitDir == 0 ? *src0 : *src1) + offsetDefault, shiftDefault ), clipRng );
        src0++;
        src1++;
      }

      dst  += strideDst;
      src0 += strideSrc0;
      src1 += strideSrc1;
    }//!<下一行加权的起始位置和结束位置右移
    weightedStartPos += weightedPosoffset;
    weightedEndPos   += weightedPosoffset;
  }
}

参考

[1] JVET-N1002

[2] JVET-N0483

感兴趣的请关注微信公众号Video Coding

发布了87 篇原创文章 · 获赞 108 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/Dillon2015/article/details/103928897
今日推荐