OpenCV学习笔记(二)常用滤波函数之下篇(卷积滤波)

上篇介绍了openCV自带的滤波函数库,中篇介绍了基于中值滤波的改进滤波算法:自适应中值滤波。这一篇将介绍OpenCV的卷积操作函数:void cvFilter2D( const CvArr* src, CvArr* dst,const CvMat* kernel,CvPoint anchor=cvPoint(-1,-1));
其中:src表示输入图像;dst表示输出图像;kernel表示卷积核,为单通道浮点矩阵, 如果想要应用不同的核于不同的通道,需要先用 cvSplit 函数分解图像到单个色彩通道上,然后单独处理;anchor为核的锚点,表示一个被滤波的点在核内的位置((-1,-1)点表示核的中心点)。
这是C语言版本的,卷积函数还有C++版本的,其函数形式为:CV_EXPORTS_W void filter2D( InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT );
其中:
InputArray src表示输入图像;OutputArray dst表示 输出图像,和输入图像具有相同的尺寸和通道数量;int ddepth表示 目标图像深度,如果没写将生成与原图像深度相同的图像,当ddepth输入值为-1时,目标图像和原图像深度保持一致;InputArray kernel表示 卷积核,是一个单通道浮点型矩阵。如果想在图像不同的通道使用不同的kernel,需要先使用split()函数将多通道图像分离成单通道图像;Point anchor表示 内核的锚点,其默认值为(-1,-1)说明位于kernel的中心位置;double delta表示 在储存目标图像前可选的添加到像素的值,默认值为0;int borderType表示 像素向外逼近的方法,默认值BORDER_DEFAULT,即对全部边界进行计算。
下面使用C++版本的库函数filter2D来依次进行图像的模糊、去噪、锐化和边缘检测等操作。

在此,先简单介绍一下图像处理中的卷积概念。
图像处理中的卷积概念与数字信号处理中的卷积概念不同,它更趋向于相对求和,对卷积模板中所有的点进行累加求和。如下图所示:
OpenCV学习笔记(二)常用滤波函数之下篇(卷积滤波)_第1张图片
卷积模板中的矩阵元素为:
(A) [ 4 0 0 0 0 0 0 0 − 4 ] \left[ \begin{matrix} 4 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & -4 \end{matrix} \right] \tag{A} 400000004(A)
原图像中要进行卷积操作的矩阵元素为:
(B) [ 0 0 0 0 1 1 0 1 2 ] \left[ \begin{matrix} 0 & 0 & 0 \\ 0 & 1 & 1 \\ 0 & 1 & 2 \end{matrix} \right] \tag{B} 000011012(B)
对A、B中的各元素依次对应相乘最后求和即得到了卷积后的像素点的值,该值替换原图像中的位置由锚点决定 Point anchor=Point(-1,-1) ,其中(-1,-1)表示在卷积核的中心位置。依次逐个点遍历全图即可得到最终的卷积滤波后的图像。

1、图像模糊

先以卷积核为
(A) [ 0 0 0 0 1 0 0 0 0 ] \left[ \begin{matrix} 0 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 0 \end{matrix} \right] \tag{A} 000010000(A)
来说明,

如上图所示,滤波前后图像无变化,因为卷积核中除中心位置外,其他位置都为0,卷积后的结果就是中心处的像素点,也就相当于并没有对图像滤波。

均值滤波

均值滤波卷积核为:
(B) [ 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 1 / 9 ] \left[ \begin{matrix} 1/9 & 1/9 & 1/9 \\ 1/9 & 1/9 & 1/9 \\ 1/9 & 1/9 & 1/9 \end{matrix} \right] \tag{B} 1/91/91/91/91/91/91/91/91/9(B)
卷积滤波效果如下图所示:

有一定的模糊,下面立即上高斯滤波的效果,这样更能看出高斯滤波与均值滤波之间的差异。

高斯滤波

高斯滤波的卷积核为:
(C) [ 1 / 16 2 / 16 1 / 16 2 / 16 4 / 16 2 / 16 1 / 16 2 / 16 1 / 16 ] \left[ \begin{matrix} 1/16 & 2/16 & 1/16 \\ 2/16 & 4/16 & 2/16 \\ 1/16 & 2/16 & 1/16 \end{matrix} \right] \tag{C} 1/162/161/162/164/162/161/162/161/16(C)
卷积滤波效果如下:

结合上图可以看出,高斯滤波虽然也对图像造成了一定的模糊,但是在细节保留方面要明显强于均值滤波。这是因为其滤波过程是结合周围像素点的权重比来决定的,像素点相似度高的权重大,相似度低的权重相应减少,这样比起均值滤波的一刀切就要好很多了。

2、图像锐化

锐化的卷积滤波核为:
(D) [ − 1 − 1 − 1 − 1 9 − 1 − 1 − 1 − 1 ] \left[ \begin{matrix} -1 & -1 & -1 \\ -1 & 9 & -1 \\ -1 & -1 & -1 \end{matrix} \right] \tag{D} 111191111(D)
锐化效果如下:

可以看出,在边缘部分的图像明显比之前更有对比度。
除了这种锐化边缘之外,还有一种锐化更强调图像边缘处的细节信息。它的锐化的卷积滤波核为:
(E) [ 1 1 1 1 − 7 1 1 1 1 ] \left[ \begin{matrix} 1 & 1 & 1 \\ 1 & -7 & 1 \\ 1 & 1 & 1 \end{matrix} \right] \tag{E} 111171111(E)
滤波效果如下:

