• opencv学习笔记之cvSobel 函数解析


    首先,我们来开一下计算机是如何检测边缘的。以灰度图像为例,它的理论基础是这样的,如果出现一个边缘,那么图像的灰度就会有一定的变化,为了方便假设由黑渐变为白代表一个边界,那么对其灰度分析,在边缘的灰度函数就是一个一次函数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方式求完导数后会有负值,还有会大于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方向求导是什么效果。

    代码如下

     1 //Sobel边缘检测的程序
     2 #include "cv.h"
     3 #include "highgui.h"
     4 int main( int argc , char** argv)
     5 {
     6   //以灰度图格式加载图像或者用函数cvCvtColor(frame,gray,CV_BGR2GRAY);转为灰度
     7   IplImage* src = cvLoadImage(argv[1],CV_LOAD_IMAGE_GRAYSCALE);
     8   //以sobel方式求完导之后会有负数,会有大于255的值,故建立图像的数据格式
     9   //要为IPL_DEPTH_16S
    10   IplImage* dst1 = cvCreateImage(cvGetSize(src),IPL_DEPTH_16S,1);
    11   IplImage* dst2 = cvCreateImage(cvGetSize(src),IPL_DEPTH_16S,1);
    12   IplImage* dst18u = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
    13   IplImage* dst28u = cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1);
    14   //逼近x方向上一阶微分的Sobel算子的效果
    15   cvSobel(
    16           src,
    17           dst1,
    18           1,
    19           0,
    20           3);
    21   //逼近y方向上一阶微分的Sobel算子的效果  
    22   cvSobel(
    23           src,
    24           dst2,
    25           0,
    26           1,
    27           3);
    28   //图像是以8位无符号显示的,故要把输出图像的数据格式转化为IPL_DEPTH_8U
    29   cvConvertScaleAbs(dst1,dst18u,1,0);
    30   cvConvertScaleAbs(dst2,dst28u,1,0);
    31   cvNamedWindow("src",1);
    32   cvNamedWindow("dst1",1);
    33   cvNamedWindow("dst2",1);
    34   cvShowImage("src",src);
    35   cvShowImage("dst1",dst18u);
    36   cvShowImage("dst2",dst28u);
    37   cvWaitKey(0);
    38   cvReleaseImage(&src);
    39   cvReleaseImage(&dst1);
    40   cvReleaseImage(&dst2);
    41   cvReleaseImage(&dst18u);
    42   cvReleaseImage(&dst28u);
    43   cvDestroyWindow("src");
    44   cvDestroyWindow("dst1");
    45   cvDestroyWindow("dst2");
    46   return 0;
    47 }
  • 相关阅读:
    springboot的build.gradle增加阿里仓库地址以及eclipse增加lombok
    mqttfx无法选择证书
    EMQX源码编译过程
    新环境chart包helmlint校验
    安装rebar3
    Ubuntu16.04下,erlang安装和rabbitmq安装步骤
    UTF-8,GBK,ANSI之间的关系和区别
    write命令
    bunzip2命令
    bzip2命令
  • 原文地址:https://www.cnblogs.com/feifanrensheng/p/8047420.html
Copyright © 2020-2023  润新知