当前位置: 首页 > 工具软件 > EMT > 使用案例 >

H.266代码学习:xT,xTrMxN,xTrMxN_EMT函数

高增
2023-12-01

今天来学习xTxTrMxNxTrMxN_EMT函数。

在之前的 H.266代码学习:transformNxN函数 中提到JEM变换分为两次:主变换+二次NSST。transformNxN会调用xT进行主变换。

在JEM中,主变换有三种:
1.原HEVC中的DCT-2/4x4 DST。
2.多核变换。
3.KLT变换。

其中1和3的入口函数为xTrMxN,2的入口函数为xTrMxN_EMT

当然,有变换就有反变换,三者对应的反变换函数分别是:xITxITrMxNxITrMxN_EMT

xT

下面先来看xT它是主变换的入口函数。 函数体很短,比较简单。

HEVC代码学习36:xTrMxN函数中对HM中的xT进行了学习。

JEM中与HM主要区别:
在JEM中,在原HEVC中的DCT-2/4x4 DST基础上增加了多核变换和KLT变换,xT增加了多核变换的入口函数xTrMxN_EMT,而KLT变换的入口函数放在了xTrMxN中,之后会具体学习。

流程如下:
一、读取残差
二、如果变换模式ucTrIdx不是DCT2_HEVC且KLT为false时,执行1,否则执行2
1.调用xTrMxN_EMT进行多核变换。
2.调用xTrMxN进行DCT-2/4x4 DST和KLT变换。

代码分析:

/** Wrapper function between HM interface and core NxN forward transform (2D)
 *  \param channelBitDepth bit depth of channel
 *  \param useDST
 *  \param piBlkResi input data (residual)
 *  \param uiStride stride of input residual data
 *  \param psCoeff output data (transform coefficients)
 *  \param iWidth transform width
 *  \param iHeight transform height
 *  \param maxLog2TrDynamicRange
 */
//变换函数
Void TComTrQuant::xT( const Int channelBitDepth, Bool useDST, Pel* piBlkResi, UInt uiStride, TCoeff* psCoeff, Int iWidth, Int iHeight, const Int maxLog2TrDynamicRange 
#if COM16_C806_EMT
                     , UChar ucMode
                     , UChar ucTrIdx
#endif
#if VCEG_AZ08_KLT_COMMON
                     , Bool useKLT      //KLT flag
#endif
                     )
{
#if MATRIX_MULT
  if( iWidth == iHeight)
  {
#if COM16_C806_EMT
    if( ucTrIdx!=DCT2_HEVC )
    {
      xTr_EMT(channelBitDepth, piBlkResi, psCoeff, uiStride, (UInt)iWidth, useDST, maxLog2TrDynamicRange, ucMode, ucTrIdx );
    }
    else
#endif
    xTr(channelBitDepth, piBlkResi, psCoeff, uiStride, (UInt)iWidth, useDST, maxLog2TrDynamicRange);
    return;
  }
#endif

  TCoeff block[ MAX_TU_SIZE * MAX_TU_SIZE ];        
  TCoeff coeff[ MAX_TU_SIZE * MAX_TU_SIZE ];

  for (Int y = 0; y < iHeight; y++)
  {
    for (Int x = 0; x < iWidth; x++)
    {
      block[(y * iWidth) + x] = piBlkResi[(y * uiStride) + x];      //读取残差
    }
  }
#if COM16_C806_EMT
#if VCEG_AZ08_KLT_COMMON
  if( ucTrIdx!=DCT2_HEVC && useKLT == false)        //不是DCT-2且KLT为false时,使用EMT
#else
  if( ucTrIdx!=DCT2_HEVC )
#endif
  {
    xTrMxN_EMT(channelBitDepth, block, coeff, iWidth, iHeight, useDST, maxLog2TrDynamicRange, ucMode, ucTrIdx );
  }
  else  //否则使用DCT-2或KLT
#endif

#if VCEG_AZ08_KLT_COMMON
  xTrMxN( channelBitDepth, block, coeff, iWidth, iHeight, useDST, maxLog2TrDynamicRange, useKLT);
#else
  xTrMxN( channelBitDepth, block, coeff, iWidth, iHeight, useDST, maxLog2TrDynamicRange );
#endif
  memcpy(psCoeff, coeff, (iWidth * iHeight * sizeof(TCoeff)));
}

