H.266/VVC代码学习:去块滤波Deblock代码二

去块滤波原理

目录

xSetEdgefilterMultiple函数

xSetMaxFilterLengthPQFromTransformSizes函数

xGetBoundaryStrengthSingle函数


xSetEdgefilterMultiple函数

xSetEdgefilterMultiple函数遍历全部的边界(垂直或者水平),设置其是否使用滤波

void LoopFilter::xSetEdgefilterMultiple( const CodingUnit&    cu,
                                         const DeblockEdgeDir edgeDir,
                                         const Area&          area,
                                         const bool           bValue,
                                         const bool           EdgeIdx )
{
  const PreCalcValues& pcv = *cu.cs->pcv;

  const unsigned uiAdd     = ( edgeDir == EDGE_VER ) ? pcv.partsInCtuWidth : 1;//垂直滤波往下走1个,水平滤波往右走1个
  // 4x4边界数目
  const unsigned uiNumElem = ( edgeDir == EDGE_VER ) ? ( area.height / pcv.minCUHeight ) : ( area.width / pcv.minCUWidth );
  unsigned uiBsIdx         = getRasterIdx( area, pcv ); //第一个边界光栅扫描的索引值

  for( int ui = 0; ui < uiNumElem; ui++ ) //遍历所有的边界(垂直或者水平)
  {
    m_aapbEdgeFilter[edgeDir][uiBsIdx] = bValue; //设置是否使用滤波器
    if ( m_aapucBS[edgeDir][uiBsIdx] && bValue )
    {
      m_aapucBS[edgeDir][uiBsIdx] = 3;  // both the TU and PU edge 表示既是TU边界也是PU边界
    }
    else
    {
      if( ! EdgeIdx )
      {
        m_aapucBS[edgeDir][uiBsIdx] = bValue;
      }
    }
    uiBsIdx += uiAdd;
  }
}

xSetMaxFilterLengthPQFromTransformSizes函数

xSetMaxFilterLengthPQFromTransformSizes函数是根据边界处相邻块的长度判断需要进行滤波的最大像素数,记边界两侧的块分别为P块和Q块

对于垂直边界,需要判断P块和Q块的宽度(width);对于水平边界,需要判断P块和Q块的高度(Height)

对于亮度分量,分为以下三种情况:

  • 小块(小于等于4),最多滤波1个像素
  • 大块(大于等于32),最多滤波7个像素
  • 否则,最多滤波3个像素

对于色度分量,分为以下两种情况:

  • 大块(大于等于8),最多滤波3个像素
  • 否则,最多滤波1个像素

这里只是计算了每个边界在进行滤波时的最大像素数,其真正进行滤波的像素数还需要根据滤波强度决策来决定。

代码及注释如下:

void LoopFilter::xSetMaxFilterLengthPQFromTransformSizes( const DeblockEdgeDir edgeDir, const CodingUnit& cu, const TransformUnit& currTU )
{
  const TransformUnit& tuQ = currTU;// 将Q设置为当前TU

  if ( edgeDir == EDGE_HOR ) //水平边界
  { // 遍历所有的颜色分量
    for ( int cIdx = 0; cIdx < ::getNumberValidComponents(tuQ.chromaFormat); cIdx++ ) // per component
    {
      const ComponentID comp = ComponentID(cIdx);
      const ChannelType ch   = toChannelType(comp);
      const int shiftHor     = ( ( ch == CH_L ) ? 0 : m_shiftHor );
      const int shiftVer     = ( ( ch == CH_L ) ? 0 : m_shiftVer );
      const int ctuXOff      = currTU.block(comp).x - ( m_ctuXLumaSamples >> shiftHor ); // x方向距离左CTU的距离 x offset from left edge of CTU in respective channel sample units
      const int ctuYOff      = currTU.block(comp).y - ( m_ctuYLumaSamples >> shiftVer ); // y方向距离上CTU的距离 y offset from top edge of CTU in respective channel sample units
      const int minCUWidth   = cu.cs->pcv->minCUWidth >> shiftHor;
      if ( currTU.block(comp).valid() && ( ( currTU.block(comp).y == cu.block(comp).y ) ? m_stLFCUParam.topEdge : m_stLFCUParam.internalEdge ) ) // Edge deblocking needs to be recomputed since ISP contains whole CU chroma transforms in last TU of the CU
      { 
        //边缘去块需要重新计算,因为ISP在CU的最后一个TU中包含整个CU色度变换
        for ( int x = 0; x < currTU.blocks[cIdx].width; x += minCUWidth ) //以4x4为单位遍历边界
        {
          const Position  posQ     = Position( currTU.blocks[ch].x + x, currTU.blocks[ch].y );
          const Position  posP     = posQ.offset( 0, -1 );
          const int sizeQSide      = tuQ.block(comp).height;//Q像素长度
          const TransformUnit& tuP = *cu.cs->getTU( posP, ch );
          const int sizePSide      = tuP.block(comp).height;//P像素长度
          m_transformEdge[cIdx][ctuXOff+x][ctuYOff] = true;

          if ( comp == COMPONENT_Y )
          {
            bool smallBlock = (sizePSide <= 4) || (sizeQSide <= 4);
            if (smallBlock) //对于亮度的小块(小于等于4x4),仅滤波1个像素
            {
              m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff] = 1;
              m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff] = 1;
            }
            else //对于大块(大于等于32),滤波7个像素,否则滤波3个像素
            {
              m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff] = (sizeQSide >= 32) ? 7 : 3;
              m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff] = (sizePSide >= 32) ? 7 : 3;
            }
          }
          else
          { //对于色度大块(大于等于8),滤波3个像素,否则滤波1个像素
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
          }
        }
      }
    }
  }
  if ( edgeDir == EDGE_VER ) //垂直边界
  {
    for ( int cIdx = 0; cIdx < ::getNumberValidComponents(tuQ.chromaFormat); cIdx++ ) // per component
    {
      const ComponentID comp = ComponentID(cIdx);
      const ChannelType ch   = toChannelType(comp);
      const int shiftHor     = ( ( ch == CH_L ) ? 0 : m_shiftHor );
      const int shiftVer     = ( ( ch == CH_L ) ? 0 : m_shiftVer );
      const int ctuXOff      = currTU.block(comp).x - ( m_ctuXLumaSamples >> shiftHor ); // x offset from left edge of CTU in respective channel sample units
      const int ctuYOff      = currTU.block(comp).y - ( m_ctuYLumaSamples >> shiftVer ); // y offset from top edge of CTU in respective channel sample units
      const int minCUHeight  = cu.cs->pcv->minCUHeight >> shiftVer;
      if ( currTU.block(comp).valid() && ( ( currTU.block(comp).x == cu.block(comp).x ) ? m_stLFCUParam.leftEdge : m_stLFCUParam.internalEdge ) ) // Edge deblocking needs to be recomputed since ISP contains whole CU chroma transforms in last TU of the CU
      {
        for ( int y = 0; y < currTU.blocks[cIdx].height; y += minCUHeight )
        {
          const Position  posQ     = Position( currTU.blocks[ch].x, currTU.blocks[ch].y + y );
          const Position  posP     = posQ.offset( -1, 0 );
          const int sizeQSide      = tuQ.block(comp).width;
          const TransformUnit& tuP = *cu.cs->getTU( posP, ch );
          const int sizePSide      = tuP.block(comp).width;
          m_transformEdge[cIdx][ctuXOff][ctuYOff+y] = true;

          if ( comp == COMPONENT_Y )
          {
            bool smallBlock = (sizePSide <= 4) || (sizeQSide <= 4);
            if (smallBlock)
            {
              m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff + y] = 1;
              m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff + y] = 1;
            }
            else
            {
              m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff + y] = (sizeQSide >= 32) ? 7 : 3;
              m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff + y] = (sizePSide >= 32) ? 7 : 3;
            }
          }
          else
          {
            m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff+y] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
            m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff+y] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
          }
        }
      }
    }
  }
}

xGetBoundaryStrengthSingle函数

确定最大滤波像素数后,需要根据边界两侧的块的预测模式和编码参数设置边界强度,具体如下表所示。在xGetBoundaryStrengthSingle函数中获取边界强度。

代码及注释如下: 

