VVC/JEM代码学习14:loopFilterPic

在代码学习13中,记录了SAO技术的详细过程,其实在执行SAO之前,要先对重建图像进行去方块滤波;去方块滤波和样点自适应补偿合并称为环路滤波。今天就记录一下去方块滤波的详细过程。

   去方块滤波的原理:由于在编码时各个块的变换量化相互独立,对不同的块使用了不同的量化参数,导致每个块引入的量化误差大小不一致,导致相邻块边界的不连续性。此外,运动补偿预测过程中,相邻块的预测值可能来自不同图像的不同位置,导致预测残差信号在块边界不连续;另外,时域预测技术使得参考图像中存在的边界不连续可能会传递给后续编码的图像;所以在编码端加入去方块滤波模块已减少方块效应,同时也能减少方块效应对后续编码图像的影响。

    相关函数解析:

Void TComLoopFilter::loopFilterPic( TComPic* pcPic )
{
  // Horizontal filtering,水平滤波
  for ( UInt ctuRsAddr = 0; ctuRsAddr < pcPic->getNumberOfCtusInFrame(); ctuRsAddr++ )//遍历CTU
  {
    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );

    ::memset( m_aapucBS       [EDGE_VER], 0, sizeof( UChar ) * m_uiNumPartitions );
    ::memset( m_aapbEdgeFilter[EDGE_VER], 0, sizeof( Bool  ) * m_uiNumPartitions );

    // CU-based deblocking,基于CU的去方块
#if JVET_C0024_QTBT
    pCtu->getSlice()->setTextType(CHANNEL_TYPE_LUMA);//将分量设置为亮度
    UInt uiCTUSize = pCtu->getSlice()->getSPS()->getCTUSize() ;
    xDeblockCU( pCtu, 0, 0, uiCTUSize, uiCTUSize,EDGE_VER );//执行去方块滤波的主函数;
    if (pCtu->getSlice()->isIntra())//如果是I帧
    {
      ::memset( m_aapucBS       [EDGE_VER], 0, sizeof( UChar ) * m_uiNumPartitions );
      ::memset( m_aapbEdgeFilter[EDGE_VER], 0, sizeof( Bool  ) * m_uiNumPartitions );

      pCtu->getSlice()->setTextType(CHANNEL_TYPE_CHROMA);//将分量设置为色度
      xDeblockCU( pCtu, 0, 0, uiCTUSize, uiCTUSize, EDGE_VER );//对色度分量执行去方块滤波
    }
#else
    xDeblockCU( pCtu, 0, 0, EDGE_VER );
#endif
  }

  // Vertical filtering,当整幅图像都执行完垂直去方块滤波后再执行水平去方块滤波
  for ( UInt ctuRsAddr = 0; ctuRsAddr < pcPic->getNumberOfCtusInFrame(); ctuRsAddr++ )
  {
    TComDataCU* pCtu = pcPic->getCtu( ctuRsAddr );

    ::memset( m_aapucBS       [EDGE_HOR], 0, sizeof( UChar ) * m_uiNumPartitions );
    ::memset( m_aapbEdgeFilter[EDGE_HOR], 0, sizeof( Bool  ) * m_uiNumPartitions );

    // CU-based deblocking
#if JVET_C0024_QTBT
    pCtu->getSlice()->setTextType(CHANNEL_TYPE_LUMA);
    UInt uiCTUSize = pCtu->getSlice()->getSPS()->getCTUSize() ;
    xDeblockCU( pCtu, 0, 0, uiCTUSize, uiCTUSize, EDGE_HOR );//先执行I帧亮度分量和B和P帧亮度色度的去方块滤波
    if (pCtu->getSlice()->isIntra())
    {
      ::memset( m_aapucBS       [EDGE_HOR], 0, sizeof( UChar ) * m_uiNumPartitions );
      ::memset( m_aapbEdgeFilter[EDGE_HOR], 0, sizeof( Bool  ) * m_uiNumPartitions );

      pCtu->getSlice()->setTextType(CHANNEL_TYPE_CHROMA);
      xDeblockCU( pCtu, 0, 0, uiCTUSize, uiCTUSize, EDGE_HOR );//再执行I帧色度分量的去方块滤波
    }
#else
    xDeblockCU( pCtu, 0, 0, EDGE_HOR );
#endif
  }
}
#if JVET_C0024_QTBT
Void TComLoopFilter::xDeblockCU( TComDataCU* pcCU, UInt uiAbsZorderIdx, UInt uiDepth, UInt uiWidth, UInt uiHeight, DeblockEdgeDir edgeDir )
#else
Void TComLoopFilter::xDeblockCU( TComDataCU* pcCU, UInt uiAbsZorderIdx, UInt uiDepth, DeblockEdgeDir edgeDir )
#endif
{
#if JVET_C0024_QTBT 
  if(pcCU->getPic()==0)
#else
  if(pcCU->getPic()==0||pcCU->getPartitionSize(uiAbsZorderIdx)==NUMBER_OF_PART_SIZES)
#endif
  {
    return;
  }
  TComPic* pcPic     = pcCU->getPic();
  UInt uiCurNumParts = pcPic->getNumPartitionsInCtu() >> (uiDepth<<1);
  UInt uiQNumParts   = uiCurNumParts>>2;
  const TComSPS &sps = *(pcCU->getSlice()->getSPS());

  if( pcCU->getDepth(uiAbsZorderIdx) > uiDepth )//还没到最大深度
  {
    for ( UInt uiPartIdx = 0; uiPartIdx < 4; uiPartIdx++, uiAbsZorderIdx+=uiQNumParts )//四叉树划分,递归四次
    {
      UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsZorderIdx] ];
      UInt uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsZorderIdx] ];
      if( ( uiLPelX < sps.getPicWidthInLumaSamples() ) && ( uiTPelY < sps.getPicHeightInLumaSamples() ) )//不是边界
      {
#if JVET_C0024_QTBT
        xDeblockCU( pcCU, uiAbsZorderIdx, uiDepth+1, uiWidth>>1, uiHeight>>1, edgeDir );
#else
        xDeblockCU( pcCU, uiAbsZorderIdx, uiDepth+1, edgeDir );
#endif
      }
    }
    return;
  }