xTrMxN

HM中的xTrMxN函数学习见HEVC代码学习36:xTrMxN函数

xTrMxN是DCT-2/4x4 DST和KLT变换的入口函数。

JEM中与HM主要区别:
1.在JEM中新增了KLT变换,入口函数xKLTrxTrMxN中。
2.增加了调零操作,对变换后的残差系数宽、高大于32的部分直接取0。(调零是JEM中新增的内容,详见 H.266变换编码:高频调零的大尺寸块变换
3.增加了更多尺寸的DCT-2变换:尺寸为2fastForwardDCT2_B2,尺寸为64fastForwardDCT2_B64,尺寸为128fastForwardDCT2_B128

流程如下:
一、当useKLT为true,调用xKLTr进行KLT变换,直接返回。
二、当useKLT为false,进行原HEVC中的DCT-2/DST变换。
1.计算水平和垂直偏移。
2.设置调零宽度和高度。
3.根据宽度进行对应尺寸的水平变换,然后根据高度进行对应尺寸的垂直变换。

代码分析:

Void xTrMxN(Int bitDepth, TCoeff *block, TCoeff *coeff, Int iWidth, Int iHeight, Bool useDST, const Int maxLog2TrDynamicRange
#if VCEG_AZ08_KLT_COMMON
    , Bool useKLT       //是否使用KLT
#endif
    )
{
#if VCEG_AZ08_KLT_COMMON
    if (useKLT == true)     //使用KLT
    {
      xKLTr(bitDepth, block, coeff, iWidth);        //KLT
      return;
    }
#endif
  const Int TRANSFORM_MATRIX_SHIFT = g_transformMatrixShift[TRANSFORM_FORWARD];

#if !COM16_C806_T64
  const
#endif
#if JVET_C0024_QTBT
    //第一维水平变换偏移
    Int shift_1st = ((g_aucConvertToBit[iWidth] + MIN_CU_LOG2) +  bitDepth + TRANSFORM_MATRIX_SHIFT) - maxLog2TrDynamicRange;       
#else
    Int shift_1st = ((g_aucConvertToBit[iWidth] + 2) +  bitDepth + TRANSFORM_MATRIX_SHIFT) - maxLog2TrDynamicRange;
#endif
#if !COM16_C806_T64
  const 
#endif
#if JVET_C0024_QTBT
    //第二维垂直变换偏移
    Int shift_2nd = (g_aucConvertToBit[iHeight] + MIN_CU_LOG2) + TRANSFORM_MATRIX_SHIFT;        
#else
    Int shift_2nd = (g_aucConvertToBit[iHeight] + 2) + TRANSFORM_MATRIX_SHIFT;
#endif

#if COM16_C806_T64
#if JVET_C0024_QTBT
  if( iWidth>=64 || iWidth==2)      //宽度大于64或宽度等于2时
  {
      shift_1st += COM16_C806_TRANS_PREC;       //COM16_C806_TRANS_PREC=2
  }
  if( iHeight>=64 || iHeight==2)            //宽度大于64或宽度等于2时
  {
      shift_2nd += COM16_C806_TRANS_PREC;           //COM16_C806_TRANS_PREC=2
  }
  }
#else
// 删除默认不启用的代码
#endif
#endif

  assert(shift_1st >= 0);
  assert(shift_2nd >= 0);

  TCoeff tmp[ MAX_TU_SIZE * MAX_TU_SIZE ];
  //调零,宽度和高度大于32的部分调零。
  Int iSkipWidth = (iWidth > ZERO_OUT_TH ? iWidth-ZERO_OUT_TH : 0);     //ZERO_OUT_TH=32
  Int iSkipHeight = (iHeight > ZERO_OUT_TH ? iHeight-ZERO_OUT_TH : 0);
  switch (iWidth)       //根据宽度选择不同尺寸的水平变换  
  {
#if JVET_C0024_QTBT
#if JVET_D0077_TRANSFORM_OPT
    case 2:    fastForwardDCT2_B2( block, tmp, shift_1st, iHeight, 0, iSkipWidth, 0 );  break;
#else
    case 2:    fastForwardDCT2_B2( block, tmp, shift_1st, iHeight, 0, 0 );  break;
#endif
#endif
    case 4:
      {
        if ((iHeight == 4) && useDST)    // Check for DCT or DST    4x4的块使用DST变换
        {
           fastForwardDst( block, tmp, shift_1st );
        }
        else        //否则使用蝶形快速变换
        {
#if JVET_D0077_TRANSFORM_OPT
          partialButterfly4 ( block, tmp, shift_1st, iHeight, iSkipHeight );
#else
          partialButterfly4 ( block, tmp, shift_1st, iHeight );
#endif
        }
      }
      break;

#if JVET_D0077_TRANSFORM_OPT
    //其余尺寸块使用蝶形快速变换
    case 8:     partialButterfly8 ( block, tmp, shift_1st, iHeight, iSkipHeight );  break;
    case 16:    partialButterfly16( block, tmp, shift_1st, iHeight, iSkipHeight );  break;
    case 32:    partialButterfly32( block, tmp, shift_1st, iHeight, iSkipHeight );  break;
#else
// 删除默认不启用的代码
#endif
#if COM16_C806_T64
#if JVET_C0024_QTBT
#if JVET_D0077_TRANSFORM_OPT
    case 64:    fastForwardDCT2_B64( block, tmp, shift_1st, iHeight, 0, iSkipWidth, 0 );  break;
    case 128:   fastForwardDCT2_B128( block, tmp, shift_1st, iHeight, 0, iSkipWidth, 0 );  break;
#else
// 删除默认不启用的代码
#endif
#else
    case 64:    fastForwardDCT2_B64( block, tmp, shift_1st, iHeight, 1, 0 );  break;
#endif
#endif
    default:
      assert(0); exit (1); break;
  }

  switch (iHeight)      //根据高度选择不同尺寸的水平变换 
  {
#if JVET_C0024_QTBT
#if JVET_D0077_TRANSFORM_OPT
    case 2:    fastForwardDCT2_B2( tmp, coeff, shift_2nd, iWidth, iSkipWidth, iSkipHeight, 0 );  break;
#else
    case 2:    fastForwardDCT2_B2( tmp, coeff, shift_2nd, iWidth, 0, 0 );  break;
#endif
#endif
    case 4:
      {
        if ((iWidth == 4) && useDST)    // Check for DCT or DST  4x4的块使用DST变换
        {
          fastForwardDst( tmp, coeff, shift_2nd );
        }
        else        //否则使用蝶形快速变换
        {
#if JVET_D0077_TRANSFORM_OPT
          partialButterfly4 ( tmp, coeff, shift_2nd, iWidth, iSkipWidth );
#else
          partialButterfly4 ( tmp, coeff, shift_2nd, iWidth );
#endif
        }
      }
      break;

#if JVET_D0077_TRANSFORM_OPT
    //其余尺寸块使用蝶形快速变换
    case 8:     partialButterfly8 ( tmp, coeff, shift_2nd, iWidth, iSkipWidth );    break;
    case 16:    partialButterfly16( tmp, coeff, shift_2nd, iWidth, iSkipWidth );    break;
    case 32:    partialButterfly32( tmp, coeff, shift_2nd, iWidth, iSkipWidth );    break;
#else
// 删除默认不启用的代码
#endif
#if COM16_C806_T64
#if JVET_C0024_QTBT
#if JVET_D0077_TRANSFORM_OPT
    case 64:    fastForwardDCT2_B64( tmp, coeff, shift_2nd, iWidth, iSkipWidth, iSkipHeight, 0 );  break;
    case 128:   fastForwardDCT2_B128( tmp, coeff, shift_2nd, iWidth, iSkipWidth, iSkipHeight, 0 );  break;
#else
// 删除默认不启用的代码
#endif
#else
    case 64:    fastForwardDCT2_B64( tmp, coeff, shift_2nd, iWidth, 2, 0 );  break;
#endif
#endif
    default:
      assert(0); exit (1); break;
  }
}

