其他分享
首页 > 其他分享> > H.266/VVC变换代码学习:xT函数

H.266/VVC变换代码学习:xT函数

作者:互联网

H.266/VVC的变换代码中,xT函数是进行主变换的函数,主要是先进行水平和垂直变换核选择然后分别进行水平和垂直变换。

基本流程如下:

  1. 通过调用getTrTypes()函数获取垂直和水平变换核类型(默认为DCT-2);
  2. 根据垂直和水平变换核类型以及宽度和高度确定SkipWidth和SkipHeight(主要是用来进行高频调零操作:对于使用DCT2的大尺寸(64)变换块进行高频调零,只保留低频系数(32);对于使用MTS的大尺寸(32)变换块进行高频调零,即只保留低频系数(16))
  3. 通过是否使用LFNST二次变换调整SkipWidth和SkipHeight(对于形状为4xN、Nx4(N > 8)的块, LFNST只应用于左上角的4x4区域;对于形状为NxN(N>8)的块,LFNST只应用于左上角的8x8块)???
  4. 根据垂直或者水平变换核类型以及尺寸分别选择对应的变换函数(DCT-2/DST7/DCT8)并分别进行变换(垂直和水平变换是分开的,即对于二维变换调用两次fastFwdTrans)

代码如下:

void TrQuant::xT( const TransformUnit &tu, const ComponentID &compID, const CPelBuf &resi, CoeffBuf &dstCoeff, const int width, const int height )
{
  const unsigned maxLog2TrDynamicRange  = tu.cs->sps->getMaxLog2TrDynamicRange( toChannelType( compID ) );//15
  const unsigned bitDepth               = tu.cs->sps->getBitDepth(              toChannelType( compID ) );
  const int      TRANSFORM_MATRIX_SHIFT = g_transformMatrixShift[TRANSFORM_FORWARD];
  const uint32_t transformWidthIndex    = g_aucLog2[width ] - 1;  // nLog2WidthMinus1, since transform start from 2-point
  const uint32_t transformHeightIndex   = g_aucLog2[height] - 1;  // nLog2HeightMinus1, since transform start from 2-point


  int trTypeHor = DCT2;
  int trTypeVer = DCT2;
  //获取水平和垂直的变换类型(根据是否是隐性MTS和ISP模式或者SBT或者显性MTS来获取)
  getTrTypes ( tu, compID, trTypeHor, trTypeVer ); 

#if !JVET_O0094_LFNST_ZERO_PRIM_COEFFS
  const int      skipWidth  = ( trTypeHor != DCT2 && width  == 32 ) ? 16 : width  > JVET_C0024_ZERO_OUT_TH ? width  - JVET_C0024_ZERO_OUT_TH : 0;
  const int      skipHeight = ( trTypeVer != DCT2 && height == 32 ) ? 16 : height > JVET_C0024_ZERO_OUT_TH ? height - JVET_C0024_ZERO_OUT_TH : 0;
#else
  //对于使用DCT2的大尺寸(64x64)变换块进行高频调零
  //对于使用MTS的大尺寸(32x32)变换块进行高频调零,即只保留16x16低频系数
  int  skipWidth  = ( trTypeHor != DCT2 && width  == 32 ) ? 16 : width  > JVET_C0024_ZERO_OUT_TH ? width  - JVET_C0024_ZERO_OUT_TH : 0;
  int  skipHeight = ( trTypeVer != DCT2 && height == 32 ) ? 16 : height > JVET_C0024_ZERO_OUT_TH ? height - JVET_C0024_ZERO_OUT_TH : 0;
  
  if( tu.cs->sps->getUseLFNST() && tu.cu->lfnstIdx )
  {
    //如果使用LFNST则只对部分块进行变换
    //对于形状为4xN、Nx4和N > 8的块,建议的限制意味着LFNST现在只应用一次,而只应用于左上角的4x4区域,即将第一4x4子组外的所有主系数都归零
    if( (width == 4 && height > 4) || (width > 4 && height == 4) )
    {
      skipWidth  = width  - 4;
      skipHeight = height - 4;
    }
    else if( (width >= 8 && height >= 8) )
    {
      skipWidth  = width  - 8;
      skipHeight = height - 8;
    }
  }
#endif

#if RExt__DECODER_DEBUG_TOOL_STATISTICS
  if ( trTypeHor != DCT2 )
  {
    CodingStatistics::IncrementStatisticTool( CodingStatisticsClassType{ STATS__TOOL_EMT, uint32_t( width ), uint32_t( height ), compID } );
  }
#endif
  //__declspec(align(nBytes))
  ALIGN_DATA( MEMORY_ALIGN_DEF_SIZE, TCoeff block[MAX_TB_SIZEY * MAX_TB_SIZEY] );

  const Pel *resiBuf    = resi.buf; //像素矩阵
  const int  resiStride = resi.stride; //每行的使用的像素数目

  for( int y = 0; y < height; y++ )
  {
    for( int x = 0; x < width; x++ )
    {
      block[( y * width ) + x] = resiBuf[( y * resiStride ) + x];//将像素矩阵赋值给block矩阵
    }
  }

  if( width > 1 && height > 1 ) // 2-D transform
  {
    //TRANSFORM_MATRIX_SHIFT=6, COM16_C806_TRANS_PREC=0
    const int      shift_1st              = ((g_aucLog2[width ]) + bitDepth + TRANSFORM_MATRIX_SHIFT) - maxLog2TrDynamicRange + COM16_C806_TRANS_PREC;
    const int      shift_2nd              =  (g_aucLog2[height])            + TRANSFORM_MATRIX_SHIFT                          + COM16_C806_TRANS_PREC;
    CHECK( shift_1st < 0, "Negative shift" );
    CHECK( shift_2nd < 0, "Negative shift" );
  TCoeff *tmp = ( TCoeff * ) alloca( width * height * sizeof( TCoeff ) );   
  //fastFwdTrans函数是一个DCT2、DST7、DCT8的3x6的函数数组,第一行代表DCT2(2/4/8/16/32/64)第二行代表DCT8(null/4/8/16/32/null)第三行代表DST7(null/4/8/16/32/null)
  fastFwdTrans[trTypeHor][transformWidthIndex ](block,        tmp, shift_1st, height,        0, skipWidth);//horizontal transform
  fastFwdTrans[trTypeVer][transformHeightIndex](tmp, dstCoeff.buf, shift_2nd, width, skipWidth, skipHeight);//vertical transform
  }
  else if( height == 1 ) //1-D horizontal transform
  {
    const int      shift              = ((g_aucLog2[width ]) + bitDepth + TRANSFORM_MATRIX_SHIFT) - maxLog2TrDynamicRange + COM16_C806_TRANS_PREC;
    CHECK( shift < 0, "Negative shift" );
    CHECKD( ( transformWidthIndex < 0 ), "There is a problem with the width." );
    fastFwdTrans[trTypeHor][transformWidthIndex]( block, dstCoeff.buf, shift, 1, 0, skipWidth );
  }
  else //if (iWidth == 1) //1-D vertical transform
  {
    int shift = ( ( g_aucLog2[height] ) + bitDepth + TRANSFORM_MATRIX_SHIFT ) - maxLog2TrDynamicRange + COM16_C806_TRANS_PREC;
    CHECK( shift < 0, "Negative shift" );
    CHECKD( ( transformHeightIndex < 0 ), "There is a problem with the height." );
    fastFwdTrans[trTypeVer][transformHeightIndex]( block, dstCoeff.buf, shift, 1, 0, skipHeight );
  }
}

