1、Sobel边缘检测算子html
l 在讨论边缘算子以前,首先给出一些术语的定义:算法
l (1)边缘:灰度或结构等信息的突变处,边缘是一个区域的结束,也是另外一个区域的开始,利用该特征能够分割图像。函数
l (2)边缘点:图像中具备坐标[x,y],且处在强度显著变化的位置上的点。ui
l (3)边缘段:对应于边缘点坐标[x,y]及其方位 ,边缘的方位多是梯度角。url
幻灯片3spa
2、Sobel算子的基本原理视频
l Sobel算子是一阶导数的边缘检测算子,在算法实现过程当中,经过3×3模板做为核与图像中的每一个像素点作卷积和运算,而后选取合适的阈值以提取边缘。htm
l 采用3×3邻域能够避免在像素之间内插点上计算梯度。Sobel算子也是一种梯度幅值,即:blog
l 其中的偏导数Sx 和Sy可用卷积模板来实现。内存
幻灯片4
Sx=(Z1+2Z2+Z3)-(Z7+2Z8+Z9)
Sy=(Z1+2Z4+Z7)-(Z3+2Z6+Z9)
幻灯片5
l Sobel算子算法的优势是计算简单,速度快。可是因为只采用了2个方向的模板,只能检测水平和垂直方向的边缘,所以这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断欠合理,会形成边缘点的误判,由于许多噪声点的灰度值也很大。
幻灯片6
3、模板方向的改变
幻灯片7
幻灯片8
4、Sobel算子图像边缘检测的MATLB程序实现
l >> f=imread('peppers.png');
l >> f=rgb2gray(f);
l >> f=im2double(f);
l >> figure,imshow(f),title('原始图像');
l >> [SFST Threshold] =edge(f,'sobel','horizontal');
l >> figure,imshow(SFST),title(' 水平图像边缘检测');
l >> [VSFAT Threshold]=edge(f,'sobel','vertical');
l >> figure,imshow(VSFAT),title('垂直图像边缘检测');
l >> s45=[-2 -1 0;-1 0 1;0 1 2];
l >> SFST45=imfilter(f,s45,'replicate');
l >> SFST45=SFST45>=Threshold;
l >> figure,imshow(SFST45),title('45度角图像边缘检测');
l >> s135=[0 -1 -2;1 0 -1;2 1 0];
l >> SFST135=imfilter(f,s135,'replicate');
l >> SFST135=SFST135>=Threshold;
l >> figure,imshow(SFST135),title('135度角图像边缘检测');
l >>
幻灯片9
幻灯片10
幻灯片11
幻灯片12
幻灯片13
因为项目里要用到边缘检测,因此今天研究了一下最简单的梯度的方法。
首先,咱们来开一下计算机是如何检测边缘的。以灰度图像为例,它的理论基础是这样的,若是出现一个边缘,那么图像的灰度就会有必定的变化,为了方便假设由黑渐变为白表明一个边界,那么对其灰度分析,在边缘的灰度函数就是一个一次函数y=kx,对其求一阶导数就是其斜率k,就是说边缘的一阶导数是一个常数,而因为非边缘的一阶导数为零,这样经过求一阶导数就能初步判断图像的边缘了。一般是X方向和Y方向的导数,也就是梯度。理论上计算机就是经过这种方式来得到图像的边缘。
可是,具体应用到图像中你会发现这个导数是求不了的,由于没一个准确的函数让你去求导,并且计算机在求解析解要比求数值解麻烦得多,因此就想到了一种替代的方式来求导数。就是用一个3×3的窗口来对图像进行近似求导。拿对X方向求导为例,某一点的导数为第三列的元素之和减去第一列元素之和,这样就求得了某一点的近似导数。其实也很好理解为何它就近似表明导数,导数就表明一个变化率,从第一列变为第三列,灰度值相减,固然就是一个变化率了。这就是所谓的Prewitt算子。这样近似X方向导数就求出来了。Y方向导数与X方向导数求法类似,只不过是用第三行元素之和减去第一行元素之和。X方向和Y方向导数有了,那么梯度也就出来了。这样就能够找出一幅图中的边缘了。
还有一个问题,因为求的是3×3中心点的导数,因此给第二列加了一个权重,它的权重为2,第一列和第三列的权重为1,好了,这就是Sobel算子了。相比Prewitt算子,Sobel的抗噪能力更强。如图所示:这样,中心点的Y方向导数就求出来了。
举个例子吧。,X点以Sobel方式求导数ΔX=1×50+2×30+1×50-(1×50+2×30+1×50)=0。这样能够看出这个点不是边界。
好了,了解了基本理论以后,咱们看看OpenCv下的Sobel函数吧,void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );src:输入图像;dst:输出图像;xorder:x 方向上的差分阶数;yorder:y 方向上的差分阶数;aperture_size 扩展 Sobel 核的大小(既窗口阶数),必须是 1(注意这是一个3×1或1×3向量而不是一个方阵), 3, 5 或 7。
下面编写一个Sobel边缘检测的程序吧,平台是VS08,创建Win32控制台应用程序。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("lena.jpg");//加载图像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配图像空间
sobel=cvCreateImage(cvGetSize(frame),frame->depth,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//转为灰度
cvSobel(gray,sobel,1,0,3);
cvShowImage("frame",frame);//显示图像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//释放空间(对视频处理很重要,不释放会形成内存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}
运行,你会发现出错,仔细看看没有问题啊。其实,这里是问题的,由于以Sobel方式求完导数后会有负值,还有会大于255的值而你建的Sobel的图像是 IPL_DEPTH_8U,也就是8位无符号数,因此Sobel创建的图像位数不够,要16位有符号的,也就是 IPL_DEPTH_16S。把创建图像这句改成
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);运行,发现不报错了,可是Sobel图像显示不出来,这是什么缘由呢?原来图像显示是以8位无符号显示的,如今是16位有符号,固然显示会出问题了。因此还要将Sobel转为8位无符号。OpenCv里提供了一个函数,就是cvConvertScaleAbs( const CvArr* src, CvArr* dst, double scale=1, double shift=0 );src:源图像;dst:目标图像;scale:转化前乘的系数;shift转化前加的系数。这样新建一个无符号图像再转换就能够实现了。
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
再在显示图像前加上cvConvertScaleAbs(sobel,sobel8u,1,0);这样就能够看到cvSobel的效果了。能够看X方向或Y方向求导是什么效果。
为了方便你们,我把改好后的程序也放上来了。
#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("e:/p1.jpg");//加载图像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配图像空间
sobel=cvCreateImage(cvGetSize(frame),IPL_DEPTH_16S,1);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
cvCvtColor(frame,gray,CV_BGR2GRAY);//转为灰度
cvSobel(gray,sobel,1,0,3);
IplImage *sobel8u=cvCreateImage(cvGetSize(sobel),IPL_DEPTH_8U,1);
cvConvertScaleAbs(sobel,sobel8u,1,0);
cvShowImage("frame",frame);//显示图像
cvShowImage("gray",gray);
cvShowImage("sobel",sobel8u);
cvWaitKey(0);//等待
cvReleaseImage(&frame);//释放空间(对视频处理很重要,不释放会形成内存泄露)
cvReleaseImage(&gray);
cvReleaseImage(&sobel);
cvDestroyWindow("frame");
cvDestroyWindow("gray");
cvDestroyWindow("sobel");
}