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

去块滤波原理

目录

xEdgeFilterLuma函数

xUseStrongFiltering函数

xPelFilterLuma函数

xEdgeFilterChroma函数

xEdgeFilterLuma函数

xEdgeFilterLuma函数是对亮度像素的一边界进行去块滤波,基本过程如下:

以长度4为单位遍历该边界,对于垂直边界,如下图右所示;对于水平边界,如下图左所示。

对于每一个长度为4的边界,进行如下过程:

  • 获取滤波开关和滤波强度阈值参数:根据P块和Q块两侧的Qp值计算索引值,根据索引查表获得参数Tc和β
  • 滤波开关决策,计算边界两侧像素的变化率dL(调用deriveLADFShift函数推导重建像素的平均亮度级)
  • 如果dL小于β,表明该边界需要进行滤波。
  • 对于需要滤波的边界,进行滤波强度决策,通过xUseStrongFiltering函数判断是否使用强滤波
  • 调用xPelFilterLuma函数进行滤波过程

xEdgeFilterLuma函数代码及注释如下:

void LoopFilter::xEdgeFilterLuma( const CodingUnit& cu, const DeblockEdgeDir edgeDir, const int iEdge )
{const CompArea&  lumaArea = cu.block(COMPONENT_Y);const PreCalcValues& pcv = *cu.cs->pcv;PelBuf        picYuvRec = m_enc ? m_encPicYuvBuffer.getBuf( lumaArea ) : cu.cs->getRecoBuf( lumaArea );Pel           *piSrc    = picYuvRec.buf;const int     iStride   = picYuvRec.stride;Pel           *piTmpSrc = piSrc;const PPS     &pps      = *(cu.cs->pps);const SPS     &sps      = *(cu.cs->sps);const Slice   &slice    = *(cu.slice);const bool    spsPaletteEnabledFlag          = sps.getPLTMode();const int     bitDepthLuma                   = sps.getBitDepth(CHANNEL_TYPE_LUMA);const ClpRng& clpRng( cu.cs->slice->clpRng(COMPONENT_Y) );int          iQP          = 0;// 4x4区域数unsigned     uiNumParts   = ( ( ( edgeDir == EDGE_VER ) ? lumaArea.height / pcv.minCUHeight : lumaArea.width / pcv.minCUWidth ) );int          pelsInPart   = pcv.minCUWidth; // 4unsigned     uiBsAbsIdx   = 0, uiBs = 0;int          iOffset, iSrcStep;bool  bPartPNoFilter  = false;bool  bPartQNoFilter  = false;int   betaOffsetDiv2  = slice.getDeblockingFilterBetaOffsetDiv2();//betaint   tcOffsetDiv2    = slice.getDeblockingFilterTcOffsetDiv2();//tCint   xoffset, yoffset;Position pos;if (edgeDir == EDGE_VER) //垂直滤波所需信息{xoffset   = 0;yoffset   = pelsInPart;iOffset   = 1;iSrcStep  = iStride;piTmpSrc += iEdge * pelsInPart;pos       = Position{ lumaArea.x + iEdge * pelsInPart, lumaArea.y - yoffset };}else  // (edgeDir == EDGE_HOR){xoffset   = pelsInPart;yoffset   = 0;iOffset   = iStride;iSrcStep  = 1;piTmpSrc += iEdge*pelsInPart*iStride;pos       = Position{ lumaArea.x - xoffset, lumaArea.y + iEdge * pelsInPart };}const int iBitdepthScale = 1 << (bitDepthLuma - 8);// dec pos since within the loop we first calc the pos 遍历所有的minCU// 对于垂直边界,遍历该垂直边界中的所有长度为4的区域,因此iSrcStep=iStride;// 垂直边界滤波处理的是水平方向的像素,因此iOffset=1// 对于水平边界,遍历该水平边界中的所有长度为4的区域,因此iSrcStep=1// 水平边界滤波处理的是垂直方向的像素,因此iOffset=iStridefor( int iIdx = 0; iIdx < uiNumParts; iIdx++ ){pos.x += xoffset;pos.y += yoffset;// Deblock luma boundaries on 4x4 grid only 只以亮度4x4网格作为边界,否则不滤波if (edgeDir == EDGE_HOR && (pos.y % 4) != 0){continue;}if (edgeDir == EDGE_VER && (pos.x % 4) != 0){continue;}uiBsAbsIdx = getRasterIdx( pos, pcv );//获取网格索引uiBs = BsGet(m_aapucBS[edgeDir][uiBsAbsIdx], COMPONENT_Y);//获取对应边界的边界强度if( uiBs ){const CodingUnit& cuQ =  cu; //当前CU// 相邻CUconst CodingUnit& cuP = *cu.cs->getCU(pos.offset(xoffset - pelsInPart, yoffset - pelsInPart), cu.chType);// Derive neighboring PU indexif (edgeDir == EDGE_VER){if (!isAvailableLeft(cu, cuP, !pps.getLoopFilterAcrossSlicesEnabledFlag(), !pps.getLoopFilterAcrossTilesEnabledFlag(),!( pps.getSubPicFromCU(cu).getloopFilterAcrossEnabledFlag() && pps.getSubPicFromCU(cuP).getloopFilterAcrossEnabledFlag()))){m_aapucBS[edgeDir][uiBsAbsIdx] = uiBs = 0;continue;}}else  // (iDir == EDGE_HOR){if (!isAvailableAbove(cu, cuP, !pps.getLoopFilterAcrossSlicesEnabledFlag(), !pps.getLoopFilterAcrossTilesEnabledFlag(),!( pps.getSubPicFromCU(cu).getloopFilterAcrossEnabledFlag() && pps.getSubPicFromCU(cuP).getloopFilterAcrossEnabledFlag()))){m_aapucBS[edgeDir][uiBsAbsIdx] = uiBs = 0;continue;}}iQP = (cuP.qp + cuQ.qp + 1) >> 1;//获取QP作为第一维索引,后面还要加一个shift#if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSETif ( sps.getLadfEnabled() ){int iShift = 0;deriveLADFShift( piTmpSrc + iSrcStep * (iIdx*pelsInPart), iStride, iShift, edgeDir, sps );iQP += iShift;}
#endifbool sidePisLarge   = false;bool sideQisLarge   = false;// P和Q的最大滤波长度int maxFilterLengthP = m_maxFilterLengthP[COMPONENT_Y][pos.x-m_ctuXLumaSamples][pos.y-m_ctuYLumaSamples];int maxFilterLengthQ = m_maxFilterLengthQ[COMPONENT_Y][pos.x-m_ctuXLumaSamples][pos.y-m_ctuYLumaSamples];if (maxFilterLengthP > 3){sidePisLarge = true;if ( maxFilterLengthP > 5 ){// restrict filter length if sub-blocks are used (e.g affine or ATMVP)如果使用子块,则限制过滤器长度(例如,affine或ATMVP)if (cuP.affine){maxFilterLengthP = std::min(maxFilterLengthP, 5);}}}if (maxFilterLengthQ > 3){sideQisLarge = true;}if (edgeDir == EDGE_HOR && pos.y % slice.getSPS()->getCTUSize() == 0){sidePisLarge = false;}/**************** 获取查表得到的tc,beta数值 ****************/const int iIndexTC  = Clip3(0, MAX_QP + DEFAULT_INTRA_TC_OFFSET, int(iQP + DEFAULT_INTRA_TC_OFFSET*(uiBs - 1) + (tcOffsetDiv2 << 1)));const int iIndexB   = Clip3(0, MAX_QP, iQP + (betaOffsetDiv2 << 1));const int iTc = bitDepthLuma < 10 ? ((sm_tcTable[iIndexTC] + (1 << (9 - bitDepthLuma))) >> (10 - bitDepthLuma)) : ((sm_tcTable[iIndexTC]) << (bitDepthLuma - 10));const int iBeta     = sm_betaTable[iIndexB ] * iBitdepthScale;const int iSideThreshold = ( iBeta + ( iBeta >> 1 ) ) >> 3;const int iThrCut   = iTc * 10;const unsigned uiBlocksInPart = pelsInPart / 4 ? pelsInPart / 4 : 1;for( int iBlkIdx = 0; iBlkIdx < uiBlocksInPart; iBlkIdx++ ) //遍历每个minCU中的4x4的块{/****************** 1.2 根据像素的变化率,来进行一次滤波开关决策,与QP找到的beta有关 ******************///对于垂直边界,计算水平方向的变化率;对于水平边界,计算垂直方向的变化率const int dp0 = xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0), iOffset);const int dq0 = xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0), iOffset);const int dp3 = xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3), iOffset);const int dq3 = xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3), iOffset);int dp0L = dp0;//对大块变化率的初始化int dq0L = dq0;int dp3L = dp3;int dq3L = dq3;//对于大块情况,计算变化率时使用的像素变多if (sidePisLarge)//如果P是大块{dp0L = (dp0L + xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0) - 3 * iOffset, iOffset) + 1) >> 1;dp3L = (dp3L + xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3) - 3 * iOffset, iOffset) + 1) >> 1;}if (sideQisLarge)//如果Q是大块{dq0L = (dq0L + xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0) + 3 * iOffset, iOffset) + 1) >> 1;dq3L = (dq3L + xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3) + 3 * iOffset, iOffset) + 1) >> 1;}bool useLongtapFilter = false;/****************** 大块:双线性强滤波 ******************/if (sidePisLarge || sideQisLarge)//如果其中一个是大块{int d0L = dp0L + dq0L;int d3L = dp3L + dq3L;int dpL = dp0L + dp3L;int dqL = dq0L + dq3L;int dL = d0L + d3L;//总变化率bPartPNoFilter = bPartQNoFilter = false;if (spsPaletteEnabledFlag){// check if each of PUs is palette codedbPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);}/****************** 1.3 根据像素的变化率,来进行一次滤波强弱选择,与beta和tc都有关 ******************/if (dL < iBeta) //滤波开关判断{const bool filterP = (dpL < iSideThreshold);const bool filterQ = (dqL < iSideThreshold);//对于垂直边界,src0和src3表示第0行和第3行的像素;对于水平边界,src0和src3表示第0列和第3列的像素Pel* src0 = piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 0);Pel* src3 = piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 3);// adjust decision so that it is not read beyond p5 is maxFilterLengthP is 5 and q5 if maxFilterLengthQ is 5// 调整决策,使其读数不超过p5为maxFilterLengthP为5,如果maxFilterLengthQ为5,则为q5const bool swL = xUseStrongFiltering(src0, iOffset, 2 * d0L, iBeta, iTc, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ)&& xUseStrongFiltering(src3, iOffset, 2 * d3L, iBeta, iTc, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ);if (swL) //更强滤波{useLongtapFilter = true;// 对于垂直边界,滤波四行的像素// 对于水平边界,滤波四列的像素for (int i = 0; i < DEBLOCK_SMALLEST_BLOCK / 2; i++){xPelFilterLuma(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + i), iOffset, iTc, swL, bPartPNoFilter, bPartQNoFilter, iThrCut, filterP, filterQ, clpRng, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ);}}}}if (!useLongtapFilter){const int d0 = dp0 + dq0;const int d3 = dp3 + dq3;const int dp = dp0 + dp3;const int dq = dq0 + dq3;const int d  = d0 + d3;bPartPNoFilter = bPartQNoFilter = false;if (spsPaletteEnabledFlag){// check if each of PUs is palette codedbPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);}if (d < iBeta) //滤波开关决策{bool bFilterP = false;bool bFilterQ = false;if (maxFilterLengthP > 1 && maxFilterLengthQ > 1){bFilterP = (dp < iSideThreshold);bFilterQ = (dq < iSideThreshold);}bool sw = false; //滤波强度决策if (maxFilterLengthP > 2 && maxFilterLengthQ > 2){sw = xUseStrongFiltering(piTmpSrc + iSrcStep * (iIdx * pelsInPart + iBlkIdx * 4 + 0), iOffset, 2 * d0,iBeta, iTc)&& xUseStrongFiltering(piTmpSrc + iSrcStep * (iIdx * pelsInPart + iBlkIdx * 4 + 3), iOffset, 2 * d3,iBeta, iTc);}for (int i = 0; i < DEBLOCK_SMALLEST_BLOCK / 2; i++){xPelFilterLuma(piTmpSrc + iSrcStep * (iIdx * pelsInPart + iBlkIdx * 4 + i), iOffset, iTc, sw,bPartPNoFilter, bPartQNoFilter, iThrCut, bFilterP, bFilterQ, clpRng);}}}}}}
}

