灰度变换和空间滤波是空间域处理的两种主要方法,灰度变换是特殊的空间滤波处理,它的邻域大小为1*1大小,所以灰度变换也称为点处理技术,冈萨雷斯的《数字图像处理》一书中用灰度变换实现了图像增强和图像分割两种应用,目前个人学的还比较浅。灰度变换主要用在图像全局显示效果改善上,如幂律变换,直方图拉伸等,还有就是阈值分割了,这个大家都应该很熟悉。
空间滤波器是图像处理的重点,是图像预处理的重要组成部分。空间滤波器的机理主要是(1)一个邻域(2)在该邻域上预定义的操作(一般是滤波器系数和像素的乘积和)。滤波器产生一个新像素取代滤波器中心点的像素值。滤波的本质是加权,即选择性的提取图像中某些方面的内容,这些内容能够满足特定的应用场合。opencv中低通滤波基本的函数有均值滤波blur,高斯滤波GaussianBlur。
均值滤波函数一般只是简单的用邻域像素均值取代中心的像素值,其模糊效果与邻域尺寸直接相关,尺寸越大图像越模糊,对细节的保留越少,它适用于提取全局图像目标中的主要目标,对感兴趣物体进行粗略的描述。
高斯滤波属于加权平均滤波,它的滤波效果与邻域尺寸和加权系数分布有关,离中心越近的点加权系数越高,因此高斯模糊可以在过度平滑和保留细节之间折衷,加权系数的获取可以用对应的sigma值调用函数getGaussianKernal。不同的sigma值对应不同的系数分布,高斯函数图像如下,可见sigma越小,中心像素值占的比重越大。
opencv中高斯函数还有一个使用技巧是,可以只设置sigma的值(第四个参数)由opencv决定size的值(第三个参数设为0)。反过来也可以,即只提供尺寸的数值,sigma设为0,由函数自行判断最适合的sigma值。
在本次实习的项目中相机标定部分使用自定义的标定板,获取标定板的角点之前先对图像经行了高斯模糊,这样可以保证获取的角点位于两条直线的交点。可见,高斯滤波适合提取全局图像细节中的主要细节(自己的理解),所以高斯滤波在许多opencv函数中作为去除噪声的首选函数。
这里顺便提一下中值滤波,中值滤波是非线性滤波器,基于它的滤波原理,其非常适合消除椒盐噪声,椒盐噪声一般是在信号通信过程中受到干扰而产生的。中值滤波有利于保存边缘,但会洗去匀质区域的纹理。
图像模糊处理类似于积分,相应的有图像的空间微分,微分处理主要是突出图像上灰度的过渡部分,即边缘。关于图像微分《数字图像处理》讲解的非常好,我这里简单描述一下一阶微分和二阶微分的差异,首先,它们的公式还是要记得的,然后 性质也就很清楚了。
一阶微分:(1)恒定灰度区域为0,(2)灰度台阶或斜坡开始处不为0, (3)灰度斜坡点处不为0
二阶微分:(1)恒定灰度区域为0,(2)灰度台阶或斜坡开始和结束处不为0,(3)灰度斜坡点出为0
一阶微分在图像中主要是用梯度幅值来实现的,所以也可以称为是幅梯度图。数字图像的在边缘上的灰度常常类似于斜坡过渡,这样导致一阶微分产生较粗的边缘,opencv中常用的一阶边缘函数是sobel,也可以利用自定义的核+filter2D实现图像一阶微分运算或者特定方向边缘检测的运算。
二阶微分在图像处理中使用 拉普拉斯算子实现,拉普拉斯算子产生的边缘较sobel算子要细,同时很难保证边缘的连续性,在质量较好,背景单一的图像中可以考虑使用二阶微分来计算边缘。此外,二阶微分对孤立点和细线也有较强的响应,具体实例可参考《数字图像和处理》第十章。
以下给出sobel,laplacian算子处理lena图的边缘结果:
代码如下:
void main()
{
Mat dst, dst1,dst2,dst3,dst4, dst5;
Mat src = imread("F:\\编程学习\\图像处理\\图片库\\图像处理练习图片库\\lena.png", 0);
resize(src, src, Size(600, 600));
imshow("原图", src);
Scharr(src, dst2, CV_16S, 1,0);
Scharr(src, dst3, CV_16S, 0,1);
addWeighted(abs(dst2), 0.5,abs(dst3), 0.5, 0, dst4);
double max = 0;
minMaxLoc(dst4, 0, &max); //提取最大值用于归一化
cout << max << endl;
dst4.convertTo(dst5, CV_8U, 255./max); //转变图像数据类型,并归一化
threshold(dst5, dst5, 40, 255, THRESH_BINARY);//阈值化
imshow("Scharr合成sobel", dst5);
Mat dst1_1;
Laplacian(src, dst1, CV_16S, 3);
double max2 = 0;
minMaxLoc(dst1, 0, &max2);
cout << max << endl;
dst1.convertTo(dst1_1, CV_8U, 255. / max2);
threshold(dst1_1, dst1_1, 40, 255, THRESH_BINARY);
imshow("拉普拉斯图", dst1_1);
waitKey(0);
}
从图像可看出,一阶边缘虽然较粗,但是比较完整、连续的显示出了图像边缘,可以很方便的用findContours函数对这些边缘进行提取。二阶微分产生的边缘较细,断裂处较多无法很好的进行轮廓提取。实际使用中,梯度阈值法也是边缘获取的一种选择。此外,还有优秀的canny边缘检测算子,它提取的边缘质量很高,但是也会出现边缘断裂的问题,需要自己后续处理。