大体过程如下:
1、设置可用的过滤器形状:分两个通道,即亮度通道和色度通道;设置剪裁范围:获取片的剪裁范围;设置CTU启用标志:即确定该CTU是否进行划分。
2、首先确定CTU标志是否开启,对启用的部分重建亮度、色度系数,并且将重建YUV值存入临时YUV。得到之前的计算值。之后遍历开启了CTU标志的每个CU块,对每个块都确定其最小的宽高。
3、如果这个块对亮度滤波,则进行分类并进行对应的77滤波。对色度,如果满足条件,则继续划分,并用55的滤波器进行滤波。
具体滤波部分:
亮度有7x7,5x5两种类型的滤波器,有25个类及4种方向性,处理4x4的块;色度只有5x5类型,1个类,处理2x2的块,分类的过程在二中详细阐述。其中7x7类型需要13个系数可以滤波,5x5需要7个系数,用系数模板进行滤波的过程在三中详细阐述。
1、如果是对亮度分量的滤波,则存入325个系数(25类,每类13个系数)。
2、如果是对色度分量的滤波,则不需要重建系数。
3、如果采用了预测模式,每个系数=自己系数-前一排系数。
亮度3232的块进行划分,亮度分量分到44的块来获取分类。
随后进行计算每个块的所有分类的值(拉普拉斯变换的第一个维度)。在各个块中获取拉普拉斯变换的值后,运算得出四种方向D的sum,计算活动性A。
对上面求到的sum进行比较:如果V>H,D=1;如果H>V,D=3;如果D0>D1,D=0;如果D1>D0,D=2。再对比水平竖直和对角线确定哪个是主方向,若差距比较大还可以附加方向强度。最终可得到块在25种分类中的所在分类及方向性,,将最终的4*4的16个块都分配上相同的分类和方向性。
获取临时及重建的亮度值、stride。设置滤波区块范围,pImgYPad是竖排范围,pImg是横排范围,范围均是7。创建滤波的起始高度、终止高度、起始宽度、终止宽度,并检验是否合法。先对亮度图2与图3是滤波方法,图2是针对亮度块进行卷积得到77的滤波后的值,图3针对色度块进行卷积得到55滤波后的值。
77和55类型的滤波器,都有4种不同的方向性有不同的滤波系数顺序。处理结束当前4*4块后,得到sum值,与offset(=256)相加后,除以shift(=512)即可得到该块滤波值。
亮度的7*7滤波范围及系数模板:
pImg6[0]
pImg4[-1] pImg4[0] pImg4[1]
pImg2[-2] pImg2[-1] pImg2[0] pImg2[1] pImg2[2]
pImg0[-3] pImg0[-2] pImg0[-1] pImg0[0] pImg0[1] pImg0[2] pImg0[3]
pImg1[-2] pImg1[-1] pImg1[0] pImg1[1] pImg1[2]
pImg3[-1] pImg3[0] pImg3[1]
pImg5[0]
Coeff[0]
Coeff[1] Coeff[2] Coeff[3]
Coeff[4] Coeff[5] Coeff[6] Coeff[7] Coeff[8]
Coeff[9] Coeff[10] Coeff[11] Coeff[12] Coeff[11] Coeff[10] Coeff[9]
Coeff[8] Coeff[7] Coeff[6] Coeff[5] Coeff[4]
Coeff[3] Coeff[2] Coeff[1]
Coeff[0]
色度的5*5滤波范围及系数模板:
pImg4[0]
pImg2[-1] pImg2[0] pImg2[1]
pImg0[-2] pImg0[-1] pImg0[0] pImg0[1] pImg0[2]
pImg1[-1] pImg1[0] pImg1[1]
pImg3[0]
Coeff[0]
Coeff[1] Coeff[2] Coeff[3]
Coeff[4] Coeff[5] Coeff[6] Coeff[5] Coeff[4]
Coeff[3] Coeff[2] Coeff[1]
Coeff[0]
1.亮度系数=自己系数+上一排系数,每排最后一个系数是512-2*此排和。
2.活动性A:activity = (Pel)Clip3( 0, 15, ( (sumV + sumH) * 32 ) >> 512 )。
3.分类计算:分类序号+(x+方向强度)*5
其中分类序号是0、1、2、3、4,x在D=1或3时为2,否则为0。
框架代码如下:
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
for( int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++ )
{
m_ctuEnableFlag[compIdx] = cs.picture->getAlfCtuEnableFlag( compIdx );
}
//============重建系数=================//
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 );
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 ) );
//======对亮度的滤波,7*7,Y========//
if( m_ctuEnableFlag[COMPONENT_Y][ctuIdx] )
{
Area blk( xPos, yPos, width, height );
deriveClassification( m_classifier, tmpYuv.get( COMPONENT_Y ), blk );//亮度要获取分类
#if JVET_L0664_ALF_REMOVE_LUMA_5x5
m_filter7x7Blk(m_classifier, recYuv, tmpYuv, blk, COMPONENT_Y, m_coeffFinal, m_clpRngs.comp[COMPONENT_Y]);
#else
if( alfSliceParam.lumaFilterType == ALF_FILTER_5 )
{
m_filter5x5Blk( m_classifier, recYuv, tmpYuv, blk, COMPONENT_Y, m_coeffFinal, m_clpRngs.comp[COMPONENT_Y] );
}
else if( alfSliceParam.lumaFilterType == ALF_FILTER_7 )
{
m_filter7x7Blk( m_classifier, recYuv, tmpYuv, blk, COMPONENT_Y, m_coeffFinal, m_clpRngs.comp[COMPONENT_Y] );
}
else
{
CHECK( 0, "Wrong ALF filter type" );
}
#endif
}
//======对色度的滤波,5*5,Cb,Cr========//
for( int compIdx = 1; compIdx < MAX_NUM_COMPONENT; compIdx++ )
{
ComponentID compID = ComponentID( compIdx );
const int chromaScaleX = getComponentScaleX( compID, tmpYuv.chromaFormat );//确定色度范围(4:2:0要缩一下)
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] );//5*5滤波
}
}
ctuIdx++;//继续下一CTU
}
}
}