xUseStrongFiltering函数

xUseStrongFiltering函数进行滤波强度决策,代码及注释如下:

/**- Decision between strong and weak filter 滤波强度决策.\param offset         offset value for picture data\param d               d value\param beta            beta value\param tc              tc value\param piSrc           pointer to picture data*/
inline bool LoopFilter::xUseStrongFiltering(Pel* piSrc, const int iOffset, const int d, const int beta, const int tc, bool sidePisLarge, bool sideQisLarge, int maxFilterLengthP, int maxFilterLengthQ, bool isChromaHorCTBBoundary) const
{const Pel m4 = piSrc[ 0          ];//          **             **   **             ** const Pel m3 = piSrc[-iOffset    ];//          m0   m1   m2   m3   m4   m5   m6   m7     const Pel m7 = piSrc[ iOffset * 3];//                              |const Pel m0 = piSrc[-iOffset * 4];//                    **    当前起始位置[0](Q块)const Pel m2 = piSrc[-iOffset * 2];int       sp3 = abs(m0 - m3);//最左边的位置减去左侧边缘if (isChromaHorCTBBoundary)//如果是色度CTU边缘,则看相邻边缘{sp3 = abs(m2 - m3);}int       sq3      = abs(m7 - m4);//最右边的位置减去右侧边缘const int d_strong = sp3 + sq3; // 两者的差距if (sidePisLarge || sideQisLarge)//对于大块,进一步增加长度{Pel mP4;Pel m11;if (sidePisLarge) //如果P是大块{if (maxFilterLengthP == 7) //如果P块滤波长度为7,则扩展到左侧第七个像素{const Pel mP5 = piSrc[-iOffset * 5];const Pel mP6 = piSrc[-iOffset * 6];const Pel mP7 = piSrc[-iOffset * 7];;mP4 = piSrc[-iOffset * 8];sp3 = sp3 + abs(mP5 - mP6 - mP7 + mP4);}else{mP4 = piSrc[-iOffset * 6];}sp3 = (sp3 + abs(m0 - mP4) + 1) >> 1;}if (sideQisLarge) //如果Q是大块{if (maxFilterLengthQ == 7){const Pel m8 = piSrc[iOffset * 4];const Pel m9 = piSrc[iOffset * 5];const Pel m10 = piSrc[iOffset * 6];;m11 = piSrc[iOffset * 7];sq3 = sq3 + abs(m8 - m9 - m10 + m11);}else{m11 = piSrc[iOffset * 5];}sq3 = (sq3 + abs(m11 - m7) + 1) >> 1;}return ((sp3 + sq3) < (beta * 3 >> 5)) && (d < (beta >> 4)) && (abs(m3 - m4) < ((tc * 5 + 1) >> 1));}else{return ((d_strong < (beta >> 3)) && (d < (beta >> 2)) && (abs(m3 - m4) < ((tc * 5 + 1) >> 1)));}
}