其中,fastFwdTrans是一种二维函数数组,它的定义如下:

FwdTrans *fastFwdTrans[NUM_TRANS_TYPE][g_numTransformMatrixSizes] =
{
  { fastForwardDCT2_B2, fastForwardDCT2_B4, fastForwardDCT2_B8, fastForwardDCT2_B16, fastForwardDCT2_B32, fastForwardDCT2_B64 },
  { nullptr,            fastForwardDCT8_B4, fastForwardDCT8_B8, fastForwardDCT8_B16, fastForwardDCT8_B32, nullptr },
  { nullptr,            fastForwardDST7_B4, fastForwardDST7_B8, fastForwardDST7_B16, fastForwardDST7_B32, nullptr },
};

第一个索引是变换类型(DCT-2、DCT-8、DST-7),第二个索引是相应的变换块尺寸。

函数getTrTypes()是用于获取水平和垂直的变换核类型,主要是根据是否是显性MTS、隐性MTS、ISP模式、MIP模式、SBT来选择变换核,其中MTS、SBT变换核选择原理参考:https://blog.csdn.net/BigDream123/article/details/102599000

代码如下:

void TrQuant::getTrTypes(const TransformUnit tu, const ComponentID compID, int &trTypeHor, int &trTypeVer)
{
  const bool isExplicitMTS = (CU::isIntra(*tu.cu) ? tu.cs->sps->getUseIntraMTS() : tu.cs->sps->getUseInterMTS() && CU::isInter(*tu.cu)) && isLuma(compID);
#if JVET_O0529_IMPLICIT_MTS_HARMONIZE
  const bool isImplicitMTS = CU::isIntra(*tu.cu) && tu.cs->sps->getUseImplicitMTS() && isLuma(compID) && tu.cu->lfnstIdx == 0 && tu.cu->mipFlag == 0;
#else
  const bool isImplicitMTS = CU::isIntra(*tu.cu) && tu.cs->sps->getUseImplicitMTS() && isLuma(compID);
#endif
  const bool isISP = CU::isIntra(*tu.cu) && tu.cu->ispMode && isLuma(compID);
  const bool isSBT = CU::isInter(*tu.cu) && tu.cu->sbtInfo && isLuma(compID);//对于SBT仅亮度块的转换核与位置有关,色度块都用DCT-2

  trTypeHor = DCT2;
  trTypeVer = DCT2;

#if JVET_O0538_SPS_CONTROL_ISP_SBT
  if (!tu.cs->sps->getUseMTS())
    return;
#endif

  if (isImplicitMTS || isISP)
  {
    int  width = tu.blocks[compID].width;
    int  height = tu.blocks[compID].height;
    bool widthDstOk = width >= 4 && width <= 16;
    bool heightDstOk = height >= 4 && height <= 16;

    if (widthDstOk)
      trTypeHor = DST7;
    if (heightDstOk)
      trTypeVer = DST7;
    return;
  }


  if (isSBT)
  {
    uint8_t sbtIdx = tu.cu->getSbtIdx();
    uint8_t sbtPos = tu.cu->getSbtPos();

    if( sbtIdx == SBT_VER_HALF || sbtIdx == SBT_VER_QUAD )
    {
      assert( tu.lwidth() <= MTS_INTER_MAX_CU_SIZE ); //垂直分割时如果宽度大于32则出错
      if( tu.lheight() > MTS_INTER_MAX_CU_SIZE )
      {
        trTypeHor = trTypeVer = DCT2;//若残差的一侧大于32则两个维度变换均设为DCT-2
      }
      else
      {
        if( sbtPos == SBT_POS0 )  { trTypeHor = DCT8;  trTypeVer = DST7; }
        else                      { trTypeHor = DST7;  trTypeVer = DST7; }
      }
    }
    else
    {
      assert( tu.lheight() <= MTS_INTER_MAX_CU_SIZE ); //水平分割时如果高度大于32则出错
      if( tu.lwidth() > MTS_INTER_MAX_CU_SIZE )
      {
        trTypeHor = trTypeVer = DCT2;////若残差的一侧大于32则两个维度变换均设为DCT-2
      }
      else
      {
        if( sbtPos == SBT_POS0 )  { trTypeHor = DST7;  trTypeVer = DCT8; }
        else                      { trTypeHor = DST7;  trTypeVer = DST7; }
      }
    }
    return;
  }

  if (isExplicitMTS)
  {
    if (tu.mtsIdx > MTS_SKIP)
    {
      int indHor = (tu.mtsIdx - MTS_DST7_DST7) & 1;
      int indVer = (tu.mtsIdx - MTS_DST7_DST7) >> 1;

      trTypeHor = indHor ? DCT8 : DST7;
      trTypeVer = indVer ? DCT8 : DST7;
    }
  }
}

 

标签:H.266,const,int,tu,height,width,&&,VVC,xT
来源: https://blog.csdn.net/BigDream123/article/details/102748739