当前位置: 首页 > 编程日记 > 正文

图像边缘检测--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 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。

  • 注意事项:cvCanny只接受单通道图像作为输入。
  • 外部链接:经典的canny自调整阈值算法的一个opencv的实现见在OpenCV中自适应确定canny算法的分割门限
  • 参考OpenCV中文官网:http://www.opencv.org.cn/index.php/Cv%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86#Canny

说明: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检测算法的实现进行详述。

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

1、边缘检测原理及步骤

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

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

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

        2)增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。

        3)检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

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)图像高斯滤波

对图像进行高斯滤波,听起来很玄乎,其实就是根据待滤波的像素点及其邻域点的灰度值按照一定的参数规则进行加权平均。这样可以有效滤去理想图像中叠加的高频噪声。

通常滤波和边缘检测是矛盾的概念,抑制了噪声会使得图像边缘模糊,这回增加边缘定位的不确定性;而如果要提高边缘检测的灵敏度,同时对噪声也提高了灵敏度。实际工程经验表明,高斯函数确定的核可以在抗噪声干扰和边缘检测精确定位之间提供较好的折衷方案。这就是所谓的高斯图像滤波,具体实现代码见下文。

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邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。

转载于:https://www.cnblogs.com/wuyida/p/6301385.html

相关文章:

微信小程序 封装网络请求并调用

微信小程序开发交流qq群 526474645 正文&#xff1a; util.js // 网络请求 const request function(url, method, data, msg, succ, fail, com) {// 小程序顶部显示Loadingwx.showNavigationBarLoading();if (msg ! "") {wx.showLoading({title: msg})}wx.requ…

什么是导师负责制_为什么一个导师是不够的

什么是导师负责制by Rick West由里克韦斯特(Rick West) 为什么一个导师是不够的 (Why one mentor just isn’t enough) A mentor can give career guidance and help with learning. They can teach you how to solve problems, network, and the list goes on.导师可以提供职…

CodeForces 114B 【STL应用】

思路&#xff1a; 原来string类能sort 和 swap....太强了.... 注意&#xff1a;字典序最小输出&#xff0c;因为某个地方写挫了&#xff0c;sort了n发&#xff0c;代码挫。 #include <bits/stdc.h> using namespace std; typedef long long LL;int tol; map<string,in…

微信小程序订单页面下拉刷新上拉分页加载

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; 效果图&#xff1a; 代码&#xff1a; json代码&#xff1a; {"enablePullDownRefresh": true,"backgroundColor": "#19ad19" } js代码&#xff1a…

从网络上获取一张图片简单的

告诉ScrollView缩放的视图&#xff0c;要设置scrollView的代理。 转载于:https://www.cnblogs.com/x1024598115/p/4182674.html

es6 generator_让我们探索一下ES6 Generators

es6 generatorby Tiago Lopes Ferreira由Tiago Lopes Ferreira 让我们探索一下ES6 Generators (Let’s explore ES6 Generators) Generators are an implementation of iterables.生成器是可迭代对象的实现 。 The big deal about generators is that they are functions tha…

没听说过这些,就不要说你懂并发了,three。

引言 很久没有跟大家再聊聊并发了&#xff0c;今天LZ闲来无事&#xff0c;跟大家再聊聊并发。由于时间过去的有点久&#xff0c;因此LZ就不按照常理出牌了&#xff0c;只是把自己的理解记录在此&#xff0c;如果各位猿友觉得有所收获&#xff0c;就点个推荐或者留言激励下LZ&am…

设计模式之代理模式(Proxy Pattern)

定义&#xff1a;为其他对象提供一种代理以控制这个对象的访问&#xff0c;也叫做委托模式。 咱们比作游戏&#xff0c;通俗讲代理模式就是&#xff0c;一个主题虚基类派生出两个子类&#xff0c;一个玩家类&#xff0c;实现相关操作&#xff0c;一个是代练类&#xff0c;代替执…

[微信小程序]给data的对象的属性赋值

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; <view wx:for"{{leixing}}"><button class"leixing_btn {{user_infor.lx_btnitem.divingtype…

无家可归的iPhone

by Fabrice Dubois通过Fabrice Dubois 无家可归的iPhone (Homeless iPhone) So, apparently the next iPhone won’t have a physical Home button. There’s been much speculation already about what that means for the user. The bottom area of the device, for some, w…

Spring 自动化装配Bean

声明一张cd的接口&#xff1a; public interface CompactDisc {public abstract void play(); } 实现cd接口&#xff1a; Component("SgtPeppers") public class SgtPeppers implements CompactDisc {private String title "Sgt.Peppers Lonely Hearts Club Ba…

js中函数,方法,事件对比区分,什么是方法,什么是函数

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; 简单的理解&#xff1a;函数是运行在本地的&#xff0c;方法是公用的。 事件是开关&#xff0c;通过某某事件触发某个函数 通常命名规范 函数的命名使用小写字母和下划线&#xff…

笔记 JVM调优流程

待续 转载于:https://www.cnblogs.com/leeeee/p/7276287.html

创建新的apple id_Google是新的Apple吗?

