zoukankan      html  css  js  c++  java
  • 图像边缘检測--OpenCV之cvCanny函数

    图像边缘检測--OpenCV之cvCanny函数

    分类: C/C++

    void cvCanny( const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3 ); image单通道输入图像.edges单通道存储边缘的输出图像threshold1第一个阈值threshold2第二个阈值aperture_sizeSobel 算子内核大小 (见 cvSobel).

    函数 cvCanny 採用 CANNY 算法发现输入图像的边缘并且在输出图像中标识这些边缘。threshold1和threshold2 其中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始切割。

    说明:OpenCV中cvCanny函数用到了cvSobel的差分计算。

    下图为OpenCV的cvCanny函数效果

    26020768_1335255227dllb

    点击(此处)折叠或打开

    #include "stdafx.h"
    #include <cv.h>
    #include <cxcore.h>
    #include <highgui.h>
    #include <cmath>
    using namespace std;
    using namespace cv;
    int main(int argc ,char ** argv)
    {
    IplImage * pImg=NULL;
        IplImage * pCannyImg=NULL;
    if (argc ==2&&(pImg=cvLoadImage(argv[1],0))!=0)
    {
            pCannyImg=cvCreateImage(cvGetSize(pImg),IPL_DEPTH_8U,1);
            cvCanny(pImg,pCannyImg,50,150,3);
    //创建窗体
            cvNamedWindow("src", 1);
            cvNamedWindow("canny",1);
    //显示图像
            cvShowImage( "src", pImg );
            cvShowImage( "canny", pCannyImg );
            cvWaitKey(0); //等待按键
    //销毁窗体
            cvDestroyWindow( "src" );
            cvDestroyWindow( "canny" );
    //释放图像
            cvReleaseImage( &pImg );
            cvReleaseImage( &pCannyImg );
            return 0;
    }
        return -1;
    }

    重要:Canny原理:链接及内容:http://blog.csdn.net/likezhaobin/article/details/6892176

    opencv2版本号:

    // canny边缘检測.cpp : 定义控制台应用程序的入口点。
    //

    #include "stdafx.h"


    #include "stdafx.h"
    #include <cv.h>
    #include <cxcore.h>
    #include <highgui.h>
    #include <cmath>

    #include <opencv2/imgproc/imgproc.hpp> 
    #include <opencv2/highgui/highgui.hpp> 
    #include <iostream> 
     
    #pragma comment(lib,"opencv_core2410d.lib")           
    #pragma comment(lib,"opencv_highgui2410d.lib")           
    #pragma comment(lib,"opencv_imgproc2410d.lib")

    using namespace std;
    using namespace cv;

    int main(int argc ,char ** argv)
    {
        IplImage * pImg=NULL;
        IplImage * pCannyImg=NULL;


        cv::Mat src = cv::imread("swan.jpg"); 
        if (src.empty()) 
            return -1; 
         
        cv::Mat bw; 
        cv::cvtColor(src, bw, CV_BGR2GRAY); 

        Mat canny_mat(src.size(),CV_8U);
       
            //cvCanny(pImg,pCannyImg,50,150,3);
            cv::Canny(bw,canny_mat,50,150,3);
       
            imshow("canny",canny_mat);

            cvWaitKey(0); //等待按键


            return 0;
       
    }

    swan

    边缘检測后:

    QQ截图20141219204956

    图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般能够看作是一个阶跃。既从一个灰度值在非常小的缓冲区域内急剧变化到还有一个灰度相差较大的灰度值。图象的边缘部分集中了图象的大部分信息。图象边缘的确定与提取对于整个图象场景的识别与理解是非常重要的,同一时候也是图象切割所依赖的重要特征,边缘检測主要是图象的灰度变化的度量、检測和定位。自从1959提出边缘检測以来,经过五十多年的发展,已有很多中不同的边缘检測方法。依据作者的理解和实践。本文对边缘检測的原理进行了描写叙述,在此基础上着重对Canny检測算法的实现进行详述。

            本文所述内容均由编程验证而来,在实现过程中,有不论什么错误或者不足之处大家共同讨论(本文不讲述枯燥的理论证明和数学推导。只从算法的实现以及改进上进行原理性和project化的描写叙述)。

    1、边缘检測原理及步骤

            在之前的博文中,作者从一维函数的跃变检測開始。循序渐进的对二维图像边缘检測的基本原理进行了通俗化的描写叙述。结论是:实现图像的边缘检測,就是要用离散化梯度逼近函数依据二维灰度矩阵梯度向量来寻找图像灰度矩阵的灰度跃变位置。然后在图像中将这些位置的点连起来就构成了所谓的图像边缘(图像边缘在这里是一个统称,包含了二维图像上的边缘、角点、纹理等基元图)。

            在实际情况中理想的灰度阶跃及其线条边缘图像是非常少见到的。同一时候大多数的传感器件具有低频滤波特性,这样会使得阶跃边缘变为斜坡性边缘,看起来当中的强度变化不是瞬间的。而是跨越了一定的距离。

    这就使得在边缘检測中首先要进行的工作是滤波。

            1)滤波:边缘检測的算法主要是基于图像强度的一阶和二阶导数。但导数通常对噪声非常敏感,因此必须採用滤波器来改善与噪声有关的边缘检測器的性能。常见的滤波方法主要有高斯滤波,即採用离散化的高斯函数产生一组归一化的高斯核(详细见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和(详细程序实现见下文)。

            2)增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法能够将图像灰度点邻域强度值有显著变化的点凸显出来。

    在详细编程实现时,可通过计算梯度幅值来确定。

            3)检測:经过增强的图像,往往邻域中有非常多点的梯度值比較大。而在特定的应用中。这些点并非我们要找的边缘点,所以应该採用某种方法来对这些点进行取舍。

    实际project中,经常使用的方法是通过阈值化方法来检測。

    2、Canny边缘检測算法原理

            JohnCanny于1986年提出Canny算子。它与Marr(LoG)边缘检測方法类似,也属于是先平滑后求导数的方法。

    本节对依据上述的边缘检測过程对Canny检測算法的原理进行介绍。

    2.1 对原始图像进行灰度化

            Canny算法通常处理的图像为灰度图。因此假设摄像机获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是依据图像各个通道的採样值进行加权平均。以RGB格式的彩图为例,通常灰度化採用的方法主要有:

            方法1:Gray=(R+G+B)/3;

            方法2:Gray=0.299R+0.587G+0.114B;(这样的參数考虑到了人眼的生理特点)

            注意1:至于其它格式的彩色图像,能够依据对应的转换关系转为RGB然后再进行灰度化;

            注意2:在编程时要注意图像格式中RGB的顺序通常为BGR

    2.2 对图像进行高斯滤波

            图像高斯滤波的实现能够用两个一维高斯核分别两次加权实现,也能够通过一个二维高斯核一次卷积实现。

            1)高斯核实现

    0_1319118393k2d6

    上式为离散化的一维高斯函数,确定參数就能够得到一维核向量。

    0_13191185277qam

            上式为离散化的二维高斯函数,确定參数就能够得到二维核向量。

            注意1:关于參数Sigma的取值详见上篇博文。

            注意2:在求的高斯核后,要对整个核进行归一化处理。

    2)图像高斯滤波

            对图像进行高斯滤波,听起来非常玄乎。事实上就是依据待滤波的像素点及其邻域点的灰度值依照一定的參数规则进行加权平均。这样能够有效滤去理想图像中叠加的高频噪声。

            通常滤波和边缘检測是矛盾的概念。抑制了噪声会使得图像边缘模糊,这回添加边缘定位的不确定性。而假设要提高边缘检測的灵敏度,同一时候对噪声也提高了灵敏度。

    实际project经验表明,高斯函数确定的核能够在抗噪声干扰和边缘检測精确定位之间提供较好的折衷方案。这就是所谓的高斯图像滤波,详细实现代码见下文。

    2.3 用一阶偏导的有限差分来计算梯度的幅值和方向

            关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就能够得图像在x和y方向上偏导数的两个矩阵。经常使用的梯度算子有例如以下几种:

            1)Roberts算子

    0_1319118788tu9f

            上式为其x和y方向偏导数计算模板。可用数学公式表达其每一个点的梯度幅值为:

    0_1319118857nq6k

            2)Sobel算子

    0_1319118933e59w

            上式三个矩阵分别为该算子的x向卷积模板、y向卷积模板以及待处理点的邻域点标记矩阵。据此可用数学公式表达其每一个点的梯度幅值为:

    0_1319119015ux8d

            3)Prewitt算子

            和Sobel算子原理一样,在此仅给出其卷积模板。

    0_1319119083ivvz

            4)Canny算法所採用的方法

            在本文实现的Canny算法中所採用的卷积算子比較简单,表达例如以下:

    0_13191191580buw

            其x向、y向的一阶偏导数矩阵,梯度幅值以及梯度方向的数学表达式为:

    0_131911920906fx

            求出这几个矩阵后,就能够进行下一步的检測过程。

    2.4 对梯度幅值进行非极大值抑制        图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这不过属于图像增强的过程)。

    在Canny算法中。非极大值抑制是进行边缘检測的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所相应的灰度值置为0,这样能够剔除掉一大部分非边缘的点(这是本人的理解)。

    0_1319119291xblz

    图1 非极大值抑制原理

            依据图1 可知,要进行非极大值抑制,就首先要确定像素点C的灰度值在其8值邻域内是否为最大。图1中蓝色的线条方向为C点的梯度方向,这样就能够确定其局部的最大值肯定分布在这条线上,也即出了C点外,梯度方向的交点dTmp1和dTmp2这两个点的值也可能会是局部最大值。

    因此,推断C点灰度与这两个点灰度大小就可以推断C点是否为其邻域内的局部最大灰度点。假设经过推断,C点灰度值小于这两个点中的任一个,那就说明C点不是局部极大值,那么则能够排除C点为边缘。这就是非极大值抑制的工作原理。

            作者觉得。在理解的过程中须要注意下面两点:

            1)中非最大抑制是回答这样一个问题:“当前的梯度值在梯度方向上是一个局部最大值吗?” 所以,要把当前位置的梯度值与梯度方向上两側的梯度值进行比較。

            2)梯度方向垂直于边缘方向。

            但实际上。我们仅仅能得到C点邻域的8个点的值。而dTmp1和dTmp2并不在当中。要得到这两个值就须要对该两个点两端的已知灰度进行线性插值,也即依据图1中的g1和g2对dTmp1进行插值,依据g3和g4对dTmp2进行插值,这要用到其梯度方向,这是上文Canny算法中要求解梯度方向矩阵Thita的原因。

            完毕非极大值抑制后。会得到一个二值图像,非边缘的点灰度值均为0,可能为边缘的局部灰度极大值点可设置其灰度为128。依据下文的详细測试图像能够看出,这样一个检測结果还是包括了非常多由噪声及其它原因造成的假边缘。

    因此还须要进一步的处理。

    2.5 用双阈值算法检測和连接边缘

            Canny算法中降低假边缘数量的方法是採用双阈值法。选择两个阈值(关于阈值的选取方法在扩展中进行讨论),依据高阈值得到一个边缘图像,这样一个图像含有非常少的假边缘,可是因为阈值较高,产生的图像边缘可能不闭合,未解决这样一个问题採用了另外一个低阈值。

            在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时。该算法会在断点的8邻域点中寻找满足低阈值的点,再依据此点收集新的边缘。直到整个图像边缘闭合。

  • 相关阅读:
    winform 通过 windows api 实现本机两个exe 消息推送
    winform ListView控件 绘制item和subitems颜色
    C# Windows Service创建和安装
    C# 解析xml文件(带命名空间 xmlns和 xmlns:xsi)
    H5+CSS3 实现分页功能
    C# HashTable Dictionary ConcurrentDictionary HashSet
    使用泛型返回类
    WPF的XAML注意点
    Http监听HttpListener接收请求的Nancy框架
    获取Windows后台进程
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/6734431.html
Copyright © 2011-2022 走看看