本文介绍了如何使用OpenCV对图像进行卷积滤波:
- 噪声
- 图像卷积
- 线性滤波
- 非线性滤波
噪声
图像噪声,通常指图像中除了成像物体之外的其它信息(例如下面这张图片,红色方框区域部分有一些灰色斑点),这些额外的错误信息干扰了成像物体的显示,影响成像质量,所以往往需要通过图像滤波来消除噪声。通常图像滤波分为线性和非线性滤波。
图像的卷积
对图像的滤波通常都是通过卷积操作来完成的。那么什么是卷积?
我们有一个二维图像和一个二维的滤波器(卷积核)。然后,对于图像的每一个像素点,不断移动卷积核,计算它的邻域像素和卷积核的对应元素的乘积,然后加起来,作为该像素位置的值。这就是图像的卷积。
滤波器一般具备以下特点:
1)滤波器的大小通常是奇数,这样方便确定它的中心,例如3x3,5x5或者7x7。有中心,也就有了半径的定义,例如5x5大小的核的半径是2, 7x7大小的核的半径是3。
2)滤波器矩阵所有的元素之和通常要等于1,这是为了保证滤波前后图像的总体亮度保持不变。
3)如果滤波器矩阵所有元素之和大于1,那么滤波后的图像就会比原图像的亮度更高,反之亮度就会更低。
4)注意滤波后的结果可能发生溢出。例如对于CV_8UC3类型,计算出现负数或者大于255的数值。对这种情况,通常将结果截断到0和255之间。
线性滤波
- 均值滤波
均值滤波其实就是对目标像素及周边像素取平均值后再填回目标像素的滤波方法。
如下图所示,我们采用了一个3x3的矩阵作为均值滤波矩阵。滤波矩阵里的每个值都是1/9,这样的话每个像素经过滤波后的值就是它自身加上邻近的8个像素的加权平均值。
下面是在OpenCV中创建并使用均值滤波的代码, OpenCV的滤波是通过filter2D()函数实现的。
Mat image = imread("../book.jpg");
Mat kernel = Mat::ones(3,3, CV_64F);
kernel = kernel/9;
Mat imgFiltered;
filter2D(image, imgFiltered, -1 , kernel);
imshow("Original", image);
imshow("Blur", imgFiltered);
我们应用上面的代码对带有噪声的图像(左侧)进行均值滤波,可以看到滤波后的图像(右侧)中的噪点没有那么明显了。但均值滤波同样带来了一个副作用,就是所有像素值都被它的邻近像素“均匀”了,因此图片出现了模糊。
- 高斯滤波
下图就是一个参考的5x5高斯滤波器:高斯模糊实质上也是一种均值滤波,不同的是,高斯滤波是按照加权平均的,距离中心店越近的点权重越大,距离越远的点权重越小。
OpenCV已经实现了高斯滤波的函数GaussianBlur(),我们可以直接调用。
Mat image = imread("../book.jpg");
Mat kernel = Mat::ones(5,5, CV_64F);
double SigmaX, SigmaY;
kernel = kernel / 25;
Mat imgFiltered;
GaussianBlur(image, imgFiltered, Size(5,5), SigmaX=0, SigmaY=0);
imshow("Original", image);
imshow("Blur", imgFiltered);
右图是高斯滤波的结果。由于考虑了邻近像素的权重,因此相比于均值滤波,输出图像的模糊程度减轻了。非线性滤波
- 中值滤波
中值滤波的思想很简单,取卷积核当中所覆盖像素中的中值作为锚点的像素值即可。
在OpenCV中使用中值滤波非常简单,直接调用medianBlur()并且指定卷积核的大小即可。下面的代码中使用了5x5的卷积核。
Mat image = imread("../book.jpg");
Mat imgFiltered;
medianBlur(image, imgFiltered, (5,5));
imshow("Original", image);
imshow("Blur", imgFiltered);
右图是滤波后的结果,可以看到噪声几乎被完全被滤除了。这也是中值滤波的特点,它对椒盐噪声的效果非常好。
- 双边滤波
双边滤波是图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
在OpenCV中可以使用bilateralFilter()来进行双边滤波。
Mat image = imread("../lena_noised.png");
Mat imgFiltered;
/* 第三个参数d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
* 第四个参数sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
* 第五个参数sigmaSpace, 坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,
从而使更大的区域内相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。*/
bilateralFilter(image, imgFiltered, 9, 75, 75);
imshow("Original", image);
imshow("Blur", imgFiltered);
注意bilateralFilter()需要设置合适的参数来取得理想的滤波效果。因为椒盐噪声是一种高频噪声,因此我们不能使用双边滤波器进行滤波。双边滤波器适用的场景是低频噪声+边缘较多的场景。例如下面的左图,使用双边滤波器在取得了比较好的去噪效果,同时也很好的保留了图像中的边缘信息。