学习OpenCV(三)用filter2D实现矩阵掩码操作
在对数字图像进行处理时,我们一般都会在空间域(spatial domain)或者频域(frequency domain)中进行。所谓“空间域”,实际上指的是图像本身,在空间域上的操作常常是改变像素点的值,也就是经过一个映射(我们所做的变换,如滤波等),将原来的f(x,y)变换为新的g(x,y)。而“频域”,它的数学基础是法国学者傅里叶提出的傅里叶级数和随后发展起来的傅里叶变换。在这其中起到重要作用的,就是电子计算机的不断完善和快速傅里叶变换(FFT)算法的提出。这些使得傅里叶变换成为了一中有力的分析和变换工具。就像一列波,我们在时间上观察,每个时刻的幅值是一个时间的函数。而当我们变换角度,从频率域上去看,又会发现它是一系列正弦波的叠加,而这些正弦波的频率都会是某个基波频率的整数倍。可谓“横看成岭侧成峰”!
在空间域的操作主要可以分为两类:第一类是所谓的“图像强度变换”(Intensity Transform),另一类是所谓的“空间域图像滤波”(Spatial Filtering)。这两者的区别主要是处理方法的不同。前者对单个像素点进行操作,例如通过阈值函数实现图形的二值化,实现灰度平均等。而后者建立在邻域(neighborhood)的概念上,讲究的是利用一个矩阵核(Kernel)对一个小区域进行操作。今天这篇文章主要介绍的是后者,以及如何用OpenCV中的函数去实现。
我们先来看下面的这个公式,以及它的矩阵表示形式:
这个公式到它的矩阵表示形式的转换相信并不复杂,稍有线性代数知识就可以看懂。而这种操作可以用来进行图像增强。那么我们如何来使用OpenCV中的函数进行操作呢?我们要用到filter2D这个函数。可以在帮助文档中查到,filter2D函数的功能是:Convolves an image with the kernel,即如何实现图像的卷积运算。其函数原型如下:
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT ),可以用下面这个公式来说明:
让我们用代码来说明:
#include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/core/core.hpp> #include <opencv/cv.hpp> using namespace std; using namespace cv; int main() { string picName="lena.jpg"; Mat A=imread (picName,CV_LOAD_IMAGE_COLOR); Mat mask=(Mat_<char>(3,3)<<0,-1,0, -1,5,-1, 0,-1,0); Mat B; filter2D (A,B,A.depth (),mask); imshow("A的图像",A); imshow("B的图像",B); waitKey (); return 0; }
比较上面这两幅图片,是否感觉处理后的图像对比度更加强烈了呢?
接下来让我们用这个函数实现一个平滑滤波的功能,我们只要把矩阵核写成下面这样:
所以代码改成了:
#include <iostream> #include <opencv2/highgui/highgui.hpp> #include <opencv2/core/core.hpp> #include <opencv/cv.hpp> using namespace std; using namespace cv; int main() { string picName="board.jpg"; Mat A=imread (picName,CV_LOAD_IMAGE_COLOR); //构造矩阵核 Mat mask=Mat::ones (3,3,CV_32FC1); mask/=9; Mat B; filter2D (A,B,A.depth (),mask); //使用了OpenCV提供的平滑滤波函数 Mat C; Size s=Size(3,3); blur(A,C,s); imshow("A的图像",A); imshow("B的图像",B); imshow("C的图像",C); waitKey (); return 0; }
运行结果为:
可以看出,图像的边缘变得模糊不清,左上角芯片上的字迹已经不易识别了。