OpenCv中使用Sobel算子

时间:2021-02-10 09:52:30

Sobel边缘检测算子

1.基本原理

    Sobel算子是一阶导数的边缘检测算子,在算法实现过程中,通过3×3模板作为核与图像中的每个像素点做卷积和运算,然后选取合适的阈值提取边缘。

    采用3×3邻域可以避免在像素之间内插点上计算梯度。Sobel算子也是一种梯度幅值,即:其中的偏导数Sx和Sy可用卷积模板来实现。其模板如下:

OpenCv中使用Sobel算子  OpenCv中使用Sobel算子

    Sx = (Z1 + 2*Z2 + Z3)-(Z7 + 2*Z8 + Z9);

    Sy = (Z1 + 2*Z4 + Z7)-(Z3 + 2*Z6 + Z9);

2.算法优缺点

    Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。

3.基于OpenCv的Sobel边缘检测

 开发平台VC6.0,OpenCv1.0。
   函数cvSobel(constCvArr*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。

代码如下:

#include <cv.h>
#include <highgui.h>
void main()
{
IplImage *frame,*gray,*sobel;
frame=cvLoadImage("car.bmp");//加载图像
gray=cvCreateImage(cvGetSize(frame),frame->depth,1);//分配图像空间
sobel=cvCreateImage(cvGetSize(frame),frame->depth,1);
cvCvtColor(frame,gray,CV_BGR2GRAY);//转为灰度
cvSobel(gray,sobel,1,0,3);
cvNamedWindow("frame");
cvNamedWindow("gray");
cvNamedWindow("sobel");
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("car.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");
}
实验结果如下:

OpenCv中使用Sobel算子

原图

OpenCv中使用Sobel算子

16S的Sobel图(什么都没有)

OpenCv中使用Sobel算子

Sobel边缘图