图像边缘检測--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函数效果

点击(此处)折叠或打开
#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;
}

边缘检測后:

图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般能够看作是一个阶跃。既从一个灰度值在非常小的缓冲区域内急剧变化到还有一个灰度相差较大的灰度值。图象的边缘部分集中了图象的大部分信息。图象边缘的确定与提取对于整个图象场景的识别与理解是非常重要的,同一时候也是图象切割所依赖的重要特征,边缘检測主要是图象的灰度变化的度量、检測和定位。自从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)高斯核实现

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

上式为离散化的二维高斯函数,确定參数就能够得到二维核向量。
注意1:关于參数Sigma的取值详见上篇博文。
注意2:在求的高斯核后,要对整个核进行归一化处理。
2)图像高斯滤波
对图像进行高斯滤波,听起来非常玄乎。事实上就是依据待滤波的像素点及其邻域点的灰度值依照一定的參数规则进行加权平均。这样能够有效滤去理想图像中叠加的高频噪声。
通常滤波和边缘检測是矛盾的概念。抑制了噪声会使得图像边缘模糊,这回添加边缘定位的不确定性。而假设要提高边缘检測的灵敏度,同一时候对噪声也提高了灵敏度。
实际project经验表明,高斯函数确定的核能够在抗噪声干扰和边缘检測精确定位之间提供较好的折衷方案。这就是所谓的高斯图像滤波,详细实现代码见下文。
2.3 用一阶偏导的有限差分来计算梯度的幅值和方向
关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就能够得图像在x和y方向上偏导数的两个矩阵。经常使用的梯度算子有例如以下几种:
1)Roberts算子

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

2)Sobel算子

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

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

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

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

求出这几个矩阵后,就能够进行下一步的检測过程。
2.4 对梯度幅值进行非极大值抑制 图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这不过属于图像增强的过程)。
在Canny算法中。非极大值抑制是进行边缘检測的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所相应的灰度值置为0,这样能够剔除掉一大部分非边缘的点(这是本人的理解)。

图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邻域点中寻找满足低阈值的点,再依据此点收集新的边缘。直到整个图像边缘闭合。
图像边缘检測--OpenCV之cvCanny函数的更多相关文章
- 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...
- Canny边缘检測算法原理及其VC实现具体解释(一)
图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般能够看作是一个阶跃,既从一个灰度值在非常小的缓冲区域内急剧变化到还有一个灰度相差较大的灰度值.图象的边缘部分集中了图象的大部分信息,图 ...
- 图像处理之Canny边缘检測
图像处理之Canny 边缘检測 一:历史 Canny边缘检測算法是1986年有John F. Canny开发出来一种基于图像梯度计算的边缘 检測算法,同一时候Canny本人对计算图像边缘提取学科的发展 ...
- Python下opencv使用笔记(七)(图像梯度与边缘检測)
梯度简单来说就是求导,在图像上表现出来的就是提取图像的边缘(无论是横向的.纵向的.斜方向的等等),所须要的无非也是一个核模板.模板的不同结果也不同.所以能够看到,全部的这些个算子函数,归结究竟都能够用 ...
- OpenCV图像处理篇之边缘检測算子
3种边缘检測算子 灰度或结构等信息的突变位置是图像的边缘,图像的边缘有幅度和方向属性.沿边缘方向像素变化缓慢,垂直边缘方向像素变化剧烈.因此,边缘上的变化能通过梯度计算出来. 一阶导数的梯度算子 对于 ...
- 图像边缘检测--OpenCV之cvCanny函数
图像边缘检测--OpenCV之cvCanny函数 分类: C/C++ void cvCanny( const CvArr* image, CvArr* edges, double threshold1 ...
- OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)
收入囊中 差分在边缘检測的角色 Sobel算子 OpenCV sobel函数 OpenCV Scharr函数 prewitt算子 Roberts算子 葵花宝典 差分在边缘检測究竟有什么用呢?先看以下的 ...
- 利用opencv中的级联分类器进行人脸检測-opencv学习(1)
OpenCV支持的目标检測的方法是利用样本的Haar特征进行的分类器训练,得到的级联boosted分类器(Cascade Classification).注意,新版本号的C++接口除了Haar特征以外 ...
- OpenCV2马拉松第15圈——边缘检測(Laplace算子,LOG算子)
收入囊中 拉普拉斯算子 LOG算子(高斯拉普拉斯算子) OpenCV Laplacian函数 构建自己的拉普拉斯算子 利用拉普拉斯算子进行图像的锐化 葵花宝典 在OpenCV2马拉松第14圈--边缘检 ...
随机推荐
- struts中action名称反复导致的神秘事件
近期由于项目需求变更.须要本人对当中的某个业务功能进行改动.本人依照前台页面找action,依据action找代码的逻辑进行了改动(公司项目是ssh框架,struts配置全部是通过注解的方式进行.配置 ...
- 显示vim当前颜色主题
在vim内,查看colors_name :echo g:colors_name 如果值为空,那么默认为:default主题
- linux c:关联变量的双for循环
举例说明: 比如打印一个倒三角形. * * * * * * * * * * 第一层循环为行数,第二层循环为每行打印的*数,且随着行数的变化,打印的*数也随着改变. 这就是关联变量的双层循环.我的做法是 ...
- WCF入门学习1-最简单的一次通信
跟着msdn的教程试了一下wcf,真心好用 1.先创建一个wcf服务库,是服务类库,远程的lib 2.全部按照默认设置,不修改.然后点发布,会出现一个wcf测试客户端,可以看有没有发布成功. 3.ms ...
- PHP函数之类
if(isset($_POST['dosubmit'])) { } 打散 $array_urls = explode("\r\n",$inurl_str); foreach ($a ...
- 【Android】20.1 音频播放
分类:C#.Android.VS2015: 创建日期:2016-03-11 一.简介 MediaPlayer:适合每次播放一个音频资源或者音频文件的场合. SoundPool:适合同时播放多个音频资源 ...
- LoadRunner Controller 常见用法
Controller 工作原理:通过场景设计来模拟用户的真实操作并调用vugen中的脚本,再通过设置的压力机产生压力 Scenario-convert scenario to the percenta ...
- tortisegit 创建分支和合并分支
第一步:创建本地分支 点击右键选择TortoiseGit,选择Create Branch…,在Branch框中填写新分支的名称(若选中”switch to new branch”则直接转到新分支上,省 ...
- CAsyncSocket编程 MFC
许多时候我们实现网络编程使用的是winsock api函数,虽然这些函数使用起来也很方便,很灵活,但是VC++的MFC类库中提供了CAsyncSocket这样一个套接字类,用它来实现socket编程会 ...
- GNU风格 ARM汇编语法1
汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等. 这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.GNU汇编语言语句格式 任何Linux汇编行 ...