FFmpeg HEVC 环路滤波Bug分析一

和以往的视频编码标准类似,HEVC仍采用基于块的混合编码框架,一些失真效应仍然存在,如方块效应、振铃效应、颜色偏差以及图像模糊等等。为了解决这些问题,HEVC中采用了环路滤波技术,它其实是一种用于解码端的后处理滤波技术,主要包括去块滤波(Deblocking Filter,DBF)和样点自适应补偿(Sample Adaptive Offset,SAO)。

DBF的作用与H.264类似,主要是去除方块效应,造成方块效应的主要原因有三个:各个块的变换、量化编码过程相互独立(相当于对各个块使用了不同参数的滤波器分别滤波,因此各块引入的量化误差大小及其分布特性相互独立,导致相邻块边界的不连续);运动补偿预测过程中,相邻块的预测值可能来自于不同图像的不同位置,导致预测残差信号在块边界产生数值的不连续;时域预测技术使得参考图像中存在的边界不连续可能会传递到后续编码图像;需要注意的是,帧内预测采用的是解码宏块像素作为下个帧内预测的参考,而帧间预测则是采用经环路滤波后的解码宏块像素作为运动预测参考图像。SAO是HEVC中的新技术。用于改善振铃效应,造成振铃效应的原因是:高频信息的丢失(HEVC仍采用基于块的DCT或者DST变换,并在频域对变换系数进行量化,对于图像里的强边缘,由于高频交流系数的量化失真,解码后会在边缘周围产生波纹现象,即吉布斯现象,振铃效应会严重影响视频的主客观质量)。

环路滤波以CTB为单位进行处理,且DBF先于SAO进行处理。DBF的处理顺序是:首先对整个图像的垂直边缘进行水平滤波,然后对水平边缘进行垂直滤波。SAO算法有边界补充和边带补偿两种。由于DBF和SAO两种滤波器之间的先后关系,在实现时可以有多种不同的实现:可以先对整帧做DBF,然后做SAO,标准的HM就是这样做的;也可以先对一个CTB做DBF,然后对另一个符合条件的CTB做SAO,FFmpeg中就是这样子做的;也可以先对一个CTB行做DBF然后再对一个CTB行做SAO,ittiam HEVC decoder(Ittiam是家印度公司,他们的解码器被google集成到了Android源代码里)。ittiam HEVC decoder是目前已知的最快的开源解码器,当然最快并不是因为它的滤波部分的原因,主要是由于它其他方面的设计:比如指令集的优化,参数解析和解码的顺序,以及数据的存储设计等。

下面主要针对FFmpeg中DBF和SAO部分的一个Bug进行分析。

在HEVC标准中DBF以CTB为单位,在处理的时候以8×8的块为单位进行滤波,滤波是先对垂直边界做水平滤波,然后对水平边界做垂直滤波,如下图所示,先做AB边的滤波,再做DE边的滤波,最后做AC边滤波。在做AB边的滤波的时候需要用到AB边左右各四个像素值,也就是由133、140、245和252所围成的四边形区域的像素值,滤波的同时可能会改变134、139、246和251所围成的四边形区域的像素值。做DE边滤波是也可能会改变对应的像素值,最后对AC边进行滤波,在对AC边进行滤波的时候用到的像素值是已经经过垂直滤波处理过后的像素值。

FFmpeg HEVC 环路滤波Bug分析一

 

SAO是在DBF之后,用到的是经过DBF处理之后的像素值。如下图所示,图中每个小方格表示一个CTB,假设当前处理的是第12个CTB,由于帧内预测需要使用未经DBF滤波的原始像素值,所以处理第12个CTB的时候,第9、10、11和12的DBF都不能做,因为,如果做了DBF滤波,那么第13、14、15和16等CTB在做帧内预测的时候就无法获取到未经滤波的像素值,那么就只能做第7个CTB的DBF,做第2个CTB的SAO操作,这是FFmpeg的实现设计。当SAO采用边界补偿的方式,并且符合第三种对角类型的时候,处理第7个CTB需要使用第12个CTB左上角的一个像素值,但此时第12个CTB还没有经过DBF处理,显然不可行,所以第7个CTB的DBF处理完成的时候,只能处理第2个CTB的SAO。在处理第7个CTB的DBF的时候,最上面的边的最右边的8个像素不能处理,需要等到第8个CTB的最左边的垂直边处理完成后才能处理第7个CTB最上面边的最右边的8个像素,因为,处理第8个CTB的最左边的垂直边的时候会改变第7个CTB的像素值,在这之后再处理第7个CTB的上面边的8个像素,才符合整体上先垂直边,然后水平边的设计思路。

FFmpeg HEVC 环路滤波Bug分析一FFmpeg HEVC 环路滤波Bug分析一

 

上面所说的FFmpeg中对DBF和SAO的设计,存在一个小小的缺陷,那就是针对CTB的大小为16的时候,色度分量的滤波会出现问题。因为亮度分量水平边延迟了8个像素进行滤波,针对420采样比例的视频,色度分量将会延迟16个像素,也就是说,当处理第7个CTB的DBF时,虽然亮度是处理的第7个,但是色度实际上处理的是第6个CTB的DBF,如果此时对第2个CTB进行SAO滤波,而且碰巧也是第三种边界滤波类型,那么将导致错误,所以此时只能处理第一个CTB的SAO。只需要对源代码做稍微的改动,即可修复这个问题。