xPelFilterLuma函数

xPelFilterLuma函数进行亮度像素的滤波处理过程。对于大块(大于等于32)的强滤波,调用xFilteringPandQ函数执行双线性滤波。否则,执行普通的强滤波或者弱滤波过程。

inline void LoopFilter::xPelFilterLuma(Pel* piSrc, const int iOffset, const int tc, const bool sw, const bool bPartPNoFilter, const bool bPartQNoFilter, const int iThrCut, const bool bFilterSecondP, const bool bFilterSecondQ, const ClpRng& clpRng, bool sidePisLarge, bool sideQisLarge, int maxFilterLengthP, int maxFilterLengthQ) const
{int delta;/******** 初始化像素位置、像素值 *******/const Pel m4  = piSrc[ 0          ];const Pel m3  = piSrc[-iOffset    ];const Pel m5  = piSrc[ iOffset    ];const Pel m2  = piSrc[-iOffset * 2];const Pel m6  = piSrc[ iOffset * 2];const Pel m1  = piSrc[-iOffset * 3];const Pel m7  = piSrc[ iOffset * 3];const Pel m0  = piSrc[-iOffset * 4];const Pel mP1 = piSrc[-iOffset * 5];const Pel mP2 = piSrc[-iOffset * 6];const Pel mP3 = piSrc[-iOffset * 7];const Pel m8  = piSrc[ iOffset * 4];const Pel m9  = piSrc[ iOffset * 5];const Pel m10 = piSrc[ iOffset * 6];const char tc3[3] = { 3, 2, 1};if (sw) //强滤波{if (sidePisLarge || sideQisLarge) //如果是大块,那么进行双线性滤波{xFilteringPandQ(piSrc, iOffset, sidePisLarge ? maxFilterLengthP : 3, sideQisLarge ? maxFilterLengthQ : 3, tc);}else // 普通的强滤波{piSrc[-iOffset]     = Clip3(m3 - tc3[0] * tc, m3 + tc3[0] * tc, ((m1 + 2 * m2 + 2 * m3 + 2 * m4 + m5 + 4) >> 3));piSrc[0]            = Clip3(m4 - tc3[0] * tc, m4 + tc3[0] * tc, ((m2 + 2 * m3 + 2 * m4 + 2 * m5 + m6 + 4) >> 3));piSrc[-iOffset * 2] = Clip3(m2 - tc3[1] * tc, m2 + tc3[1] * tc, ((m1 + m2 + m3 + m4 + 2) >> 2));piSrc[iOffset]      = Clip3(m5 - tc3[1] * tc, m5 + tc3[1] * tc, ((m3 + m4 + m5 + m6 + 2) >> 2));piSrc[-iOffset * 3] = Clip3(m1 - tc3[2] * tc, m1 + tc3[2] * tc, ((2 * m0 + 3 * m1 + m2 + m3 + m4 + 4) >> 3));piSrc[iOffset * 2]  = Clip3(m6 - tc3[2] * tc, m6 + tc3[2] * tc, ((m3 + m4 + m5 + 3 * m6 + 2 * m7 + 4) >> 3));}}else{/* Weak filter 弱滤波 */delta = ( 9 * ( m4 - m3 ) - 3 * ( m5 - m2 ) + 8 ) >> 4;if ( abs(delta) < iThrCut ) //如果不满足该条件,说明不连续可能是内容导致的{delta = Clip3( -tc, tc, delta );piSrc[-iOffset] = ClipPel( m3 + delta, clpRng);piSrc[0]        = ClipPel( m4 - delta, clpRng);const int tc2 = tc >> 1;if( bFilterSecondP ){const int delta1 = Clip3( -tc2, tc2, ( ( ( ( m1 + m3 + 1 ) >> 1 ) - m2 + delta ) >> 1 ) );piSrc[-iOffset * 2] = ClipPel( m2 + delta1, clpRng);}if( bFilterSecondQ ){const int delta2 = Clip3( -tc2, tc2, ( ( ( ( m6 + m4 + 1 ) >> 1 ) - m5 - delta ) >> 1 ) );piSrc[iOffset] = ClipPel( m5 + delta2, clpRng);}}}if(bPartPNoFilter){piSrc[-iOffset    ] = m3;piSrc[-iOffset * 2] = m2;piSrc[-iOffset * 3] = m1;if (sidePisLarge){piSrc[-iOffset * 4] = m0;piSrc[-iOffset * 5] = mP1;piSrc[-iOffset * 6] = mP2;piSrc[-iOffset * 7] = mP3;}}if(bPartQNoFilter){piSrc[ 0          ] = m4;piSrc[ iOffset    ] = m5;piSrc[ iOffset * 2] = m6;if (sideQisLarge){piSrc[iOffset * 3] = m7;piSrc[iOffset * 4] = m8;piSrc[iOffset * 5] = m9;piSrc[iOffset * 6] = m10;}}
}

