• sobel算子及cvSobel


       图像锐化处理的主要目的是突出图像中的细节,像边沿区域,锐化处理与图像在该点的突变程度有关。图像微分增强了边缘与其他突变的信息,并削弱了灰度变化缓慢的信息。以灰度图像为例,它的理论基础是这样的,如果出现一个边缘,那么图像的灰度就会有一定的变化,为了方便假设由黑渐变为白代表一个边界,那么对其灰度分析,在边缘的灰度函数就是一个一次函数y=kx,对其求一阶导数就是其斜率k,就是说边缘的一阶导数是一个常数,而由于非边缘的一阶导数为零,这样通过求一阶导数就能初步判断图像的边缘了。通常是X方向和Y方向的导数,也就是梯度。理论上计算机就是通过这种方式来获得图像的边缘。

       
      但是,具体应用到图像中你会发现这个导数是求不了的,因为没一个准确的函数让你去求导,而且计算机在求解析解要比求数值解麻烦得多,所以就想到了一种替代的方式来求导数。就是用一个3×3的窗口来对图像进行近似求导。拿对X方向求导为例,某一点的导数为第三列的元素之和减去第一列元素之和,这样就求得了某一点的近似导数。其实也很好理解为什么它就近似代表导数,导数就代表一个变化率,从第一列变为第三列,灰度值相减,当然就是一个变化率了。这就是所谓的Prewitt算子。这样近似X方向导数就求出来了。Y方向导数与X方向导数求法相似,只不过是用第三行元素之和减去第一行元素之和。X方向和Y方向导数有了,那么梯度也就出来了。这样就可以找出一幅图中的边缘了。

     
    还有一个问题,由于求的是3×3中心点的导数,所以给第二列加了一个权重,它的权重为2,第一列和第三列的权重为1,好了,这就是Sobel算子了。相比Prewitt算子,Sobel的抗噪能力更强。如图所示:Sobel算子及cvSobel这样,中心点的Y方向导数就求出来了。

    下面编写一个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");

    }

    cvSobel(gray,sobel,1,0,3);表示对图像x方向进行一阶差分,得到的图像垂直的边缘比较明显,而cvSobel(gray,sobel,0,1,3);表示对图像y方向进行一阶差分,得到的图像水平的边缘比较明显,如果使用cvSobel(gray,sobel,1,1,3);两个方向的边缘反而都不清楚了。

    一般Sobel算子都是用来单一方向的边缘的,边缘检测都是按需求用的,所以一般检测单一方向的边缘会用Sobel算子,两个方向边缘都清楚可以试下Canny算子,原理和Sobel不同,但是效果不错。cvSobel(gray,sobel,1,1,3);应该是两个方向都做了差分,应该是二阶的,虑去了有些频率的信息,两个方向的边缘都不是很清楚了

  • 相关阅读:
    机器学习-第四讲(模型升级)
    AI人脸匹对
    AI换脸
    人脸识别分析小Demo
    动态规划-线性DP&区间DP
    动态规划-背包问题
    数学知识-博弈论
    数学知识-组合数
    数学知识-扩展欧几里得
    数学知识-欧拉函数&快速幂
  • 原文地址:https://www.cnblogs.com/jameskun77/p/3216712.html
Copyright © 2020-2023  润新知