1. 中值滤波
摘要 :本文介绍 OpenCV 中两种常用的空域平滑滤波方法------中值滤波与双边滤波。中值滤波通过取邻域灰度中值去除椒盐噪声,适合消除孤立噪点;双边滤波在加权平均中同时考虑空间距离与灰度差异,能够在降噪的同时较好地保留图像边缘。文中分别给出
medianBlur()与bilateralFilter()的接口说明及完整 C++ 示例代码。
中值滤波用像素点领域灰度值的中值来代替该像素点的灰度值,也就是说用一片区域的中间值来代替所有值,这可以除去最大值和最小值。中值滤波对除去斑点噪声和椒盐噪声很有用,缺点是中值滤波花费的时间在均值滤波的5倍以上。
中值平滑也有核,但并不进行卷积计算,而是对核中所有像素值排序得到中间值,用该中间值来代替锚点值。中值滤波在数字图像处理中属于空域平滑滤波的内容。
在OpenCV中,利用medianBlur()来进行中值平滑,其声明如下:
cpp
void cv::medianBlur ( InputArray src, OutputArray dst, int ksize );
其中参数src表示输入图像,图像为1、3、4通道的图像,当模板尺寸为3或5时,图像深度只能为CV_8U、CV_16U、CV_32F中的一个。对于较大孔径尺寸的图片,图像深度只能是CV_8U。dst表示输出图像,尺寸和类型与输入图像一致,可以使用Mat::Clone以原图像为模板来初始化输出图像。ksize表示滤波模板的尺寸大小,必须是大于1的奇数,如
3、5、7......
【例6.3】实现中值滤波
在代码中,我们对图片进行了孔径为7的中值滤波。
cpp
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main()
{
Mat image = imread("lady.png"); // 载入原图
namedWindow("src"); //创建窗口
namedWindow("median filtering");//命名窗口
//显示原图
imshow("src", image);
//进行中值滤波操作
Mat out;
medianBlur(image, out, 7);//输入,输出,孔径为7
//显示效果图
imshow("median filtering", out);//显示中值滤波效果图
waitKey(0);
}

2. 双边滤波
当前介绍的滤波器都是为了平滑图像,问题是有些时候这些滤波器不仅削弱了噪声,还把边缘也给磨掉了。为了避免这样的情形(至少在一定程度上),我们可以使用双边滤波。类似于高斯滤波器,双边滤波器也给每一个邻域像素分配一个加权系数。这些加权系数包含两部分:第一部分的加权方式与高斯滤波一样,第二部分的权重取决于该邻域像素与当前像素的灰度差值。
双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。和其他滤波原理一样,双边滤波也采用加权平均的方法,用周边像素亮度值的加权平均代表某个像素的强度,所用的加权平均基于高斯分布。最重要的是,在计算中心像素的时候,双边滤波的权重不仅考虑了像素的欧氏距离(如普通的高斯低通滤波,只考虑了位置对中心像素的
影响),还考虑了像素范围域中的辐射差异(如卷积核中像素与中心像素之间的相似程
度、颜色强度、深度距离等)。
与高斯滤波相比,双边滤波能够更好地保存图像的边缘信息。其原理为一个与空间距离相关的高斯函数与一个与灰度距离相关的高斯函数相乘。
下面来看一个例子,使用4种不同的滤波器读取一幅图像,并显示平滑图像。
【例6.4】使用4种不同的滤波器读取并显示平滑图像

cpp
#include <iostream>
#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
int DELAY_CAPTION = 1500;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
Mat src; Mat dst;
char window_name[] = "Smoothing Demo";
int display_caption(const char* caption);
int display_dst(int delay);
int main(int argc, char** argv)
{
namedWindow(window_name, WINDOW_AUTOSIZE);
const char* filename = argc >= 2 ? argv[1] : "lena.png";
src = imread(samples::findFile(filename), IMREAD_COLOR);
if (src.empty())
{
printf(" Error opening image\n");
printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
return EXIT_FAILURE;
}
if (display_caption("Original Image") != 0)
{
return 0;
}
dst = src.clone();
if (display_dst(DELAY_CAPTION) != 0)
{
return 0;
}
if (display_caption("Homogeneous Blur") != 0)
{
return 0;
}
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
blur(src, dst, Size(i, i), Point(-1, -1));
if (display_dst(DELAY_BLUR) != 0)
{
return 0;
}
}
if (display_caption("Gaussian Blur") != 0)
{
return 0;
}
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
GaussianBlur(src, dst, Size(i, i), 0, 0);
if (display_dst(DELAY_BLUR) != 0)
{
return 0;
}
}
if (display_caption("Median Blur") != 0)
{
return 0;
}
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
medianBlur(src, dst, i);
if (display_dst(DELAY_BLUR) != 0)
{
return 0;
}
}
if (display_caption("Bilateral Blur") != 0)
{
return 0;
}
for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2)
{
bilateralFilter(src, dst, i, i * 2, i / 2);
if (display_dst(DELAY_BLUR) != 0)
{
return 0;
}
}
display_caption("Done!");
return 0;
}
int display_caption(const char* caption)
{
dst = Mat::zeros(src.size(), src.type());
putText(dst, caption, Point(src.cols / 4, src.rows / 2), FONT_HERSHEY_COMPLEX, 1,
Scalar(255, 255, 255));
return display_dst(DELAY_CAPTION);
}
int display_dst(int delay)
{
imshow(window_name, dst);
int c = waitKey(delay);
if (c >= 0) { return -1; }
return 0;
}