创建新的apple idby Sumit Gupta由Sumit Gupta Google是新的Apple吗&#xff1f; (Is Google the new Apple?) 随着众多设备的推出&#xff0c;谷歌试图击败苹果。 “由Google制造”会使Google更像Apple吗&#xff1f; (With the launch of numerous devices, Google is tryi…

yeomen/bower/grunt

yeomen: npm install yo angular-in-action project npm install -g generator-angular npm install -g genrator-webapp yo angular learnangular new angular project yo webapp grunt-by-yeomen package.json npm install (执行package.json所指定的依赖包) bower: npm ins…

Window Server 2008 R2 安装 Share Point 2013

原文地址&#xff1a;http://www.cnblogs.com/jianyus/p/3631905.html转载于:https://www.cnblogs.com/gaobing/p/4191060.html

esp freertos_如何开始使用FreeRTOS和ESP8266

esp freertosby Denis Nuțiu丹尼斯努尤(Denis Nuțiu) 如何开始使用FreeRTOS和ESP8266 (How to get started with FreeRTOS and ESP8266) Recently, I purchased a NodeMCU from AliExpress for about $4. The reason I did this was to find out what all the fuss is about…

[SCOI2007]修车

题目描述 同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员&#xff0c;不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序&#xff0c;使得顾客平均等待的时间最小。 说明&#xff1a;顾客的等待时…

[微信小程序]时间戳转日期

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; // util.js //时间戳转换成日期时间 function js_date_time(unixtime) {var dateTime new Date(parseInt(unixtime) …

React模式:集中式PropTypes

通过集中化PropType避免重复自己 (Avoid repeating yourself by centralizing PropTypes) There are three popular ways to handle types in React: PropTypes, TypeScript and Flow. This post is about PropTypes, which are currently the most popular.在React中有三种流行…

Java Class SecurityManager

# 前言 简单了解 SecurityManager。具体查阅 API。 # What 它是 Java 沙盒模型控制安全的重要一个环节。它是 Java 的一个类。下面一段话源于SecurityManager API&#xff1a; The security manager is a class that allows applications to implement a security policy. It a…

微信小程序,对象转换成数组

有问题可以扫码加我微信&#xff0c;有偿解决问题。承接小程序开发。 微信小程序开发交流qq群 173683895 、 526474645 &#xff1b; 正文&#xff1a; 对象转数组: var jiu res.data.k4.f3var nArr [];for (var i in jiu){nArr.push(jiu[i]);}console.log(jiu);consol…

sql 事务使用

BEGIN TRAN Tran_Money --开始事务DECLARE tran_error int; SET tran_error 0;BEGIN TRY UPDATE tb_Money SET MyMoney MyMoney - 30 WHERE Name 刘备;SET tran_error tran_error ERROR;--测试出错代码&#xff0c;看看刘备的钱减少&#xff0c;关羽的钱是否会增加--SE…

一群算法_树遍历解释:他们就像一群懒惰的学生,试图欺骗他们的考试

一群算法by Sachin Malhotra由Sachin Malhotra 树遍历解释&#xff1a;他们就像一群懒惰的学生&#xff0c;试图欺骗他们的考试 (Tree Traversals explained: They’re like a class of lazy students trying to cheat on their exam) Imagine that you are enrolled in a mat…

微信小程序转发 分享 打电话功能,完整代码附效果图

微信小程序开发交流qq群 581478349 承接微信小程序开发。扫码加微信。 正文&#xff1a; 按钮绑定在页面内发起转发事件onShareApp:(注意这里是button 并且给他设置了open-type"share" 属性) <button classbottom_1 bottom_1_zf open-type"share" bi…

《DSP using MATLAB》示例 Example 6.25

代码&#xff1a; % x [-0.9, 0.81]; [y, L, B] QCoeff(x, 3) % Unquantized parameters r 0.9; theta pi/3; a1 -2*r*cos(theta); a2 r*r; p1 r*exp(j*theta); p2 p1;% Quantized parameters: N 3; [ahat, L, B] QCoeff([a1, a2], 3); rhat sqrt(ahat(2)); thetah…

sqlserver查询自定义的函数

1)sp_helptext同样适应用自定义函数 2)sys.sql_modules表也可以查 查看函数的源代码: exec sp_helptext 函数名转载于:https://www.cnblogs.com/toSeeMyDream/p/4195030.html

辍学的名人_我辍学去追求成为网络开发人员和设计师的梦想

辍学的名人by Carlos Sz由Carlos Sz 我辍学去追求成为网络开发人员和设计师的梦想 (I dropped out of college to pursue my dreams of being a web developer and designer) When I was 14, I discovered HTML. Thanks to my computer science teacher at school. And from t…

Java中的static关键字的用法

1.静态方法 static&#xff1a;通常在一个类中定义一个方法为static&#xff0c;那就是说&#xff0c;无需本类的对象即可调用此方法 声明为static的方法有以下几条限制&#xff1a; &#xff08;1&#xff09;它们仅能调用其他的static方法。 &#xff08;2&#xff09;它们只…

[微信小程序]上传单张和多张图片

微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 上传单张图片并展示, <button bindtap"upimg" classjia_img >上传</button> <image src"{{tempFilePaths[0]}}"></image> data{ tempFilePaths:[]; },u…