写在前面
接着上次的博客写,上次已经介绍完了POSHE部分,也为这次写BERF做了一些铺垫。在这次正式开始之前,还是先放两张图片,以说明执行BERF的必要性:
![](https://i-blog.csdnimg.cn/blog_migrate/a415e9e95cb7647e94c12779f6dc1f6f.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/fbc93ab1f69cf2a93c12eb96c3760ae5.jpeg)
从上面两幅图中就可以看出,仅执行POSHE会产生不可避免的快效应,尤其是边界产生了线。为了抑制这种干扰,论文提出来一种名为BERF(blocking effect reduction filter)的滤波器,中文叫块效应抑制滤波器。
原理
步骤:
本质上就是一种空间域的滤波,原理比较简单,我就直接写步骤:
1、判断是否产生块效应。论文没有给出具体的判断方法,按照我的理解,就是计算经过POSHE处理后图像的子块边界相邻两侧两个像素的灰度差异f1,同时计算原图的子块边界相邻两侧两个像素的灰度差异f2,如果f1-f2大于s,则产生了块效应,s可取3~4。比如,边界某像素在图像矩阵第5行,则边界相邻两侧两个像素就分别是第4和第6行。
2、若产生块效应,则计算第一步中相邻两侧两个像素的灰度平均值,并将该边界像素的灰度值替换为平均值。
3、在垂直于边界的方向上,以递增/递减顺序的像素值被缓慢变化的值替换,而且而是从上步计算的平均值开始。简单地说,就是在垂直于边界的方向上,从平均值开始,将本来正在处于递增或递减的像素值,代替为缓慢递增或递减的像素值。比如,边界为第5行,平均灰度值为100,那么这个边界像素的灰度值被替换为100,将另外两侧相邻像素中较大的像素替换为104,较小的替换为96,然后沿着垂直于边界的方向,如果不满足终止条件,就会一直替换下去,递增的一侧就会是104、108、112、116....,递减的一侧就会是96、92、88、84.........,直至满足终止条件。
终止条件:
论文用文字对3个终止条件进行了描述,但是我觉得用文字描述很不直观:
我用公式描述吧,以行边界(垂直边界方向就是列方向咯),且像素向上递增为例:
用x1表示当前已经执行过BERF的像素的灰度值(论文写的pixels that have previously have previously...),x2表示下一个准备执行BERF的像素的灰度值(论文写的the present pixel)。
条件1:x2-x1<4(step size),step size可取4及以下的值;
条件2:x2-x1> 40(large enough),large enough可取40及以上的值;
条件3:x2>X1。
以上就是以行边界(垂直边界方向就是列方向咯),且像素向上递增情况下的终止条件,我们编程时当然用的是执行条件咯:
以上3个终止条件改写成执行条件就是:
4<=x2-x1<=40;
很简单了有木有。其他情况的条件可以依次类推,或者看我的程序。
代码
结果图开篇已经放了,直接把代码放上吧,代码有点多,但都是相似的,搞懂一种情况即可:
PS:这里只放了BERF的代码,整个可以运行的代码在:github
/***************************************
BERF滤波器
src - 输入图像
dst - 输出图像
step_r - 子块垂直移动步长
step_c - 子块水平移动步长
step_levels - 灰度级缓慢变化的步长
large_dir - 最大灰度差异
****************************************/
void BERF(cv::Mat& src, cv::Mat& dst, int step_r, int step_c , int step_levels, int large_dir){
//处理子块的行边界(横向边界)
for (int i = 0; i < dst.rows;){
for (int j = 0; j < dst.cols; ++j){
if (i - 1 >= 0 && i + 1 <= dst.rows - 1){ //图像边界判断,先排除第一行和最后一行
//块效应检查
int dfacter_newsrc = abs(src.at<uchar>(i, j) - src.at<uchar>(i + 1, j)) + abs(src.at<uchar>(i, j) - src.at<uchar>(i - 1, j)); //原图子块边界与相邻两侧像素的灰度差异
int dfacter_dst = abs(dst.at<uchar>(i, j) - dst.at<uchar>(i + 1, j)) + abs(dst.at<uchar>(i, j) - dst.at<uchar>(i - 1, j)); //POSHEed图子块边界与相邻两侧像素的灰度差异
if (dfacter_dst - dfacter_newsrc > step_levels){
//存在块效应执行BERF
int b = 0;
int ave_bound = (int)(dst.at<uchar>(i - 1, j) + dst.at<uchar>(i + 1, j)) / 2;
dst.at<uchar>(i, j) = ave_bound;
if (dst.at<uchar>(i - 1, j) > dst.at<uchar>(i + 1, j)){ //垂直于子块边界,向上递增方向( increasing rule)
if (i - 2 - b >= 0){ //图像边界判断(下一位置像素)
int pixel_present_add = dst.at<uchar>(i - 1 - b, j);
int pixel_next_add = dst.at<uchar>(i - 2 - b, j);
pixel_present_add = ave_bound + step_levels;
while (i - 2 - b >= 0 && pixel_next_add - pixel_present_add >= step_levels && pixel_next_add - pixel_present_add <= large_dir){
pixel_next_add = pixel_present_add + step_levels;
b += 1;
if (i - 2 - b >= 0){ //图像边界判断(下一位置像素)
pixel_present_add = dst.at<uchar>(i - 1 - b, j);
pixel_next_add = dst.at<uchar>(i - 2 - b, j);
}
}
}
//垂直于子块边界,向下递减方向( decreasing rule)
if (i + 2 + b <dst.rows){ //图像边界判断
int pixel_present_dec = dst.at<uchar>(i + 1 + b, j);
int pixel_next_dec = dst.at<uchar>(i + 2 + b, j);
pixel_present_dec = ave_bound - step_levels;
while (i + 2 + b >= 0 && pixel_present_dec - pixel_next_dec >= step_levels && pixel_present_dec - pixel_next_dec <= large_dir){
pixel_next_dec = pixel_present_dec - step_levels;
b += 1;
if (i + 2 + b >= 0){ //图像边界判断
pixel_present_dec = dst.at<uchar>(i + 1 + b, j);
pixel_next_dec = dst.at<uchar>(i + 2 + b, j);
}
}
}
}
else if (dst.at<uchar>(i - 1, j) < dst.at<uchar>(i + 1, j)){ 垂直于子块边界,向下递增方向( increasing rule)
if (i + 2 + b < dst.rows){ //图像边界判断
int pixel_present_add = dst.at<uchar>(i + 1 + b, j);
int pixel_next_add = dst.at<uchar>(i + 2 + b, j);
pixel_present_add = ave_bound + step_levels;
while (i + 2 + b >= 0 && pixel_next_add - pixel_present_add >= step_levels && pixel_next_add - pixel_present_add <= large_dir){
pixel_next_add = pixel_present_add + step_levels;
b += 1;
if (i + 2 + b >= 0){ //图像边界判断
pixel_present_add = dst.at<uchar>(i + 1 + b, j);
pixel_next_add = dst.at<uchar>(i + 2 + b, j);
}
}
}
//垂直于子块边界,向上递减方向( decreasing rule)
if (i - 2 - b >= 0){ //图像边界判断
int pixel_present_dec = dst.at<uchar>(i - 1 - b, j);
int pixel_next_dec = dst.at<uchar>(i - 2 - b, j);
pixel_present_dec = ave_bound - step_levels;
while (i - 2 - b >= 0 && pixel_present_dec - pixel_next_dec >= step_levels && pixel_present_dec - pixel_next_dec <= large_dir){
pixel_next_dec = pixel_present_dec - step_levels;
b += 1;
if (i - 2 - b >= 0){ //图像边界判断
pixel_present_dec = dst.at<uchar>(i - 1 - b, j);
pixel_next_dec = dst.at<uchar>(i - 2 - b, j);
}
}
}
}
}
}
}
i += step_r;
}
//处理子块的列边界(纵向向边界)
for (int j = 0; j <dst.cols;){
for (int i = 0; i < dst.rows; ++i){
if (j - 1 >= 0 && j + 1 <= dst.cols - 1){ //图像边界判断,先排除第一列和最后一列
//块效应检查
int dfacter_newsrc = abs(src.at<uchar>(i, j) - src.at<uchar>(i, j + 1)) + abs(src.at<uchar>(i, j) - src.at<uchar>(i, j - 1));
int dfacter_dst = abs(dst.at<uchar>(i, j) - dst.at<uchar>(i, j + 1)) + abs(dst.at<uchar>(i, j) - dst.at<uchar>(i, j + 1));
if (dfacter_dst - dfacter_newsrc > step_levels){
//存在块效应执行BERF
int b = 0;
int ave_bound = (int)(dst.at<uchar>(i, j - 1) + dst.at<uchar>(i, j + 1)) / 2;
dst.at<uchar>(i, j) = ave_bound;
if (dst.at<uchar>(i, j - 1) > dst.at<uchar>(i, j + 1)){ //垂直于子块边界,向左递增方向( increasing rule)
if (j - 2 - b >= 0){ //图像边界判断
int pixel_present_add = dst.at<uchar>(i, j - 1 - b);
int pixel_next_add = dst.at<uchar>(i, j - 2 - b);
pixel_present_add = ave_bound + step_levels;
while (j - 2 - b >= 0 && pixel_next_add - pixel_present_add >= step_levels && pixel_next_add - pixel_present_add <= large_dir){
pixel_next_add = pixel_present_add + step_levels;
b += 1;
if (j - 2 - b >= 0){ //图像边界判断
pixel_present_add = dst.at<uchar>(i, j - 1 - b);
pixel_next_add = dst.at<uchar>(i, j - 2 - b);
}
}
}
//垂直于子块边界,向右递减方向( decreasing rule)
if (j + 2 + b <dst.cols){ //图像边界判断
int pixel_present_dec = dst.at<uchar>(i, j + 1 + b);
int pixel_next_dec = dst.at<uchar>(i, j + 2 + b);
pixel_present_dec = ave_bound - step_levels;
while (j + 2 + b >= 0 && pixel_present_dec - pixel_next_dec >= step_levels && pixel_present_dec - pixel_next_dec <= large_dir){
pixel_next_dec = pixel_present_dec - step_levels;
b += 1;
if (j + 2 + b >= 0){ //图像边界判断
pixel_present_dec = dst.at<uchar>(i, j + 1 + b);
pixel_next_dec = dst.at<uchar>(i, j + 2 + b);
}
}
}
}
else if (dst.at<uchar>(i, j - 1) < dst.at<uchar>(i, j + 1)){ 垂直于子块边界,向右递增方向( increasing rule)
if (j + 2 + b < dst.cols){ //图像边界判断
int pixel_present_add = dst.at<uchar>(i, j + 1 + b);
int pixel_next_add = dst.at<uchar>(i, j + 1 + b);
pixel_present_add = ave_bound + step_levels;
while (j + 2 + b >= 0 && pixel_next_add - pixel_present_add >= step_levels && pixel_next_add - pixel_present_add <= large_dir){
pixel_next_add = pixel_present_add + step_levels;
b += 1;
if (j + 2 + b >= 0){ //图像边界判断
pixel_present_add = dst.at<uchar>(i, j + 1 + b);
pixel_next_add = dst.at<uchar>(i, j + 2 + b);
}
}
}
//垂直于子块边界,向左递减方向( decreasing rule)
if (j - 2 - b >= 0){ //图像边界判断
int pixel_present_dec = dst.at<uchar>(i, j - 1 - b);
int pixel_next_dec = dst.at<uchar>(i, j - 2 - b);
pixel_present_dec = ave_bound - step_levels;
while (j - 2 - b >= 0 && pixel_present_dec - pixel_next_dec >= step_levels && pixel_present_dec - pixel_next_dec <= large_dir){
pixel_next_dec = pixel_present_dec - step_levels;
b += 1;
if (j - 2 - b >= 0){ //图像边界判断
pixel_present_dec = dst.at<uchar>(i, j - 1 - b);
pixel_next_dec = dst.at<uchar>(i, j - 2 - b);
}
}
}
}
}
}
}
j += step_c;
}
}