xTrMxN_EMT

xTrMxN_EMT是多核变换的入口函数。
在之前的 H.266代码学习:AMT相关代码 中对多核变换的整体代码已经进行了学习,这里再来详细看一下xTrMxN_EMT

JEM中与HM主要区别:
多核变换是JEM新增内容。

流程如下:
一、计算水平、垂直偏移,设置调零的宽度和高度。
二、选择变换模式:
1.帧内且变换模式非DCT2_EMT时,根据帧内模式选择水平、垂直方向的变换模式。
2.帧间且变换模式非DCT2_EMT时,设置水平和垂直方向的变换模式。
三、进行水平、垂直变换。

代码分析:

/** MxN forward transform (2D)
*  \param bitDepth              [in]  bit depth
*  \param block                 [in]  residual block
*  \param coeff                 [out] transform coefficients
*  \param iWidth                [in]  width of transform
*  \param iHeight               [in]  height of transform
*  \param useDST                [in]
*  \param maxLog2TrDynamicRange [in]

*/

#if COM16_C806_EMT
void xTrMxN_EMT(Int bitDepth, TCoeff *block, TCoeff *coeff, Int iWidth, Int iHeight, Bool useDST, const Int maxLog2TrDynamicRange, UChar ucMode, UChar ucTrIdx )
{
  const Int TRANSFORM_MATRIX_SHIFT = g_transformMatrixShift[TRANSFORM_FORWARD];

#if JVET_C0024_QTBT
  //为保证精度,偏移在HEVC的基础上增加了2
  const Int shift_1st        = ((g_aucConvertToBit[iWidth] + MIN_CU_LOG2) +  bitDepth + TRANSFORM_MATRIX_SHIFT) - maxLog2TrDynamicRange + COM16_C806_TRANS_PREC;
  const Int shift_2nd        = (g_aucConvertToBit[iHeight] + MIN_CU_LOG2) + TRANSFORM_MATRIX_SHIFT + COM16_C806_TRANS_PREC;
  const UInt nLog2WidthMinus1 = g_aucConvertToBit[iWidth] + MIN_CU_LOG2 - 1;  //nLog2WidthMinus1, since transform start from 2-point
  const UInt nLog2HeightMinus1 = g_aucConvertToBit[iHeight] + MIN_CU_LOG2 - 1;  //nLog2HeightMinus1, since transform start from 2-point
  //调零
  Int iSkipWidth = (iWidth > ZERO_OUT_TH ? iWidth-ZERO_OUT_TH : 0);
  Int iSkipHeight = (iHeight > ZERO_OUT_TH ? iHeight-ZERO_OUT_TH : 0);
#else
// 删除默认不启用的代码
    );
