VVC/VTM:代码学习——环路滤波ALF详细过程

Adaptive Loop Filter (ALF)

ALF原理介绍见博文:https://blog.csdn.net/baidu_28446365/article/details/89511554

ALF在解码过程中分为以下几个部分

  1. 解析Slice级别的ALF标志位等参数
  2. 解析Slice级别的滤波器参数(Filter Coefficient)
  3. ALF滤波过程

为了便于理解,本文主要从解码端操作过程进行详解

1、解析Slice级别的ALF标志位等参数

首先解析亮度分量和色度分量的ALF Flag、滤波器个数以及每个class对应的FilterIdx,主体函数void HLSyntaxReader::parseAPS(APS* aps)

//解析Slice Header中的参数
void HLSyntaxReader::parseAPS(APS* aps)
{
#if ENABLE_TRACING
  xTraceAPSHeader();
#endif

  uint32_t  code;

  READ_CODE(5, code, "adaptation_parameter_set_id");
  aps->setAPSId(code);
//获取ALF相关的参数
  AlfSliceParam param = aps->getAlfAPSParam();
  param.enabledFlag[COMPONENT_Y] = true;//亮度分量是否ALF的标志
//获取色度分量Cb和Cr是否使用ALF的标志位
  int alfChromaIdc = truncatedUnaryEqProb(3);        //alf_chroma_idc
  param.enabledFlag[COMPONENT_Cb] = alfChromaIdc >> 1;
  param.enabledFlag[COMPONENT_Cr] = alfChromaIdc & 1;

//解析滤波器种类数numLumaFilters ,最多MAX_NUM_ALF_CLASSES=25种,此处为何存在滤波器种类少于25的情况,原因在于Filter Merge技术,之后再进行介绍
  xReadTruncBinCode(code, MAX_NUM_ALF_CLASSES);  //number_of_filters_minus1
  param.numLumaFilters = code + 1;
  if (param.numLumaFilters > 1)
  {
    for (int i = 0; i < MAX_NUM_ALF_CLASSES; i++)
    {
    //解析每个种类(根据梯度和activity将4x4小块分为25个class)所对应的滤波器索引FilterIdx
      xReadTruncBinCode(code, param.numLumaFilters);
      //filterCoeffDeltaIdx[25]:25种class对应的FilterIdx
      param.filterCoeffDeltaIdx[i] = code;
    }
  }
  else
  {
  //如果只有一种滤波器,则所有4x4小块使用同一种滤波器,直接将filterCoeffDeltaIdx全部置为0,无需解析每个class对应的FilterIdx
    memset(param.filterCoeffDeltaIdx, 0, sizeof(param.filterCoeffDeltaIdx));
  }

  alfFilter(param, false);//解析滤波器参数的主体函数,解析亮度分量的滤波器参数

  if (alfChromaIdc)
  {
  //解析滤波器参数的主体函数,解析色度分量的滤波器参数
    alfFilter(param, true);
  }
  aps->setAlfAPSParam(param);

  xReadRbspTrailingBits();
}

2、解析Slice级别的滤波器参数(Filter Coefficient)

25个ALF滤波器的参数存储在Slice级别,所以,先解析Slice级别的滤波器参数(filter coeff),解析滤波器参数的主体函数为void HLSyntaxReader::alfFilter()
关键点包括

  1. alfLumaCoeffDeltaPredictionFlag :亮度分量的滤波器之间的Filter Coeff可使用DPCM编码方式;
  2. alfLumaCoeffDeltaFlag :用总标识位标识是否每个FIlter都传输了参数;
  3. alfLumaCoeffFlag:每个滤波器单独一个Flag标识是否传输了参数;
  4. 滤波器参数用k阶指数哥伦布码进行熵编码;
void HLSyntaxReader::alfFilter( AlfSliceParam& alfSliceParam, const bool isChroma )
{
  uint32_t code;
  if( !isChroma )
  {
  //alfLumaCoeffDeltaFlag :numLumaFilters 个是否都传输了Filter Coeff(意思是有些滤波器没有传输参数)
    READ_FLAG( code, "alf_luma_coeff_delta_flag" );
    alfSliceParam.alfLumaCoeffDeltaFlag = code;

    if( !alfSliceParam.alfLumaCoeffDeltaFlag )
    {
      std::memset( alfSliceParam.alfLumaCoeffFlag, true, sizeof( alfSliceParam.alfLumaCoeffFlag ) );

      if( alfSliceParam.numLumaFilters > 1 )
      {
      //alfLumaCoeffDeltaPredictionFlag :Filter Coeff之间是否采用DPCM的方式编码(差分编码)
        READ_FLAG( code, "alf_luma_coeff_delta_prediction_flag" );
        alfSliceParam.alfLumaCoeffDeltaPredictionFlag = code;
      }
      else
      {
      //若滤波器种类数只有1个,那么也不存在DPCM编码方式这一做法
        alfSliceParam.alfLumaCoeffDeltaPredictionFlag = 0;
      }
    }
    else
    {
      alfSliceParam.alfLumaCoeffDeltaPredictionFlag = 0;
    }
  }

  // derive maxGolombIdx,根据颜色分量获取滤波器形状,色度5x5或亮度7x7
  AlfFilterShape alfShape( isChroma ? 5 : 7 );
  const int maxGolombIdx = AdaptiveLoopFilter::getMaxGolombIdx( alfShape.filterType );
  READ_UVLC( code, isChroma ? "alf_chroma_min_eg_order_minus1" : "alf_luma_min_eg_order_minus1" );

  int kMin = code + 1;
  static int kMinTab[MAX_NUM_ALF_COEFF];
  //滤波器种类数numFilters ,色度统一使用一个滤波器,亮度分量根据解析的参数赋值
  const int numFilters = isChroma ? 1 : alfSliceParam.numLumaFilters;
  short* coeff = isChroma ? alfSliceParam.chromaCoeff : alfSliceParam.lumaCoeff;

  for( int idx = 0; idx < maxGolombIdx; idx++ )
  {
    READ_FLAG( code, isChroma ? "alf_chroma_eg_order_increase_flag"  : "alf_luma_eg_order_increase_flag" );
    CHECK( code > 1, "Wrong golomb_order_increase_flag" );
    kMinTab[idx] = kMin + code;
    kMin = kMinTab[idx];
  }

  if( !isChroma )
  {
    if( alfSliceParam.alfLumaCoeffDeltaFlag )
    //如果numLumaFilters个滤波器均传输了Filter Coeff,获取某个滤波器是否传输Filter Coeff
    {
      for( int ind = 0; ind < alfSliceParam.numLumaFilters; ++ind )
      {
        READ_FLAG( code, "alf_luma_coeff_flag[i]" );
        alfSliceParam.alfLumaCoeffFlag[ind] = code;//第ind个滤波器是否传输了Filter Coeff
      }
    }
  }

  // Filter coefficients
  for( int ind = 0; ind < numFilters; ++ind )//逐个解析滤波器参数
  {
    if( !isChroma && !alfSliceParam.alfLumaCoeffFlag[ind] && alfSliceParam.alfLumaCoeffDeltaFlag )
    //若亮度分量情况下,不是numLumaFilters个都传输了Filter Coeff,且第ind个滤波器未传输Filter Coeff,则直接将当前Filter Coeff置0
    {
      memset( coeff + ind * MAX_NUM_ALF_LUMA_COEFF, 0, sizeof( *coeff ) * alfShape.numCoeff );
      continue;
    }

    for( int i = 0; i < alfShape.numCoeff - 1; i++ )
    //否则,解析Filter Coeff
    {
    //熵解码滤波器参数
      coeff[ind * MAX_NUM_ALF_LUMA_COEFF + i] = alfGolombDecode( kMinTab[alfShape.golombIdx[i]] );
    }
  }
}

3、ALF滤波过程

函数调用流程如下图所示,decode()函数会调用DecLib::executeLoopFilters()函数,其中又调用所有进行滤波操作的函数:loopFilterPic()进行DF去除方块效应、LMCS、SAOProcess()进行SAO操作减小图像的失真、ALFProcess()进行ALF操作。
在这里插入图片描述

void DecLib::executeLoopFilters()
{
  if( !m_pcPic )
  {
    return; // nothing to deblock
  }

  CodingStructure& cs = *m_pcPic->cs;
//LMCS相关操作
  if (cs.sps->getUseReshaper() && m_cReshaper.getSliceReshaperInfo().getUseSliceReshaper())
  {
      CHECK((m_cReshaper.getRecReshaped() == false), "Rec picture is not reshaped!");
      m_pcPic->getRecoBuf(COMPONENT_Y).rspSignal(m_cReshaper.getInvLUT());
      m_cReshaper.setRecReshaped(false);
      m_cSAO.setReshaper(&m_cReshaper);
  }
  // deblocking filter
  m_cLoopFilter.loopFilterPic( cs );//DF
  CS::setRefinedMotionField(cs);
  if( cs.sps->getSAOEnabledFlag() )
  {  //SAO过程
    m_cSAO.SAOProcess( cs, cs.picture->getSAO() );
  }

  if( cs.sps->getALFEnabledFlag() )
  {
    if (cs.slice->getTileGroupAlfEnabledFlag())
    {
      // ALF decodes the differentially coded coefficients and stores them in the parameters structure.
      // Code could be restructured to do directly after parsing. So far we just pass a fresh non-const
      // copy in case the APS gets used more than once.
      //ALF过程
      AlfSliceParam alfParamCopy = cs.aps->getAlfAPSParam();
      m_cALF.ALFProcess(cs, alfParamCopy);
    }

  }
}

ALF滤波主体函数是m_cALF.ALFProcess()

void AdaptiveLoopFilter::ALFProcess( CodingStructure& cs, AlfSliceParam& alfSliceParam )
{
  if( !alfSliceParam.enabledFlag[COMPONENT_Y] && !alfSliceParam.enabledFlag[COMPONENT_Cb] && !alfSliceParam.enabledFlag[COMPONENT_Cr] )
  {
    return;
  }

  // set available filter shapes
  alfSliceParam.filterShapes = m_filterShapes;

  // set clipping range
  m_clpRngs = cs.slice->getClpRngs();

  // set CTU enable flags,CTU级别的Flag
  for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ )
  {
    m_ctuEnableFlag[compIdx] = cs.picture->getAlfCtuEnableFlag( compIdx );
  }
  //根据Slice级别解析的滤波器参数重建Filter Coefficient
  reconstructCoeff( alfSliceParam, CHANNEL_TYPE_LUMA );
  reconstructCoeff( alfSliceParam, CHANNEL_TYPE_CHROMA );

  PelUnitBuf recYuv = cs.getRecoBuf();
  m_tempBuf.copyFrom( recYuv );
  PelUnitBuf tmpYuv = m_tempBuf.getBuf( cs.area );
  //为了对边缘像素进行滤波,需要将边缘扩展(例如:滤波器形状为7x7,则需要每一边扩展7/2=3个像素点)
  tmpYuv.extendBorderPel( MAX_ALF_FILTER_LENGTH >> 1 );

  const PreCalcValues& pcv = *cs.pcv;

  int ctuIdx = 0;
  for( int yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight )
  {
    for( int xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth )
    {
      const int width = ( xPos + pcv.maxCUWidth > pcv.lumaWidth ) ? ( pcv.lumaWidth - xPos ) : pcv.maxCUWidth;
      const int height = ( yPos + pcv.maxCUHeight > pcv.lumaHeight ) ? ( pcv.lumaHeight - yPos ) : pcv.maxCUHeight;
      const UnitArea area( cs.area.chromaFormat, Area( xPos, yPos, width, height ) );
      if( m_ctuEnableFlag[COMPONENT_Y][ctuIdx] )
      {
        Area blk( xPos, yPos, width, height );
        //获取4x4小块的类别(class)
        deriveClassification( m_classifier, tmpYuv.get( COMPONENT_Y ), blk );
        Area blkPCM(xPos, yPos, width, height);
        resetPCMBlkClassInfo(cs, m_classifier, tmpYuv.get(COMPONENT_Y), blkPCM);
        //对亮度分量进行滤波
        m_filter7x7Blk(m_classifier, recYuv, tmpYuv, blk, COMPONENT_Y, m_coeffFinal, m_clpRngs.comp[COMPONENT_Y], cs );
      }

      for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ )
      {
        ComponentID compID = ComponentID( compIdx );
        const int chromaScaleX = getComponentScaleX( compID, tmpYuv.chromaFormat );
        const int chromaScaleY = getComponentScaleY( compID, tmpYuv.chromaFormat );

        if( m_ctuEnableFlag[compIdx][ctuIdx] )
        {
          Area blk( xPos >> chromaScaleX, yPos >> chromaScaleY, width >> chromaScaleX, height >> chromaScaleY );
		//对色度分量进行滤波
          m_filter5x5Blk( m_classifier, recYuv, tmpYuv, blk, compID, alfSliceParam.chromaCoeff, m_clpRngs.comp[compIdx], cs );
        }
      }
      ctuIdx++;
    }
  }
}

