框架结构
一帧的所有宏块的在重建完成解码像素之后,针对整个slice操作的,所以去块滤波的操作需要放在slice的解析中,对整个一帧数据
for (int idx = 0; idx < m_max_mb_number; idx++)
{
m_macroblocks[idx]->Deblock_macroblock();
}
1、获取当前是否为上边沿和左边沿宏块
2、获取slice heder中的标志位,确定当前slice是否需要进行去块滤波
3、分别进行垂直和水平方向的滤波
//<去块滤波><1>针对每个宏块的去块滤波
int CMacroblock::Deblock_macroblock()
{
int width_in_mb = m_slice->m_sps_active->Get_pic_width_in_mbs();
int height_in_mb = m_slice->m_sps_active->Get_pic_height_in_mbs();
int total_filter_strength = 0, filter_strength_arr[16] = { 0 };
bool filterLeftMbEdgeFlag = (m_mb_idx % width_in_mb != 0); //<去块滤波><1>是否是最左边的宏块
bool filterTopMbEdgeFlag = (m_mb_idx >= width_in_mb); //<去块滤波><1>不是上边沿应该要被滤波
//<去块滤波><1>不需要进行去块滤波的操作
if (m_slice->m_sliceHeader->m_disable_deblocking_filter_idc == 1)
{
return kPARSING_ERROR_NO_ERROR;
}
m_mb_alpha_c0_offset = m_slice->m_sliceHeader->m_slice_alpha_c0_offset; //<去块滤波><1>判断每条边界是否需要滤波的一个判断条件所需要的数据
m_mb_beta_offset = m_slice->m_sliceHeader->m_slice_beta_offset;
// Vertical filtering luma 1、垂直方向的滤波
// Horizontal filtering luma 2、水平方向的滤波
return kPARSING_ERROR_NO_ERROR;
}
计算滤波强度
参考标准文档8.7.2.1,下面只是针对帧内预测编码的情况,最左边的边沿对应的滤波强度为4,非最左边的边沿为3
//<去块滤波><2>计算滤波强度:edge边界序号 strength这条边沿的滤波强度数组 标准文档8.7.2.1
int CMacroblock::get_filtering_strength(int edge, int strength[16])
{
int total_strength = 0;
for (int idx = 0; idx < 16; idx++)
{
strength[idx] = (edge == 0) ? 4 : 3; //<去块滤波><2>只针对帧内预测edge == 0为宏块的边沿
total_strength += strength[idx];
}
return total_strength;
}
获取参考像素
下图中虚线表示的边沿1,左边是0,后面是2和3,;参考像素为第一列子宏块的4个和第二列子宏块的4个
//<去块滤波><5>
/*获取边沿上参考像素的值:pixel_arr读取到的参考数据的值
*target_mb_idx:左侧或者上边宏块序号
*edge:第几个边沿
*pix_idx:边沿的第几个像素
*/
int CMacroblock::get_edge_pixel_item(int dir, int target_mb_idx, int edge, int pix_idx, int luma, int pixel_arr[8])
{
CMacroblock *target = NULL;
int neighbor_blk_idx = -1, blk_idx = pix_idx / 4, pix_pos = pix_idx % 4;//<去块滤波><5>blk_idx当前宏块所处的子块从上至下的序号 pix_pos像素在当前子块中的位置
/*
For luma:
pix_idx ranges [0,15], so blk_idx means the block index the pixel belongs to, and pix_pos means the pixel index in the sub block.
*/
if (edge)// Internal Filtering
{
target = this;
neighbor_blk_idx = edge - 1; //<去块滤波><5>edge是从16*16宏块的最左边边沿开始的
}
else
{
target = m_slice->Get_macroblock_at_index(target_mb_idx);
neighbor_blk_idx = 3;
}
if (luma)
{
if (!dir) // Vertical
{
for (int idx = 0; idx < 4; idx++)
{
pixel_arr[idx] = target->m_reconstructed_block[blk_idx][neighbor_blk_idx][pix_pos][idx];
}
for (int idx = 4; idx < 8; idx++)
{
pixel_arr[idx] = m_reconstructed_block[blk_idx][edge][pix_pos][idx-4];
}
}
else //Horizontal
{
for (int idx = 0; idx < 4; idx++)
{
pixel_arr[idx] = target->m_reconstructed_block[neighbor_blk_idx][blk_idx][idx][pix_pos];
}
for (int idx = 4; idx < 8; idx++)
{
pixel_arr[idx] = m_reconstructed_block[edge][blk_idx][idx - 4][pix_pos];
}
}
}
return kPARSING_ERROR_NO_ERROR;
}
滤波边界像素
1、准备条件:准备bs(滤波强度)、p0(参考像素)、α、β
avg_qp = (m_mb_qp + m_slice->Get_macroblock_at_index(mb_idx)->m_mb_qp + 1) >> 1; //<去块滤波><6>边界两侧所属宏块qp的平均值
index_a = IClip(0, MAX_QP, avg_qp + m_mb_alpha_c0_offset); //<去块滤波><6>两个索引值,m_mb_alpha_c0_offset从当前的slice中读取出来的
index_b = IClip(0, MAX_QP, avg_qp + m_mb_beta_offset); //<去块滤波><6>IClip就是把输出的值限定到0到51内
alpha_val = ALPHA_TABLE[index_a]; //<去块滤波><6>α在标准文档表8-16中
beta_val = BETA_TABLE[index_b]; //<去块滤波><6>β
clip_table = CLIP_TAB[index_a]; //<去块滤波><6>在标准文档表8-17中
2、判断条件:(只有满足下面条件,当前边沿才是可以滤波的)
filterSamplesFlag = ( bS != 0 && Abs( p0 − q0 ) < α && Abs( p1 − p0 ) < β && Abs( q1 − q0 ) < β )
3、开始滤波边界像素
其实就是按照标准协议的公式,参考边沿两侧参考的像素点,进行边沿滤波
//<去块滤波><6>
/*pix_vals:滤波完输出的值
*strength:强度
*pixel_arr:参考像素
*strength_idx:边沿的第几个像素
*component:亮度还是色度
*满足这个才滤波:filterSamplesFlag = ( bS != 0 && Abs( p0 − q0 ) < α && Abs( p1 − p0 ) < β && Abs( q1 − q0 ) < β )
*/
int CMacroblock::filter_pixel(int *pix_vals, int alpha_val, int beta_val, int *clip_table, int strength[16], int pixel_arr[8], int strength_idx, int component)
{
int delta = -1, abs_delta = -1;
int C0 = -1, c0 = -1, RL0 = -1, dif = -1;
int filter_strength = strength[strength_idx];
for (int i = 0; i < 8; i++)
{
pix_vals[i] = pixel_arr[i];
}
//<去块滤波><6>需满足bS != 0
if (!filter_strength)
{
return kPARSING_ERROR_NO_ERROR;
}
RL0 = pixel_arr[4] + pixel_arr[3];
delta = pixel_arr[4] - pixel_arr[3];
abs_delta = abs(delta);
if (abs_delta >= alpha_val) //<去块滤波><6>需满足Abs( p0 − q0 ) < α
{
return kPARSING_ERROR_NO_ERROR;
}
C0 = clip_table[filter_strength];
int right_diff = abs(pixel_arr[4] - pixel_arr[5]) - beta_val, left_diff = abs(pixel_arr[3] - pixel_arr[2]) - beta_val;
int right_diff2_negative = 0, left_diff2_negative = 0, small_gleft_diff2_negative = 0;
if ((right_diff & left_diff) >= 0) //<去块滤波><6>需满足 Abs( p1 − p0 ) < β && Abs( q1 − q0 ) < β
{
return kPARSING_ERROR_NO_ERROR;
}
//<去块滤波><7>实现滤波操作
if (!component) // Luma
{
right_diff2_negative = (abs(pixel_arr[4] - pixel_arr[6]) - beta_val) < 0; //<去块滤波><7>ap = Abs(p2 − p0)
left_diff2_negative = (abs(pixel_arr[3] - pixel_arr[1]) - beta_val) < 0; //<去块滤波><7>aq = Abs( q2 − q0 )
if (filter_strength == 3) //<去块滤波><7>在8.7.2.3节中实现,左右各修改两个像素
{
c0 = C0 + right_diff2_negative + left_diff2_negative; //<去块滤波><7>tC = tC0 + ((ap < β) ? 1 : 0) + ((aq < β) ? 1 : 0)
dif = IClip(-c0, c0, ((delta << 2) + (pixel_arr[2] - pixel_arr[5]) + 4) >> 3); //<去块滤波><7>∆ = Clip3(−tC, tC, ((((q0 − p0) << 2) + (p1 − q1) + 4) >> 3))
pix_vals[3] = IClip(0, 255, pixel_arr[3] + dif); //<去块滤波><7>p'0 = Clip1( p0 + ∆ )
pix_vals[4] = IClip(0, 255, pixel_arr[4] - dif); //<去块滤波><7>q'0 = Clip1( q0 − ∆ )
if (left_diff2_negative)
{
pix_vals[2] += IClip(-C0, C0, (pixel_arr[1] + ((RL0 + 1) >> 1) - (pixel_arr[2] << 1)) >> 1);
}
if (right_diff2_negative)
{
pix_vals[5] += IClip(-C0, C0, (pixel_arr[6] + ((RL0 + 1) >> 1) - (pixel_arr[5] << 1)) >> 1);
}
}
else if (filter_strength == 4) //<去块滤波><7>在8.7.2.4节中实现,左右各修改3个
{
small_gleft_diff2_negative = ( abs_delta < ((alpha_val >> 2) + 2) );
right_diff2_negative &= small_gleft_diff2_negative; //<去块滤波><7>a < β && Abs( p0 − q0 ) < ( ( α >> 2 ) + 2 )
left_diff2_negative &= small_gleft_diff2_negative; //<去块滤波><7>ap < β && Abs(p0 − q0) < ((α >> 2) + 2)
pix_vals[4] = right_diff2_negative ? (pix_vals[2] + ((pix_vals[5] + RL0) << 1) + pix_vals[6] + 4) >> 3 : ((pix_vals[5] << 1) + pix_vals[4] + pix_vals[2] + 2) >> 2;
pix_vals[3] = left_diff2_negative ? (pix_vals[5] + ((pix_vals[2] + RL0) << 1) + pix_vals[1] + 4) >> 3 : ((pix_vals[2] << 1) + pix_vals[3] + pix_vals[5] + 2) >> 2;
pix_vals[5] = right_diff2_negative ? (pix_vals[6] + pix_vals[5] + pix_vals[4] + pix_vals[3] + 2) >> 2 : pix_vals[5];
pix_vals[2] = left_diff2_negative ? (pix_vals[1] + pix_vals[2] + pix_vals[3] + pix_vals[4] + 2) >> 2 : pix_vals[2];
pix_vals[6] = right_diff2_negative ? (((pix_vals[7] + pix_vals[6]) << 1) + pix_vals[6] + pix_vals[5] + RL0 + 4) >> 3 : pix_vals[6];
pix_vals[1] = left_diff2_negative ? (((pix_vals[0] + pix_vals[1]) << 1) + pix_vals[1] + pix_vals[2] + RL0 + 4) >> 3 : pix_vals[1];
}
}
return kPARSING_ERROR_NO_ERROR;
}