源码阅读 x264 - 去块效应滤波
x264_deblock_init()
中初始化了一系列环路滤波函数。
- 包含 "v" 的是垂直滤波器,用于处理水平边界;包含 "h" 的是水平滤波器,用于处理垂直边界。
- 包含 "luma" 的是亮度滤波器,包含 "chroma" 的是色度滤波器。
- 包含 "intra" 的是处理边界强度 Bs 为 4 的强滤波器,不包含 "intra" 的是普通滤波器。
x264_deblock_init
函数代码如下所示:
void x264_deblock_init(uint32_t cpu, x264_deblock_function_t *pf, int b_mbaff) {
pf->deblock_luma[1] = deblock_v_luma_c;
pf->deblock_luma[0] = deblock_h_luma_c;
pf->deblock_chroma[1] = deblock_v_chroma_c;
pf->deblock_h_chroma_420 = deblock_h_chroma_c;
pf->deblock_h_chroma_422 = deblock_h_chroma_422_c;
pf->deblock_luma_intra[1] = deblock_v_luma_intra_c;
pf->deblock_luma_intra[0] = deblock_h_luma_intra_c;
pf->deblock_chroma_intra[1] = deblock_v_chroma_intra_c;
pf->deblock_h_chroma_420_intra = deblock_h_chroma_intra_c;
pf->deblock_h_chroma_422_intra = deblock_h_chroma_422_intra_c;
pf->deblock_luma_mbaff = deblock_h_luma_mbaff_c;
pf->deblock_chroma_420_mbaff = deblock_h_chroma_mbaff_c;
pf->deblock_luma_intra_mbaff = deblock_h_luma_intra_mbaff_c;
pf->deblock_chroma_420_intra_mbaff = deblock_h_chroma_intra_mbaff_c;
pf->deblock_strength = deblock_strength_c;
/* 此处省略大量平台的汇编函数初始化代码 */
......
}
环路滤波分类
环路滤波器根据滤波的强度可以分为两种:
- 普通滤波器。针对边界的
Bs
(边界强度)为 1、2、3 的滤波器。 - 强滤波器。针对边界的
Bs
(边界强度)为 4 的滤波器。
普通滤波器
此时环路滤波涉及到方块边界周围的 6 个点(边界两边各 3 个点):\(p2,p1,p0,q0,q1,q2\) 需要处理 4 个点(边界两边各 2 个点,只以 p 侧的点为例):
p0' = p0 + (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3
p1' = (p2 + ( ( p0 + q0 + 1) >> 1) – 2p1 ) >> 1
强滤波器
此时环路滤波涉及到方块边界周围的 8 个点(边界两边各 4 个点):\(p3,p2,p1,p0,q0,q1,q2,q3\) 需要处理 6 个点(边界两边各 3 个点,只以 p 侧的点为例):
p0' = (p2 + 2*p1 + 2*p0 + 2*q0 + q1 + 4) >> 3
p1' = (p2 + p1 + p0 + q0 + 2) >> 2
p2' = (2*p3 + 3*p2 + p1 + p0 + q0 + 4) >> 3
边界强度的计算和边界分析方法详见:编解码技术:H264 - 去块效应滤波
普通滤波函数
deblock_v_luma_c
deblock_v_luma_c()
是一个普通强度的垂直滤波器,用于处理边界强度 Bs
为 1,2,3 的水平边界。该函数的定义位于 common/deblock.c
,如下所示:
/*
* 去块效应滤波 - 普通滤波,Bs 为 1,2,3
* 垂直 Vertical 滤波器
* x
* x
* 边界 ==========
* x
* x
*/
static void deblock_v_luma_c(pixel *pix, intptr_t stride, int `alpha`, int beta, int8_t *tc0) {
// xstride = stride(用于选择滤波的像素)
// ystride = 1
deblock_luma_c(pix, stride, 1, alpha, beta, tc0);
}
deblock_v_luma_c()
调用了另一个函数 deblock_luma_c()
。需要注意传递给 deblock_luma_c()
是一个水平滤波器和垂直滤波器都会调用的通用滤波器函数。在这里传递给 deblock_luma_c()
第二个参数 xstride
的值为 stride
,第三个参数 ystride
的值为 1。
static inline void deblock_luma_c(pixel *pix, intptr_t xstride, intptr_t ystride, int alpha, int beta, int8_t *tc0) {
for (int i = 0; i < 4; i++) {
if (tc0[i] < 0 ) {
pix += 4*ystride;
continue;
}
for (int d = 0; d < 4; d++, pix += ystride)
deblock_edge_luma_c(pix, xstride, alpha, beta, tc0[i] );
}
}
具体的滤波在 deblock_edge_luma_c()
中完成。处理完一个像素后,会继续处理与当前像素距离为 ystride
的像素
static inline void deblock_edge_luma_c(pixel *pix, intptr_t xstride, int alpha, int beta, int8_t tc0) {
/*
* p 和 q
* 如果 xstride = stride,ystride = 1
* 就是处理纵向的 6 个像素
* 对应的是方块的横向边界的滤波,即如下所示:
* p2
* p1
* p0
* ===== 图像边界 =====
* q0
* q1
* q2
*
* 如果 xstride = 1,ystride = stride
* 就是处理纵向的 6 个像素
* 对应的是方块的横向边界的滤波,即如下所示:
* ||
* p2 p1 p0 || q0 q1 q2
* ||
* 边界
*
* 注意:这里乘的是 xstride
*/
int p2 = pix[-3*xstride];
int p1 = pix[-2*xstride];
int p0 = pix[-1*xstride];
int q0 = pix[0*xstride];
int q1 = pix[1*xstride];
int q2 = pix[2*xstride];
// 计算方法参考相关的标准
// alpha 和 beta 是用于检查图像内容的 2 个参数
// 只有满足 if() 里面 3 个取值条件的时候(只涉及边界旁边的 4 个点),才会滤波
if (abs( p0 - q0) < alpha && abs( p1 - p0 ) < beta && abs( q1 - q0 ) < beta ) {
int tc = tc0;
int delta;
// 上面 2 个点(p0,p2)满足条件的时候,滤波 p1
if (abs( p2 - p0) < beta ) {
// p1' = (p2 + ( ( p0 + q0 + 1) >> 1) – 2p1 ) >> 1
// = (p2 + ( ( p0 + q0 + 1) >> 1)) >> 1 - p1
if (tc0) pix[-2*xstride] = p1 + x264_clip3( (( p2 + ((p0 + q0 + 1) >> 1)) >> 1) - p1, -tc0, tc0 );
tc++;
}
// 下面 2 个点(q0,q2)满足条件的时候,滤波 q1
if (abs( q2 - q0) < beta ) {
if (tc0) pix[1*xstride] = q1 + x264_clip3( (( q2 + ((p0 + q0 + 1) >> 1)) >> 1) - q1, -tc0, tc0 );
tc++;
}
delta = x264_clip3((((q0 - p0) << 2) + (p1 - q1) + 4) >> 3, -tc, tc );
// p0' = p0 + (((q0 - p0) << 2) + (p1 - q1) + 4) >> 3
pix[-1*xstride] = x264_clip_pixel( p0 + delta ); /* p0' */
pix[0*xstride] = x264_clip_pixel( q0 - delta ); /* q0' */
}
}
static inline int x264_clip3(int v, int i_min, int i_max) { // 将 v 限制在 i_min 和 i_max 之间
return ((v < i_min) ? i_min : (v > i_max) ? i_max : v );
}
static inline pixel x264_clip_pixel(int x) { // 将 x 限制在 0 和 255 之间
return ((x & ~PIXEL_MAX) ? (-x)>>31 & PIXEL_MAX : x );
}
deblock_h_luma_c
deblock_h_luma_c()
是一个普通强度的水平滤波器,用于处理边界强度 Bs
为 1,2,3 的垂直边界。该函数的定义如下所示:
/*
* 去块效应滤波 - 普通滤波,Bs 为 1,2,3
* 水平 Horizontal 滤波器
* 边界
* ||
* x x x || x x x
* ||
*/
static void deblock_h_luma_c(pixel *pix, intptr_t stride, int alpha, int beta, int8_t *tc0)
{
// xstride = 1(用于选择滤波的像素)
// ystride = stride
deblock_luma_c(pix, 1, stride, alpha, beta, tc0);
}
和 deblock_v_luma_c()
类似,deblock_h_luma_c()
同样调用了 deblock_luma_c()
函数。唯一的不同在于它传递给 deblock_luma_c()
的第 2 个参数 xstride
为 1,第 3 个参数 ystride
为 stride
。
强滤波函数
deblock_v_luma_intra_c
deblock_v_luma_intra_c()
是一个强滤波的垂直滤波器,用于处理边界强度 Bs
为 4 的水平边界。该函数的定义位于 common/deblock.c
,如下所示:
/*
* 垂直 Vertical 强滤波器 - Bs 为 4
* 边界
* x
* x
* 边界 ----------
* x
* x
*/
static void deblock_v_luma_intra_c(pixel *pix, intptr_t stride, int alpha, int beta) {
// 注意
// xstride = stride
// ystride = 1
// 处理完 1 个像素点之后,pix 增加 ystride
// 水平滤波和垂直滤波通用的强滤波函数
deblock_luma_intra_c(pix, stride, 1, alpha, beta);
}
deblock_v_luma_intra_c()
调用了另一个函数 deblock_luma_intra_c()
。需要注意 deblock_luma_intra_c()
是一个水平滤波器和垂直滤波器都会调用的通用滤波器函数。在这里传递给 deblock_luma_intra_c()
第二个参数 xstride
的值为 stride
,第三个参数 ystride
的值为 1。
// 水平滤波和垂直滤波通用的强滤波函数 - Bs 为 4
static inline void deblock_luma_intra_c(pixel *pix, intptr_t xstride, intptr_t ystride, int alpha, int beta) {
// 循环处理 16 个点
// 处理完 1 个像素点之后,pix 增加 ystride
for (int d = 0; d < 16; d++, pix += ystride)
// 每次处理 1 个点
deblock_edge_luma_intra_c(pix, xstride, alpha, beta);
}
具体的滤波在 deblock_edge_luma_intra_c()
中完成。处理完一个像素后,会继续处理与当前像素距离为 ystride
的像素。
// 水平滤波和垂直滤波通用的强滤波函数 - 处理 1 个点 - Bs 为 4
// 注意涉及到 8 个像素
static inline void deblock_edge_luma_intra_c(pixel *pix, intptr_t xstride, int alpha, int beta) {
/*
* 如果 xstride = stride,ystride = 1
* 就是处理纵向的 6 个像素
* 对应的是方块的横向边界的滤波。如下所示:
* p2
* p1
* p0
* ===== 图像边界 =====
* q0
* q1
* q2
*
* 如果 xstride = 1,ystride = stride
* 就是处理纵向的 6 个像素
* 对应的是方块的横向边界的滤波,即如下所示:
* ||
* p2 p1 p0 || q0 q1 q2
* ||
* 边界
* 注意:这里乘的是 xstride
*/
int p2 = pix[-3*xstride];
int p1 = pix[-2*xstride];
int p0 = pix[-1*xstride];
int q0 = pix[0*xstride];
int q1 = pix[1*xstride];
int q2 = pix[2*xstride];
// 满足条件的时候,才滤波
if (abs( p0 - q0) < alpha && abs( p1 - p0 ) < beta && abs( q1 - q0 ) < beta ) {
if (abs( p0 - q0) < ((alpha >> 2) + 2) ) {
if (abs( p2 - p0) < beta ) /* p0', p1', p2' */ {
const int p3 = pix[-4*xstride];
// p0' = (p2 + 2*p1 + 2*p0 + 2*q0 + q1 + 4) >> 3
pix[-1*xstride] = ( p2 + 2*p1 + 2*p0 + 2*q0 + q1 + 4 ) >> 3;
// p1' = (p2 + p1 + p0 + q0 + 2) >> 2
pix[-2*xstride] = ( p2 + p1 + p0 + q0 + 2 ) >> 2;
// p2' = (2*p3 + 3*p2 + p1 + p0 + q0 + 4) >> 3
pix[-3*xstride] = ( 2*p3 + 3*p2 + p1 + p0 + q0 + 4 ) >> 3;
} else /* p0' */ pix[-1*xstride] = ( 2*p1 + p0 + q1 + 2 ) >> 2;
if (abs( q2 - q0) < beta ) /* q0', q1', q2' */ {
const int q3 = pix[3*xstride];
pix[0*xstride] = ( p1 + 2*p0 + 2*q0 + 2*q1 + q2 + 4 ) >> 3;
pix[1*xstride] = ( p0 + q0 + q1 + q2 + 2 ) >> 2;
pix[2*xstride] = ( 2*q3 + 3*q2 + q1 + q0 + p0 + 4 ) >> 3;
} else /* q0' */ pix[0*xstride] = ( 2*q1 + q0 + p1 + 2 ) >> 2;
} else /* p0', q0' */ {
pix[-1*xstride] = ( 2*p1 + p0 + q1 + 2 ) >> 2;
pix[0*xstride] = ( 2*q1 + q0 + p1 + 2 ) >> 2;
}
}
}