重建滤波器系数主体函数void AdaptiveLoopFilter::reconstructCoeff( )

void AdaptiveLoopFilter::reconstructCoeff( AlfSliceParam& alfSliceParam, ChannelType channel, const bool bRedo )
{
  int factor = ( 1 << ( m_NUM_BITS - 1 ) );
  AlfFilterType filterType = isLuma( channel ) ? ALF_FILTER_7 : ALF_FILTER_5;
  int numClasses = isLuma( channel ) ? MAX_NUM_ALF_CLASSES : 1;
  int numCoeff = filterType == ALF_FILTER_5 ? 7 : 13;
  int numCoeffMinus1 = numCoeff - 1;
  int numFilters = isLuma( channel ) ? alfSliceParam.numLumaFilters : 1;
  short* coeff = isLuma( channel ) ? alfSliceParam.lumaCoeff : alfSliceParam.chromaCoeff;

  if( alfSliceParam.alfLumaCoeffDeltaPredictionFlag && isLuma( channel ) )
  {
    for( int i = 1; i < numFilters; i++ )
    {
      for( int j = 0; j < numCoeffMinus1; j++ )
      {
      //Filter Coefficient采用DPCM编码方式
        coeff[i * MAX_NUM_ALF_LUMA_COEFF + j] += coeff[( i - 1 ) * MAX_NUM_ALF_LUMA_COEFF + j];
      }
    }
  }

  for( int filterIdx = 0; filterIdx < numFilters; filterIdx++ )
  {
    int sum = 0;
    for( int i = 0; i < numCoeffMinus1; i++ )
    {
      sum += ( coeff[filterIdx* MAX_NUM_ALF_LUMA_COEFF + i] << 1 );
    }
    coeff[filterIdx* MAX_NUM_ALF_LUMA_COEFF + numCoeffMinus1] = factor - sum;
  }

  if( isChroma( channel ) )
  {
    return;
  }
  //为各个种类(共25种)的像素分配滤波器系数
  for( int classIdx = 0; classIdx < numClasses; classIdx++ )
  {
    int filterIdx = alfSliceParam.filterCoeffDeltaIdx[classIdx];
    memcpy( m_coeffFinal + classIdx * MAX_NUM_ALF_LUMA_COEFF, coeff + filterIdx * MAX_NUM_ALF_LUMA_COEFF, sizeof( short ) * numCoeff );
  }

  if( bRedo && alfSliceParam.alfLumaCoeffDeltaPredictionFlag )
  {
    for( int i = numFilters - 1; i > 0; i-- )
    {
      for( int j = 0; j < numCoeffMinus1; j++ )
      {
        coeff[i * MAX_NUM_ALF_LUMA_COEFF + j] = coeff[i * MAX_NUM_ALF_LUMA_COEFF + j] - coeff[( i - 1 ) * MAX_NUM_ALF_LUMA_COEFF + j];
      }
    }
  }
}

注:本文代码为VTM4.0版本

  • 3
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值