#endif

  TCoeff tmp[ MAX_TU_SIZE * MAX_TU_SIZE ];

  UInt  nTrIdxHor = DCT2, nTrIdxVer = DCT2;     //水平和垂直方向默认变换模式为DCT2
  if( ucMode != INTER_MODE_IDX && ucTrIdx != DCT2_EMT )     //帧内且非DCT2_EMT
  {
    //根据帧内模式设置水平垂直方向的变换模式
    UInt  nTrSubsetHor = g_aucTrSetHorz[ucMode];
    UInt  nTrSubsetVer = g_aucTrSetVert[ucMode];
    nTrIdxHor = g_aiTrSubsetIntra[nTrSubsetHor][ucTrIdx &1];
    nTrIdxVer = g_aiTrSubsetIntra[nTrSubsetVer][ucTrIdx>>1];
  }
  if ( ucMode == INTER_MODE_IDX && ucTrIdx != DCT2_EMT )        //帧间且非DCT2_EMT
  {
    //设置帧间水平垂直方向的变换模式
    nTrIdxHor = g_aiTrSubsetInter[ucTrIdx &1];
    nTrIdxVer = g_aiTrSubsetInter[ucTrIdx>>1];
  }

#if JVET_C0024_QTBT
#if JVET_D0077_TRANSFORM_OPT
  //进行水平、垂直变换
  fastFwdTrans[nTrIdxHor][nLog2WidthMinus1]( block, tmp, shift_1st, iHeight, 0, iSkipWidth, 1 );
  fastFwdTrans[nTrIdxVer][nLog2HeightMinus1]( tmp, coeff, shift_2nd,  iWidth, iSkipWidth, iSkipHeight, 1 );
#else
// 删除默认不启用的代码
#endif
#else
// 删除默认不启用的代码
#endif
}
#endif
 类似资料: