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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/BigDream123/article/details/102748428

H.266/VVC中有两个transformNxN函数,它们的参数不同,如下:

void transformNxN     ( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, std::vector<TrMode>* trModes, const int maxCand );

void transformNxN     ( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, TCoeff& uiAbsSum, const Ctx& ctx, const bool loadTr = false );

1.transformNxN函数(1)

第一个transformNxN()函数主要是通过比较DCT-2变换和TransformSkip的SAD来决定修改trModes对应模式(DCT-2和TransformSkip)是否使用。

/*
通过比较DCT2的SAD和TransformSkip的SAD决定是否使用TransFormSkip,只有尺寸小于等于32才调用
trModes数组存有多核变换的索引MTSIdx和能否进行参与后续比较(int,bool),经过本函数后修改MTSIdx对应的bool值
*/
#if JVET_O0502_ISP_CLEANUP
void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, std::vector<TrMode>* trModes, const int maxCand )
#else
void TrQuant::transformNxN( TransformUnit &tu, const ComponentID &compID, const QpParam &cQP, std::vector<TrMode>* trModes, const int maxCand, double* diagRatio, double* horVerRatio )
#endif
{
        CodingStructure &cs = *tu.cs;
  const CompArea &rect      = tu.blocks[compID];
  const uint32_t width      = rect.width;
  const uint32_t height     = rect.height;

  const CPelBuf  resiBuf    = cs.getResiBuf(rect);//残差系数

#if MAX_TB_SIZE_SIGNALLING
  CHECK( cs.sps->getMaxTbSize() < width, "Unsupported transformation size" );
#else
  CHECK( MAX_TB_SIZEY < width, "Unsupported transformation size" );
#endif

  int pos = 0;//表示trCost的第二个值,应该表示MTSIdx
  std::vector<TrCost> trCosts;//trCosts:第一个值为SAD的值,第二个值为
  std::vector<TrMode>::iterator it = trModes->begin();//it有两个值,一个代表多核变换的模式号,一个是bool值表示是否参与后续比较
  const double facBB[] = { 1.2, 1.3, 1.3, 1.4, 1.5 };//一个系数值,含义?
  while( it != trModes->end() )
  {
    //遍历trModes,获得每一个多核变换模式对应的cost
    tu.mtsIdx = it->first;//获取多核变换模式号
    CoeffBuf tempCoeff( m_mtsCoeffs[tu.mtsIdx], rect );//获取MTS对应系数
    if( tu.noResidual )//如果没有残差,跳过
    {
      int sumAbs = 0;//绝对误差和:SAD
      trCosts.push_back( TrCost( sumAbs, pos++ ) );
      it++;
      continue;
    }

    if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )//TransformSkip模式:变换跳过模式
    {
      xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );
    }
    else//Transform模式:正常变换
    {
      xT( tu, compID, resiBuf, tempCoeff, width, height );
    }

    int sumAbs = 0;//绝对误差和:SAD
    for( int pos = 0; pos < width*height; pos++ )
    {
      //这里的pos是局部变量,表示的是每一个块里面的每一个像素值,和外部pos意义不同
      sumAbs += abs( tempCoeff.buf[pos] );
    }

    double scaleSAD=1.0;
    if (isLuma(compID) && tu.mtsIdx==MTS_SKIP && ((g_aucLog2[width] + g_aucLog2[height]) & 1) == 1 )
    {
      scaleSAD=1.0/1.414213562; //补偿系数 compensate for not scaling transform skip coefficients by 1/sqrt(2)
    }
    trCosts.push_back( TrCost( int(sumAbs*scaleSAD), pos++ ) );
    it++;
  }

#if !JVET_O0502_ISP_CLEANUP
  // it gets the distribution of the DCT-II coefficients energy, which will be useful to discard ISP tests
  CoeffBuf coeffsDCT( m_mtsCoeffs[0], rect );
  xGetCoeffEnergy( tu, compID, coeffsDCT, diagRatio, horVerRatio );
#endif
  int numTests = 0;
  std::vector<TrCost>::iterator itC = trCosts.begin();
  const double fac   = facBB[g_aucLog2[std::max(width, height)]-2];//根据不同块大小获取不同系数
  const double thr   = fac * trCosts.begin()->first;//第一个SAD,带系数的(系数thr用于简化,是一种快速算法),用于其他MTS模式比较时
  const double thrTS = trCosts.begin()->first;//第一个SAD,用于TransformSkip时比较
  while( itC != trCosts.end() )//遍历变换代价值
  {
    const bool testTr = itC->first <= ( itC->second == 1 ? thrTS : thr ) && numTests <= maxCand;
    trModes->at( itC->second ).second = testTr;//修改MTS模式对应的bool值
    numTests += testTr;
    itC++;
  }
}

2.transformNxN函数(2)

第二个transformNxN()函数主要是进行对残差块进行变换。