xEdgeFilterChroma函数

xEdgeFilterChroma函数过程和亮度xEdgeFilterLuma类似,不同的是该过程遍历边界时是以2为单位进行遍历的

void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir edgeDir, const int iEdge)
{const Position lumaPos   = cu.Y().valid() ? cu.Y().pos() : recalcPosition( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].pos() );const Size     lumaSize  = cu.Y().valid() ? cu.Y().size() : recalcSize( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].size() );const PreCalcValues& pcv = *cu.cs->pcv;unsigned  rasterIdx      = getRasterIdx( lumaPos, pcv );PelBuf     picYuvRecCb = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cb)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cb));PelBuf     picYuvRecCr = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cr)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cr));Pel       *piSrcCb       = picYuvRecCb.buf;Pel       *piSrcCr       = picYuvRecCr.buf;const int  iStride       = picYuvRecCb.stride;const SPS &sps           = *cu.cs->sps;const PPS &pps           = *cu.cs->pps;const Slice  &slice      = *cu.slice;const ChromaFormat nChromaFormat   = sps.getChromaFormatIdc();const unsigned uiPelsInPartChromaH = pcv.minCUWidth  >> ::getComponentScaleX(COMPONENT_Cb, nChromaFormat);const unsigned uiPelsInPartChromaV = pcv.minCUHeight >> ::getComponentScaleY(COMPONENT_Cb, nChromaFormat);int       iOffset, iSrcStep;unsigned  uiLoopLength;bool      bPartPNoFilter  = false;bool      bPartQNoFilter  = false;const int tcOffsetDiv2[2]   = { slice.getDeblockingFilterCbTcOffsetDiv2(), slice.getDeblockingFilterCrTcOffsetDiv2() };const int betaOffsetDiv2[2] = { slice.getDeblockingFilterCbBetaOffsetDiv2(), slice.getDeblockingFilterCrBetaOffsetDiv2() };// Vertical Position 垂直位置unsigned uiEdgeNumInCtuVert = rasterIdx % pcv.partsInCtuWidth + iEdge;unsigned uiEdgeNumInCtuHor  = rasterIdx / pcv.partsInCtuWidth + iEdge;if( ( uiPelsInPartChromaH < DEBLOCK_SMALLEST_BLOCK ) && ( uiPelsInPartChromaV < DEBLOCK_SMALLEST_BLOCK ) &&(( ( uiEdgeNumInCtuVert % ( DEBLOCK_SMALLEST_BLOCK / uiPelsInPartChromaH ) ) && ( edgeDir == EDGE_VER ) ) ||( ( uiEdgeNumInCtuHor  % ( DEBLOCK_SMALLEST_BLOCK / uiPelsInPartChromaV ) ) && ( edgeDir == EDGE_HOR ) ))){return;}unsigned uiNumParts =  ( edgeDir == EDGE_VER ) ? lumaSize.height / pcv.minCUHeight : lumaSize.width / pcv.minCUWidth ;int   uiNumPelsLuma = pcv.minCUWidth;unsigned uiBsAbsIdx;unsigned bS[2];Pel* piTmpSrcCb = piSrcCb;Pel* piTmpSrcCr = piSrcCr;int xoffset, yoffset;Position pos( lumaPos.x, lumaPos.y );if( edgeDir == EDGE_VER ) //垂直边界{xoffset      = 0;yoffset      = uiNumPelsLuma;iOffset      = 1;iSrcStep     = iStride;piTmpSrcCb  += iEdge*uiPelsInPartChromaH;piTmpSrcCr  += iEdge*uiPelsInPartChromaH;uiLoopLength = uiPelsInPartChromaV;//滤波长度pos          = Position{ lumaPos.x + iEdge*uiNumPelsLuma, lumaPos.y - yoffset };}else  // (edgeDir == EDGE_HOR) 水平边界{xoffset      = uiNumPelsLuma;yoffset      = 0;iOffset      = iStride;iSrcStep     = 1;piTmpSrcCb  += iEdge*iStride*uiPelsInPartChromaV;piTmpSrcCr  += iEdge*iStride*uiPelsInPartChromaV;uiLoopLength = uiPelsInPartChromaH;pos          = Position{ lumaPos.x - xoffset, lumaPos.y + iEdge*uiNumPelsLuma };}const int iBitdepthScale = 1 << (sps.getBitDepth(CHANNEL_TYPE_CHROMA) - 8);// 遍历该色度块边界的所有长度为2的区域for( int iIdx = 0; iIdx < uiNumParts; iIdx++ ){pos.x += xoffset;pos.y += yoffset;uiBsAbsIdx = getRasterIdx( pos, pcv );unsigned tmpBs = m_aapucBS[edgeDir][uiBsAbsIdx];tmpBs = m_aapucBS[edgeDir][uiBsAbsIdx];bS[0] = BsGet(tmpBs, COMPONENT_Cb);bS[1] = BsGet(tmpBs, COMPONENT_Cr);if (bS[0] > 0 || bS[1] > 0){const CodingUnit& cuQ =  cu;CodingUnit& cuP1 = *cu.cs->getCU( recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, cu.chType, pos.offset( xoffset - uiNumPelsLuma, yoffset - uiNumPelsLuma ) ), cu.chType );CodingUnit& cuP  = *cu.cs->getCU( recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, (cuP1.isSepTree() ? CHANNEL_TYPE_CHROMA : cu.chType), pos.offset( xoffset - uiNumPelsLuma, yoffset - uiNumPelsLuma ) ), (cuP1.isSepTree() ? CHANNEL_TYPE_CHROMA : cu.chType));if (edgeDir == EDGE_VER){CHECK(!isAvailableLeft(cu, cuP, !pps.getLoopFilterAcrossSlicesEnabledFlag(), !pps.getLoopFilterAcrossTilesEnabledFlag(),!( pps.getSubPicFromCU(cu).getloopFilterAcrossEnabledFlag() && pps.getSubPicFromCU(cuP).getloopFilterAcrossEnabledFlag())), "Neighbour not available");}else  // (iDir == EDGE_HOR){CHECK(!isAvailableAbove(cu, cuP, !pps.getLoopFilterAcrossSlicesEnabledFlag(), !pps.getLoopFilterAcrossTilesEnabledFlag(),!( pps.getSubPicFromCU(cu).getloopFilterAcrossEnabledFlag() && pps.getSubPicFromCU(cuP).getloopFilterAcrossEnabledFlag())), "Neighbour not available");}bPartPNoFilter = bPartQNoFilter = false;if ( sps.getPLTMode()){// check if each of PUs is palette codedbPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);}const int maxFilterLengthP = m_maxFilterLengthP[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];const int maxFilterLengthQ = m_maxFilterLengthQ[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];bool largeBoundary         = false;bool isChromaHorCTBBoundary = false;if ( maxFilterLengthP >= 3 && maxFilterLengthQ >= 3 ) //长边界{largeBoundary = true;}if (edgeDir == EDGE_HOR && pos.y % cuP.slice->getSPS()->getCTUSize() == 0){isChromaHorCTBBoundary = true;}for( int chromaIdx = 0; chromaIdx < 2; chromaIdx++ ){// 如果边界强度为2或者边界强度为1且为大块边界if ((bS[chromaIdx] == 2) || (largeBoundary && (bS[chromaIdx] == 1))){const ClpRng &clpRng(cu.cs->slice->clpRng(ComponentID(chromaIdx + 1)));Pel *         piTmpSrcChroma = (chromaIdx == 0) ? piTmpSrcCb : piTmpSrcCr;const TransformUnit &tuQ = *cuQ.cs->getTU(recalcPosition(cu.chromaFormat, CHANNEL_TYPE_LUMA, CHANNEL_TYPE_CHROMA, pos), CHANNEL_TYPE_CHROMA);const TransformUnit &tuP =*cuP.cs->getTU(recalcPosition(cu.chromaFormat, CHANNEL_TYPE_LUMA, CHANNEL_TYPE_CHROMA,(edgeDir == EDGE_VER) ? pos.offset(-1, 0) : pos.offset(0, -1)),CHANNEL_TYPE_CHROMA);const QpParam cQP(tuP, ComponentID(chromaIdx + 1), -MAX_INT, false);const QpParam cQQ(tuQ, ComponentID(chromaIdx + 1), -MAX_INT, false);const int qpBdOffset = tuP.cs->sps->getQpBDOffset(toChannelType(ComponentID(chromaIdx + 1)));int       baseQp_P   = cQP.Qp(0) - qpBdOffset;int       baseQp_Q   = cQQ.Qp(0) - qpBdOffset;int       iQP        = ((baseQp_Q + baseQp_P + 1) >> 1);const int iIndexTC =Clip3<int>(0, MAX_QP + DEFAULT_INTRA_TC_OFFSET,iQP + DEFAULT_INTRA_TC_OFFSET * (bS[chromaIdx] - 1) + (tcOffsetDiv2[chromaIdx] << 1));const int bitDepthChroma = sps.getBitDepth(CHANNEL_TYPE_CHROMA);const int iTc            = bitDepthChroma < 10? ((sm_tcTable[iIndexTC] + (1 << (9 - bitDepthChroma))) >> (10 - bitDepthChroma)): ((sm_tcTable[iIndexTC]) << (bitDepthChroma - 10));bool useLongFilter = false;if (largeBoundary) //强滤波{const int indexB = Clip3<int>(0, MAX_QP, iQP + (betaOffsetDiv2[chromaIdx] << 1));const int beta   = sm_betaTable[indexB] * iBitdepthScale;const int dp0 =xCalcDP(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 0), iOffset, isChromaHorCTBBoundary);const int dq0 = xCalcDQ(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 0), iOffset);const int subSamplingShift = (edgeDir == EDGE_VER) ? m_shiftVer : m_shiftHor;const int dp3 =(subSamplingShift == 1)? xCalcDP(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 1), iOffset, isChromaHorCTBBoundary): xCalcDP(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 3), iOffset, isChromaHorCTBBoundary);const int dq3 = (subSamplingShift == 1)? xCalcDQ(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 1), iOffset): xCalcDQ(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 3), iOffset);const int d0 = dp0 + dq0;const int d3 = dp3 + dq3;const int d  = d0 + d3;if (d < beta){useLongFilter = true;const bool sw = xUseStrongFiltering(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + 0), iOffset,2 * d0, beta, iTc, false, false, 7, 7, isChromaHorCTBBoundary)&& xUseStrongFiltering(piTmpSrcChroma + iSrcStep * (iIdx * uiLoopLength + ((subSamplingShift == 1) ? 1 : 3)),iOffset, 2 * d3, beta, iTc, false, false, 7, 7, isChromaHorCTBBoundary);for (unsigned step = 0; step < uiLoopLength; step++){xPelFilterChroma(piTmpSrcChroma + iSrcStep * (step + iIdx * uiLoopLength), iOffset, iTc, sw,bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary, isChromaHorCTBBoundary);}}}if (!useLongFilter){for (unsigned step = 0; step < uiLoopLength; step++){xPelFilterChroma(piTmpSrcChroma + iSrcStep * (step + iIdx * uiLoopLength), iOffset, iTc, false,bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary, isChromaHorCTBBoundary);}}}}}}
}