可以看出,图像的边缘部分都变为了白色。

3、边缘检测

梯度PreWitt

这种检测方法更适合检测有明确方向的图像边缘。
水平梯度PreWitt卷积滤波核为:
(F) [ − 1 0 1 − 1 0 1 − 1 0 1 ] \left[ \begin{matrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{matrix} \right] \tag{F} 111000111(F)
卷积滤波效果如下:

可以看出,图像的垂直方向检测效果更为理想。
垂直梯度PreWitt卷积滤波核为:
(G) [ − 1 − 1 − 1 0 0 0 1 1 1 ] \left[ \begin{matrix} -1 & -1 & -1 \\ 0 & 0 & 0 \\ 1 & 1 & 1 \end{matrix} \right] \tag{G} 101101101(G)
卷积滤波效果如下:

与上图相比,在水平方向的检测效果就比垂直好很多。

Sobel边缘检测

与梯度Prewitt类似的还有Sobel边缘检测,但是Sobel边缘检测比起梯度Prewitt更加强调临近像素点的影响。
Sobel水平边缘检测卷积滤波核为:
(H) [ − 1 0 1 − 2 0 2 − 1 0 1 ] \left[ \begin{matrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1 \end{matrix} \right] \tag{H} 121000121(H)
卷积滤波效果如下:

Sobel垂直边缘检测卷积滤波核为:
(I) [ − 1 − 2 − 1 0 0 0 1 2 1 ] \left[ \begin{matrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1 \end{matrix} \right] \tag{I} 101202101(I)
卷积滤波效果如下图:

不论是梯度PreWitt还是Sobel边缘检测算子,他们都只能对单一方向进行检测,而Laplace算子就可以做到所有角度的检测,因此,比起前两者来说有更大的优势。

Laplace边缘检测

Laplace边缘检测卷积滤波核:
(J) [ − 1 − 1 − 1 − 1 8 − 1 − 1 − 1 − 1 ] \left[ \begin{matrix} -1 & -1 & -1 \\ -1 & 8 & -1 \\ -1 & -1 & -1 \end{matrix} \right] \tag{J} 111181111(J)
卷积滤波效果如下:

除了上面那种卷积核,Laplace还有另外一种卷积核,
卷积滤波核:
(L) [ 0 − 1 0 − 1 4 − 1 0 − 1 0 ] \left[ \begin{matrix} 0 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 0 \end{matrix} \right] \tag{L} 010141010(L)
卷积滤波效果如下:

可以看出,Laplace边缘检测算子对图像的边缘信息提取效果最好,基本把水平和垂直方向的边缘都提取出来了。

4、实现代码

#include "stdafx.h"
#include "cv.h"
#include "highgui.h"

using namespace std;
using namespace cv;

#define Average (double)1/9
#define GasAverage (double)1/16

//卷积核
float A[]=   //卷积后图片无变化
{
	0,0,0,
	0,1,0,
	0,0,0
};

float B[]=   //卷积后图片变模糊
{
	Average,Average,Average,
	Average,Average,Average,
	Average,Average,Average
};

float C[]=   //卷积后图片变高斯模糊
{
	GasAverage,2*GasAverage,GasAverage,
	2*GasAverage,4*GasAverage,2*GasAverage,
	GasAverage,2*GasAverage,GasAverage
};

float D[]=   //卷积后图片变锐利化
{
	-1,-1,-1,
	-1, 9,-1,
	-1,-1,-1
};

float E[]=   //卷积后图片变锐利化
{
	1, 1, 1,
	1,-7, 1,
	1, 1, 1
};

float F[]=   //水平梯度PreWitt
{
	-1,0,1,
	-1,0,1,
	-1,0,1
};

float G[]=   //垂直梯度PreWitt
{
	-1,-1,-1,
	0, 0, 0,
	1, 1, 1
};

float H[]=   //Sobel水平梯度边缘检测
{
	-1,0,1,
	-2,0,2,
	-1,0,1
};

float I[]=   //Sobel垂直梯度边缘检测
{
	-1,-2,-1,
	0, 0, 0,
	1, 2, 1
};

float J[]=   //拉普拉斯边缘检测1
{
	-1,-1,-1,
	-1, 8,-1,
	-1,-1,-1
};

float L[]=   //拉普拉斯边缘检测2
{
	 0,-1, 0,
	-1, 4,-1,
	 0,-1, 0
};

int _tmain(int argc, _TCHAR* argv[])
{
	Mat imgsrc,imgdst,final_img;
	vector<Mat> img_RGBChannel(3),FilterImage(3);  //存储图片分离后的三个通道的值
	imgsrc=imread("Tree.jpg",1);
	float fScale=0.3;  //缩放尺寸
 	Size dstsize=Size(imgsrc.cols*fScale,imgsrc.rows*fScale);
	imgdst.create(dstsize,CV_32S);
	resize(imgsrc,imgdst,dstsize);
	split(imgdst,img_RGBChannel);  //通道分离
	imshow("imgdst",imgdst);  //显示缩放后的原始图片
	
	Mat Kernel(Size(3,3),CV_32FC1,E);
	filter2D(img_RGBChannel[0],FilterImage[0],-1,Kernel);
	filter2D(img_RGBChannel[1],FilterImage[1],-1,Kernel);
	filter2D(img_RGBChannel[2],FilterImage[2],-1,Kernel);

	merge(FilterImage,final_img);
	imshow("finalImg",final_img);

	waitKey(0);
	return 0;
}

你可能感兴趣的:(OpenCV学习笔记)