#if JVET_C0024_QTBT
  UInt uiBTDepth = pcCU->getBTDepth(uiAbsZorderIdx, uiWidth, uiHeight);//二叉树深度;
  UInt uiMinCUW = pcCU->getPic()->getMinCUWidth();
  UInt uiMinCUH = pcCU->getPic()->getMinCUHeight();

  if (pcCU->getBTSplitModeForBTDepth(uiAbsZorderIdx, uiBTDepth)==1)//如果是二叉树水平划分
  {
    for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 2; uiPartUnitIdx++ )//递归两次
    {
      if (uiPartUnitIdx==1)
      {
        uiAbsZorderIdx = g_auiRasterToZscan[g_auiZscanToRaster[uiAbsZorderIdx] 
        + (uiHeight>>1)/uiMinCUH*pcCU->getPic()->getNumPartInCtuWidth()];
      }
      xDeblockCU( pcCU, uiAbsZorderIdx, uiDepth, uiWidth, uiHeight>>1, edgeDir );
    }
    return;
  }
  else if (pcCU->getBTSplitModeForBTDepth(uiAbsZorderIdx, uiBTDepth)==2)//二叉树垂直划分
  {
    for ( UInt uiPartUnitIdx = 0; uiPartUnitIdx < 2; uiPartUnitIdx++ )//递归两次
    {
      if (uiPartUnitIdx==1)
      {
        uiAbsZorderIdx = g_auiRasterToZscan[g_auiZscanToRaster[uiAbsZorderIdx] 
        + (uiWidth>>1)/uiMinCUW];
      }
      xDeblockCU( pcCU, uiAbsZorderIdx, uiDepth, uiWidth>>1, uiHeight, edgeDir );
    }
    return;
  }
#endif

  xSetLoopfilterParam( pcCU, uiAbsZorderIdx );//设置环路滤波参数,即确定图像内部边界,左边界和上边界的滤波标记
#if JVET_C0024_QTBT
  xSetEdgefilterCU   ( pcCU, uiAbsZorderIdx, uiWidth, uiHeight);//确定一个CU内部base unit的边界滤波标记
#else
  TComTURecurse tuRecurse(pcCU, uiAbsZorderIdx);
  xSetEdgefilterTU   ( tuRecurse );
  xSetEdgefilterPU   ( pcCU, uiAbsZorderIdx );
#endif

#if JVET_C0024_QTBT
 // const UInt uiPelsInPart = sps.getCTUSize() >> sps.getMaxTotalCUDepth();
  for (UInt j=0; j<(edgeDir==EDGE_VER ? uiHeight: 1); j+=uiMinCUH)//遍历每个cu
  {
    for (UInt i=0; i<(edgeDir==EDGE_VER ? 1: uiWidth); i+=uiMinCUW)
    {
      UInt uiRaster = j/uiMinCUH * pcCU->getPic()->getNumPartInCtuWidth() + i/uiMinCUW + g_auiZscanToRaster[uiAbsZorderIdx];
      UInt uiPartIdx = g_auiRasterToZscan[uiRaster];
      UInt uiBSCheck = 1;
#else
  const UIn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值