流程如下:

  1. RDPCM ???
  2. 若RDPCM模式不是关闭状态且当前块是亮度块,则设置mtsIdx = MTS_Skip,否则执行3
  3. 若RDPCM模式为关闭状态,检查是否为Trans-quant-Bypass模式,若是则跳过变换和量化,否则执行4
  4. 若当前块是亮度块且mtsIdx=MTS_Skip,则执行xTransformSkip()函数,否则执行xT()函数进行主变换
  5. 判断是否使用二次变换,若使用二次变换,则调用xFwdLfnst()函数
  6. 调用xQuant函数进行量化。
#if JVET_O0502_ISP_CLEANUP
void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const QpParam& cQP, TCoeff& uiAbsSum, const Ctx& ctx, const bool loadTr )
#else
void TrQuant::transformNxN( TransformUnit &tu, const ComponentID &compID, const QpParam &cQP, TCoeff &uiAbsSum, const Ctx &ctx, const bool loadTr, double* diagRatio, double* horVerRatio )
#endif
{
        CodingStructure &cs = *tu.cs;
  const SPS &sps            = *cs.sps;
  const CompArea &rect      = tu.blocks[compID];
  const uint32_t uiWidth        = rect.width;
  const uint32_t uiHeight       = rect.height;

  const CPelBuf resiBuf     = cs.getResiBuf(rect);
        CoeffBuf rpcCoeff   = tu.getCoeffs(compID);

  if( tu.noResidual )//如果没有残差,直接设置cbf
  {
    uiAbsSum = 0;//存放残差系数绝对值的和;
    TU::setCbfAtDepth( tu, compID, tu.depth, uiAbsSum > 0 );
    return;
  }

  RDPCMMode rdpcmMode = RDPCM_OFF;//设置RDPCM模式
  rdpcmNxN(tu, compID, cQP, uiAbsSum, rdpcmMode);

  if( tu.cu->bdpcmMode && isLuma(compID) )
  {
    tu.mtsIdx = MTS_SKIP;
  }

  if (rdpcmMode == RDPCM_OFF)
  {
    uiAbsSum = 0;

    // transform and quantize
    if (CU::isLosslessCoded(*tu.cu))//旁路掉变换和量化,即直接把残差系数赋值给rpcCoeff;
    {
      const bool rotateResidual = TU::isNonTransformedResidualRotated( tu, compID );//是否残差旋转

      for( uint32_t y = 0; y < uiHeight; y++ )
      {
        for( uint32_t x = 0; x < uiWidth; x++ )
        {
          const Pel currentSample = resiBuf.at( x, y );

          if( rotateResidual )//存在残差旋转
          {
            rpcCoeff.at( uiWidth - x - 1, uiHeight - y - 1 ) = currentSample;
          }
          else
          {
            rpcCoeff.at( x, y ) = currentSample;
          }

          uiAbsSum += TCoeff( abs( currentSample ) );//计算出SAD的和
        }
      }
    }
    else//没有被旁路,进行变换、量化等操作
    {
#if MAX_TB_SIZE_SIGNALLING
      CHECK( cs.sps->getMaxTbSize() < uiWidth, "Unsupported transformation size" );

#else
      CHECK( MAX_TB_SIZEY < uiWidth, "Unsupported transformation size" );
#endif

      CoeffBuf tempCoeff( loadTr ? m_mtsCoeffs[tu.mtsIdx] : m_plTempCoeff, rect );

      DTRACE_PEL_BUF( D_RESIDUALS, resiBuf, tu, tu.cu->predMode, compID );

      if( !loadTr )
      {
        if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )
        {
          xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );//变换跳过模式,如果跳过变换,将残差系数经过伸缩和移位后赋值给temCoeff.buff;
        }
        else
        {
          xT( tu, compID, resiBuf, tempCoeff, uiWidth, uiHeight );//正常变换模式,tempCoeff保存经过变换后的残差系数
        }
      }

#if !JVET_O0502_ISP_CLEANUP
      //we do this only with the DCT-II coefficients
      if( isLuma(compID) &&
        !loadTr && tu.mtsIdx == MTS_DCT2_DCT2
        )
      {
        //it gets the distribution of the coefficients energy, which will be useful to discard ISP tests
        //它得到了系数能量的分布,这将有助于抛弃ISP测试
        xGetCoeffEnergy( tu, compID, tempCoeff, diagRatio, horVerRatio );
      }
#endif

      if( sps.getUseLFNST() )//进行二次变换LFNST
      {
        xFwdLfnst( tu, compID, loadTr );
      }

      DTRACE_COEFF_BUF( D_TCOEFF, tempCoeff, tu, tu.cu->predMode, compID );

      xQuant( tu, compID, tempCoeff, uiAbsSum, cQP, ctx );//量化

      DTRACE_COEFF_BUF( D_TCOEFF, tu.getCoeffs( compID ), tu, tu.cu->predMode, compID );
    }
  }

  // set coded block flag (CBF)
  TU::setCbfAtDepth (tu, compID, tu.depth, uiAbsSum > 0);
}

其中xT函数可以参考https://blog.csdn.net/BigDream123/article/details/102748739

xFwdLfnst函数可以参考https://blog.csdn.net/BigDream123/article/details/102748968

xTransformSkip函数可以参考https://blog.csdn.net/BigDream123/article/details/102748968

猜你喜欢

转载自blog.csdn.net/BigDream123/article/details/102748428