参考
《新一代视频压缩编码标准 毕厚杰 6.11》
【H.264/AVC视频编解码技术详解】二十、H.264的去块滤波算法
一、去块滤波的基本概念
1. 去块滤波的作用
去块滤波器(Deblocking Filter)是视频编解码器中的重要组成部分,其核心作用在于消除编码过程中产生的图像块效应。图像中的块效应主要因为以宏块为基本单元的编码结构而产生。在编码中,每个宏块的子块都会按照既定分割方式进行预测、变换和量化编码,在这个过程中可能导致块效应的因素主要有以下几种:
- 由于变换和量化编码的运算精度误差导致边界出现不连续;
- 由于码率设置较低,量化强度较大,或者相邻宏块的量化参数不一致导致重建图像的细节部分产生差异;
- 由于运动补偿时的参考块位置与当前块位置关系不一致导致重建像素的内容实际上缺乏相关性。
为了解决该问题,在H.264中定义了名为”Deblocking Filter”的像素域的图像滤波算法,以降低块与宏块边界的像素不一致性,提升整体视觉主观体验。
2. 去块滤波器的定义
在H.264的标准文档中,去块滤波器定义在8.7节中。在H.264的以下profile中,去块滤波是必要的组成部分:
- Baseline, Constrained Baseline, Main, Extended, High, High 10, High 4:2:2, High 4:4:4 Predictive;
在以下profile中,去块滤波器推荐而不强制使用:
- High 10 Intra, High 4:2:2 Intra, High 4:4:4 Intra, CAVLC 4:4:4 iNTRA;
在H.264帧解码的过程中,去块滤波器在该帧所有宏块的解码像素数据重建完成之后进行。在执行中,该过程按照宏块地址的顺序,对所有的NxN分割模式的宏块的像素块分割边界分别进行,其中不包括整个图像的边界以及被标志值disable_deblocking_filter_idc所禁用的边界。
3.后置滤波器和环路滤波器
在视频编解码器中加入去方块滤波器的方法有两种:后置滤波器和环路滤波器。后置滤波器只处理编码环路外的显示缓冲器中的数据,所以它不是标准化过程中的规范内容,在标准中只是可选项。相反,环路滤波器处理编码环路中的数据。在编码器中,被滤波的图像帧作为后续编码帧运动补偿的参考帧;在解码器中,滤波后的图像输出显示。这要求所有与本标准一致的解码器采用同一个滤波器以与编码器同步。当然如果有必要,解码器也还可以在使用环路滤波器的同时使用后置滤波器。
在编码环路中使用滤波器比后置滤波有几点优点。首先,环路滤波器可以保证不同水平的图像质量。其次,在解码器端没有必要再为滤波器准备额外帧缓存。第三,经验试验已显示环路滤波比后置滤波更能增加视频流的主客观质量,同时有效降低解码器的复杂度。
尽管有以上这些优点,环路滤波器的复杂度还是较高的。即使经过很大努力进行滤波算法的时间优化,去除其中的乘除法,滤波器也轻易地达到解码器计算复杂度的三分之一。
高复杂度的主要原因是滤波器的高度自适应性,它需要对方块边界及样点量化值进行条件判断和处理。这样,在算法的主要内部循环中不可避免地出现条件分支。众所周知,这是很耗时的。另一个高复杂度的原因是 H.264 中编码算法中残差编码的尺寸。对于 4×4 的方块大小及平均在每个方向上滤波 2 个点,这样几乎图像中每个点都要被调入到内存中,要么被修正,要么用来判断边界点是否要被修正。而对 H.263 环路滤波器或任何 MPEG-4/H.263 后置滤波器来说不是这样的,因为它们对 8×8 方块进行操作。
4.假边界
去方块滤波器的作用是去除 H.264 编解码算法带来的方块效应。但是,如果在 DCT 边界上,正好是图像的边界,如家具边等,若不加以判断而误认为是方块效应,则可能造成新的误差。为此,在滤波方块效应时,应该先判断该边界是图像真实边界还是方块效应所形成的边界(假边界)。对真实边界不进行滤波处理,而对假边界则要根据周围图像块的性质和编码方法采用不同强度的滤波。
二、 去块滤波的执行过程
去块滤波对于每一个宏块的亮度和色度分量分别进行。对于宏块的每一个分量,首先进行垂直方向的滤波,然后进行水平方向的滤波。垂直方向的滤波从左向右进行,水平方向的滤波从上向下进行。在水平和垂直两方向上,待滤波的块边沿分为两类:半宏块和1/4宏块的边沿。标准文档中的插图表示如下:
在上图中,实线表示半宏块的边沿,虚线表示1/4宏块的边沿。对于亮度/色度以及不同的参数设置,去块滤波操作的边沿不同。对于亮度分量,根据transform_size_8x8_flag的值判断:
- 如果transform_size_8x8_flag为0,即采用4×4尺寸变换,对实线和虚线的边沿进行滤波;
- 如果transform_size_8x8_flag为1,即采用8×8尺寸变换,只对实线的块边沿进行滤波;
对于色度分量,只考虑4:2:0格式,只对半宏块边沿即实线部分进行滤波。
宏块去块滤波的过程主要分为如下几个步骤:
确定当前宏块的邻域有效性;
-
获取几个关键的标识参数:
- fieldMbInFrameFlag:只考虑帧编码,该值应为0;
- filterInternalEdgesFlag:如果当前slice中的disable_deblocking_filter_idc的值设为0,则该flag为1;反之为0;
- filterLeftMbEdgeFlag:如果当前slice中的disable_deblocking_filter_idc设为1,或disable_deblocking_filter_idc设为2而且该宏块不可得,或宏块为图像的左侧边沿宏块,则该flag设为0;反之设为1;
- filterTopMbEdgeFlag:如果当前slice中的disable_deblocking_filter_idc设为1,或disable_deblocking_filter_idc设为2而且该宏块不可得,或宏块为图像的上方边沿宏块,则该flag设为0;反之设为1;
根据上述关键标识参数执行滤波过程。
2.1 左宏块边沿滤波
若标识位filterLeftMbEdgeFlag设为1,根据协议文档中8.7.1节的方法执行宏块亮度分量左侧边沿的滤波。其中chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (0, k)的k取[0,15]。
2.2 上宏块边沿滤波
若标识位filterTopMbEdgeFlag的值为1,宏块亮度分量的上水平边沿进行滤波过程。由于当前设置下MbaffFrameFlag始终为0,因此根据协议文档中8.7.1节的方法执行滤波。相关参数为chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (k,0)的k取[0,15]。
2.3 内部块边沿滤波
当标志位filterInternalEdgesFlag为1时,执行宏块内部块边沿的滤波。内部块边沿的滤波分为水平和垂直两种。
2.3.1 内部垂直块边沿滤波
内部垂直边沿滤波的步骤为:
- 如果transform_size_8x8_flag为0,根据协议文档中8.7.1节的方法执行滤波。相关参数为chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (4,k)的k取[0,15]。该步骤即为第一和第二列块相邻边沿的滤波。
- 根据协议文档中8.7.1节的方法执行滤波,相关参数为chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (8,k)的k取[0,15]。该步骤即为第二和第三列块相邻边沿的滤波。
- 如果transform_size_8x8_flag为0,根据协议文档中8.7.1节的方法执行滤波。相关参数为chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (12,k)的k取[0,15]。该步骤即为第三和第四列块相邻边沿的滤波。
2.3.2 内部水平块边沿滤波
内部水平边沿滤波的步骤为:
- 如果transform_size_8x8_flag为0,根据协议文档中8.7.1节的方法执行滤波。相关参数为chromaEdgeFlag为0,verticalEdgeFlag为0,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (k,4)的k取[0,15]。该步骤即为第一和第二行块相邻边沿的滤波。
- 根据协议文档中8.7.1节的方法执行滤波,相关参数为chromaEdgeFlag为0,verticalEdgeFlag为0,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (k,8)的k取[0,15]。该步骤即为第二和第三行块相邻边沿的滤波。
- 如果transform_size_8x8_flag为0,根据协议文档中8.7.1节的方法执行滤波。相关参数为chromaEdgeFlag为0,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (k,12)的k取[0,15]。该步骤即为第三和第四行块相邻边沿的滤波。
2.4 色度分量的滤波
在图像为彩色图像的情况下,色度部分同样需要进行滤波操作。对于色度分量的滤波,其思路类似于亮度分量。
2.4.1 色度左宏块边沿滤波
如果filterLeftMbEdgeFlag为1时,根据协议文档中8.7.1节的方法执行宏块色度分量左侧边沿的滤波。chromaEdgeFlag为1,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (0, k)的k取[0,7]。
2.4.2 色度上宏块边沿滤波
如果filterTopMbEdgeFlag为1,根据协议文档中8.7.1节的方法执行宏块色度分量上方边沿的滤波。。chromaEdgeFlag为1,verticalEdgeFlag为0,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = ( k,0)的k取[0,7]。
2.4.3 色度分量内部块边沿滤波
色度分量的内部块滤波也分为水平和垂直两个方向,且只有在transform_size_8x8_flag为0时执行。
对于水平方向,根据协议文档中8.7.1节的方法执行滤波,相关参数为chromaEdgeFlag为1,verticalEdgeFlag为0,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (k,4)的k取[0,8]。
对于垂直方向,根据协议文档中8.7.1节的方法执行滤波,相关参数为chromaEdgeFlag为1,verticalEdgeFlag为1,fieldModeInFrameFilteringFlag等于fieldMbInFrameFlag(帧编码模式中始终0),(xEk, yEk) = (4,k)的k取[0,8]。
三、块/宏块边沿滤波方法
滤波每一个块边沿的方法定义于标准协议文档的8.7.1节。所需要的步骤包括:
-
确定相关参数:
- chromaEdgeFlag:表明当前数据属于亮度或色度;
- iCbCr:色度分量索引;
- verticalEdgeFlag:水平、垂直方向标志位;
- fieldModeInFrameFilteringFlag:隔行模式标志位;
- nE:一个边沿包含的像素数量,亮度为16,色度为8;
获取原重建块的待滤波像素;
根据原像素进行滤波;
修改重建像素值。
3.1 获取原重建块的待滤波像素
对一个块边沿上的某一个像素位置进行滤波时,需要获取该像素位置相邻的8个原像素值作为参考。标准文档中的插图如下:
上图中的p0和q0代表块边界某个位置左右的相邻像素,根据滤波方向的不同,可能是左右或上下相邻的像素。以水平块边沿滤波为例,采用边界垂直方向的8个相邻像素,如下图所示:
上图中,红色像素P0和Q0表示边界两侧的相邻像素,其余蓝色像素为滤波所需要的其他相邻像素。
3.2 边界像素滤波
在获取到边界的参考像素后,下一步执行的操作是参考8个像素值进行滤波。滤波过程的具体方法定义在标准的8.7.2节。实现一个边界的滤波需要三大步骤:
- 计算边界滤波强度;
- 判断滤波条件;
- 滤波边界像素;
3.2.1 计算边界滤波强度
边界滤波强度是边界像素滤波时的一个重要的参数,决定了像素点的滤波采取怎样的算法。实现方法定义在标准的8.7.2.1节。边界滤波强度可能的取值有0、1、2、3、4共5个值。其中:
- 0:表示该边界不需要滤波;
- 1、2:主要用于帧间编码图像的滤波;
- 3、4:主要用于帧内编码图像的滤波;
本节中主要以帧内编码为例说明边界滤波强度的计算方法,即滤波强度取3、4的条件。
- 当滤波边界为宏块边沿,并且边界相邻像素p0和q0都属于帧编码宏块,并且任意一个像素的所属宏块为帧内编码或所属的帧类型为SI或SP时,边界滤波强度为4;
- 当滤波边界为子块边界,并且边界相邻像素p0和q0都属于帧编码宏块,并且任意一个像素的所属宏块为帧内编码或所属的帧类型为SI或SP时,边界滤波强度为3;
- 在帧内编码模式中,同时作为宏块边沿和图像边沿的像素,滤波强度为0,不进行滤波。
3.2.2 判断滤波条件
判断滤波条件的方法定义在8.7.2.2节,所需数据包括边界两侧的4个像素值p0/p1/q0/q1,颜色空间分量标志位chromaEdgeFlag,边界滤波强度bS,所属宏块的量化参数QPp/QPq,以及在视频头部读取到的两个语法元素filterOffsetA/filterOffsetB。
首先计算边界相邻像素所属宏块量化参数QP的平均值:
qPav =(qPp +qPq +1)>>1;
计算查表所需要的两个索引值:
indexA = Clip3(0, 51, qPav + filterOffsetA);
indexB = Clip3(0, 51, qPav + filterOffsetB);
根据计算得到的indexA和indexB,通过在表8-16中查找对应的alpha和beta值:
根据上述计算得到的信息,可以获得滤波判决条件:
filterSamplesFlag=(bS!=0 && Abs(p0 −q0 )<α && Abs(p1 −p0 )<β && Abs(q1 −q0 )<β);
3.2.3 滤波边界像素
针对边界滤波强度不同的像素点,采用的方法也相应不同。滤波过程修改的像素值包括边界两边的相邻像素以及分别向两个方向扩展两个的像素值,即根据边界左右对称的6个相邻像素。
对于滤波强度为4的像素点(即非作为图像边缘的宏块边界),采用如下方法进行滤波:
如果滤波亮度分量,且满足ap <β && Abs(p0 −q0 )<((α>>2)+2)时,边界左侧或上方的三个滤波后像素的计算方法为:
- p′0 =(p2 +2p1 +2p0 +2*q0 +q1 +4)>>3
- p′1 =(p2 +p1 +p0 +q0 +2)>>2
- p′2 =(2p3 +3p2 +p1 +p0 +q0 +4)>>3
右侧或下方的三个滤波后像素的计算方法为:
- q′0 =(p1 +2p0 +2q0 +2*q1 +q2 +4)>>3
- q′1 =(p0 +q0 +q1 +q2 +2)>>2
- q′2 =(2q3 +3q2 +q1 +q0 +p0 +4)>>3
如果滤波色度分量,或者不满足ap <β && Abs(p0 −q0 )<((α>>2)+2)时,边界左侧或上方的三个滤波后像素的计算方法为:
- p′0 =(2*p1 +p0 +q1 +2)>>2
- p′1 = p1
- p′2 = p2
右侧或下方的三个滤波后像素的计算方法为:
- q′0 =(2*q1 +q0 +p1 +2)>>2
- q′1 = q1
- q′2 = q2
对于滤波强度为3的像素点(即内部块边沿),采用的滤波方法略复杂:
首先依据上面获得的indexA和边界滤波强度查表获得C0值,该值由标准文档中的表8-17获得。
获取参考像素的二级差分值:
ap =Abs(p2 −p0 )
aq =Abs(q2 −q0 )
计算阈值变量tc:
亮度:tC =tC0 +((ap <β)?1:0)+((aq <β)?1:0)
色度:tC =tC0 +1
计算边界相邻像素:
Δ =Clip3(−tC,tC,((((q0 −p0 )<<2)+(p1 −q1 )+4)>>3)) p′0 = Clip1( p0 + Δ )
q′0 = Clip1( q0 − Δ )
对亮度分量,p1/q1的推导方式为:
p′1 =p1 +Clip3(−tC0,tC0,(p2 +((p0 +q0 +1)>>1)−(p1 <<1)) >> 1)
q′1 =q1 +Clip3(−tC0,tC0,(q2 +((p0 +q0 +1)>>1)−(q1 <<1)) >> 1)
其它,p2/q2以及色度分量的p1/q1/p2/q2像素维持原值不变。