unsigned LoopFilter::xGetBoundaryStrengthSingle ( const CodingUnit& cu, const DeblockEdgeDir edgeDir, const Position& localPos, const ChannelType chType ) const
{
  // The boundary strength that is output by the function xGetBoundaryStrengthSingle is a multi component boundary strength that contains boundary strength for luma (bits 0 to 1), cb (bits 2 to 3) and cr (bits 4 to 5).
  // 函数xGetBoundaryStrengthSingle输出的边界强度是多分量边界强度,它包含luma(位0到1)、cb(位2到3)和cr(位4到5)的边界强度。
  const Slice& sliceQ = *cu.slice;

  int shiftHor = cu.Y().valid() ? 0 : ::getComponentScaleX(COMPONENT_Cb, cu.firstPU->chromaFormat);
  int shiftVer = cu.Y().valid() ? 0 : ::getComponentScaleY(COMPONENT_Cb, cu.firstPU->chromaFormat);
  const Position& posQ = Position{ localPos.x >> shiftHor,  localPos.y >> shiftVer };
  const Position  posP  = ( edgeDir == EDGE_VER ) ? posQ.offset( -1, 0 ) : posQ.offset( 0, -1 );

  const CodingUnit& cuQ = cu;
  const CodingUnit& cuP = (chType == CHANNEL_TYPE_CHROMA && cuQ.chType == CHANNEL_TYPE_LUMA) ?
                          *cu.cs->getCU(recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, CHANNEL_TYPE_CHROMA, posP), CHANNEL_TYPE_CHROMA) :
                          *cu.cs->getCU( posP, cu.chType );

  //-- Set BS for Intra MB : BS = 4 or 3
  //-----------------相邻块中的至少一个用intra或CIIP模式编码----------------------
  if( ( MODE_INTRA == cuP.predMode ) || ( MODE_INTRA == cuQ.predMode ) )
  {
    if( chType == CHANNEL_TYPE_LUMA )
    {
      int bsY = (MODE_INTRA == cuP.predMode && cuP.bdpcmMode) && (MODE_INTRA == cuQ.predMode && cuQ.bdpcmMode) ? 0 : 2;
      return BsSet(bsY, COMPONENT_Y);
    }
    else
    {
      int bsC = (MODE_INTRA == cuP.predMode && cuP.bdpcmModeChroma) && (MODE_INTRA == cuQ.predMode && cuQ.bdpcmModeChroma) ? 0 : 2;
      return (BsSet(bsC, COMPONENT_Cb) + BsSet(bsC, COMPONENT_Cr));
    }
  }

  const TransformUnit& tuQ = *cuQ.cs->getTU(posQ, cuQ.chType);
  const TransformUnit& tuP = (cuP.chType == CHANNEL_TYPE_CHROMA && cuQ.chType == CHANNEL_TYPE_LUMA) ?
                             *cuP.cs->getTU(recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, CHANNEL_TYPE_CHROMA, posP), CHANNEL_TYPE_CHROMA) :
                             *cuP.cs->getTU(posP, cuQ.chType);

  const PreCalcValues& pcv = *cu.cs->pcv;
  const unsigned rasterIdx = getRasterIdx( Position{ localPos.x,  localPos.y }, pcv );
  if (m_aapucBS[edgeDir][rasterIdx] && (cuP.firstPU->ciipFlag || cuQ.firstPU->ciipFlag))
  {
    if(chType == CHANNEL_TYPE_LUMA)
    {
      return BsSet(2, COMPONENT_Y);
    }
    else
    {
      return BsSet(2, COMPONENT_Cb) + BsSet(2, COMPONENT_Cr);
    }
  }

  unsigned tmpBs = 0;
  //------------------------ 相邻块中的至少一个具有非零变换系数-----------------
  //-- Set BS for not Intra MB : BS = 2 or 1 or 0
  if(chType == CHANNEL_TYPE_LUMA)
  {
    // Y
    if (m_aapucBS[edgeDir][rasterIdx] && (TU::getCbf(tuQ, COMPONENT_Y) || TU::getCbf(tuP, COMPONENT_Y)))
    {
      tmpBs += BsSet(1, COMPONENT_Y);
    }
  }
  else
  {
    if (pcv.chrFormat != CHROMA_400)
    {
      // U
      if (m_aapucBS[edgeDir][rasterIdx]
          && (TU::getCbf(tuQ, COMPONENT_Cb) || TU::getCbf(tuP, COMPONENT_Cb) || tuQ.jointCbCr || tuP.jointCbCr))
      {
        tmpBs += BsSet(1, COMPONENT_Cb);
      }
      // V
      if (m_aapucBS[edgeDir][rasterIdx]
          && (TU::getCbf(tuQ, COMPONENT_Cr) || TU::getCbf(tuP, COMPONENT_Cr) || tuQ.jointCbCr || tuP.jointCbCr))
      {
        tmpBs += BsSet(1, COMPONENT_Cr);
      }
    }
  }
  if (BsGet(tmpBs, COMPONENT_Y) == 1)
  {
    return tmpBs;
  }

  if ( !cu.Y().valid() )
  {
    return tmpBs;
  }

  // and now the pred
  if (m_aapucBS[edgeDir][rasterIdx] != 0 && m_aapucBS[edgeDir][rasterIdx] != 3)
  {
    return tmpBs;
  }
  if( chType == CHANNEL_TYPE_CHROMA )
  {
    return tmpBs;
  }
  if( cuP.predMode != cuQ.predMode && chType == CHANNEL_TYPE_LUMA )
  {
    return BsSet(1, COMPONENT_Y);
  }
  const Position& lumaPosQ  = Position{ localPos.x,  localPos.y };
  const Position  lumaPosP  = ( edgeDir == EDGE_VER ) ? lumaPosQ.offset( -1, 0 ) : lumaPosQ.offset( 0, -1 );
  const MotionInfo&     miQ = cuQ.cs->getMotionInfo( lumaPosQ );
  const MotionInfo&     miP = cuP.cs->getMotionInfo( lumaPosP );
  const Slice&       sliceP = *cuP.slice;
  /************************* 帧间B帧   *********************/
  if (sliceQ.isInterB() || sliceP.isInterB())
  {
    const Picture *piRefP0 = (CU::isIBC(cuP) ? sliceP.getPic() : ((0 > miP.refIdx[0]) ? NULL : sliceP.getRefPic(REF_PIC_LIST_0, miP.refIdx[0])));
    const Picture *piRefP1 = (CU::isIBC(cuP) ? NULL            : ((0 > miP.refIdx[1]) ? NULL : sliceP.getRefPic(REF_PIC_LIST_1, miP.refIdx[1])));
    const Picture *piRefQ0 = (CU::isIBC(cuQ) ? sliceQ.getPic() : ((0 > miQ.refIdx[0]) ? NULL : sliceQ.getRefPic(REF_PIC_LIST_0, miQ.refIdx[0])));
    const Picture *piRefQ1 = (CU::isIBC(cuQ) ? NULL            : ((0 > miQ.refIdx[1]) ? NULL : sliceQ.getRefPic(REF_PIC_LIST_1, miQ.refIdx[1])));
    Mv mvP0, mvP1, mvQ0, mvQ1;

    if (0 <= miP.refIdx[0])
    {
      mvP0 = miP.mv[0];
    }
    if (0 <= miP.refIdx[1])
    {
      mvP1 = miP.mv[1];
    }
    if (0 <= miQ.refIdx[0])
    {
      mvQ0 = miQ.mv[0];
    }
    if (0 <= miQ.refIdx[1])
    {
      mvQ1 = miQ.mv[1];
    }

    int nThreshold = (1 << MV_FRACTIONAL_BITS_INTERNAL) >> 1;
    unsigned uiBs = 0;

    //th can be optimized 
     // P和Q块的参考帧相同,属于相邻块的运动矢量之间的绝对差大于或等于半个亮度样本
    if ( ((piRefP0==piRefQ0)&&(piRefP1==piRefQ1)) || ((piRefP0==piRefQ1)&&(piRefP1==piRefQ0)) )
    {
     
      if ( piRefP0 != piRefP1 )   // Different L0 & L1 如果参考帧不同,其中一个MVD大于半像素精度,则BS为1
      {
        if ( piRefP0 == piRefQ0 )
        {
          uiBs  = ((abs(mvQ0.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP0.getVer()) >= nThreshold) ||
                   (abs(mvQ1.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP1.getVer()) >= nThreshold))
                  ? 1 : 0;
        }
        else
        {
          uiBs  = ((abs(mvQ1.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP0.getVer()) >= nThreshold) ||
                   (abs(mvQ0.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP1.getVer()) >= nThreshold))
                  ? 1 : 0;
        }
      }
      else    // Same L0 & L1 如果参考帧相同,同时满足两个MVD大于半像素精度,则BS为1
      {
        uiBs  = ((abs(mvQ0.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP0.getVer()) >= nThreshold) ||
                 (abs(mvQ1.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP1.getVer()) >= nThreshold))
              &&
                ((abs(mvQ1.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP0.getVer()) >= nThreshold) ||
                 (abs(mvQ0.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP1.getVer()) >= nThreshold))
              ? 1 : 0;
      }
    }
    else // for all different Ref_Idx 参考帧不同,则设置滤波强度为1
    {
      uiBs = 1;
    }
    return uiBs + tmpBs;
  }

  /************************* 帧间P帧:如果MVD大于等于半像素精度 *********************/
  // 相邻块中的一个以IBC预测模式编码,另一个以帧间预测模式编码
  // pcSlice->isInterP()
  CHECK(CU::isInter(cuP) && 0 > miP.refIdx[0], "Invalid reference picture list index");
  CHECK(CU::isInter(cuP) && 0 > miQ.refIdx[0], "Invalid reference picture list index");
  const Picture *piRefP0 = (CU::isIBC(cuP) ? sliceP.getPic() : sliceP.getRefPic(REF_PIC_LIST_0, miP.refIdx[0]));
  const Picture *piRefQ0 = (CU::isIBC(cuQ) ? sliceQ.getPic() : sliceQ.getRefPic(REF_PIC_LIST_0, miQ.refIdx[0]));
  if (piRefP0 != piRefQ0)
  {
    return tmpBs + 1;
  }

  Mv mvP0 = miP.mv[0];
  Mv mvQ0 = miQ.mv[0];

  int nThreshold = (1 << MV_FRACTIONAL_BITS_INTERNAL) >> 1;
  return ( ( abs( mvQ0.getHor() - mvP0.getHor() ) >= nThreshold ) || ( abs( mvQ0.getVer() - mvP0.getVer() ) >= nThreshold ) ) ? (tmpBs + 1) : tmpBs;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值