Canny边缘检测原理及C#程序实现
http://blog.csdn.net/yjz_uestc/article/details/6664937
Canny边缘检测是被公认的检测效果最好的边缘检测方法,是由John F. Canny于1986年提出,算法目标是找出一个最优的边缘检测的方法,所谓最优即:1.好的检测:算法能够尽可能的标识出图像的边缘;2.好的定位:标识出的边缘要尽可能的与实际边缘相接近;3.最小响应:图像中的边缘只能标识一次,并且不能把噪声标识成边缘。同时我们也要满足3个准则:信噪比准则、定位精度准则、单边缘响应准则。
Canny边缘检测算法可分为4步:
高斯滤波器平滑、计算梯度、非极大值抑制、双阈值边缘检测和边缘连接。
(经典不会随着时间褪色,算法也是一样)
下面将逐步讲解并给出程序:
第一步:高斯平滑
为什么要对图像(灰度图像)进行高斯平滑预处理呢?高斯滤波器对去除服从正态分布的的噪声很有效,我做过实验,随着高斯模板的增大,被识别的边缘会逐渐减少,所以通过选着适合大小的高斯模板平滑,可以比较有效的去除一些伪边缘点。
第二步:计算梯度
首先,由一阶导数算子(一般用sobel模板)计算灰度图像每个像素点在水平和竖直方向上的导数Gx、Gy,得出梯度向量(Gx,Gy),计算梯度的值G和方向theta:
G=sqrt(Gx*Gx+Gy*Gy) theta=arctan(Gy/Gx)
然后,将每个像素点的梯度的值和方向分别放入两个数组中,程序如下:
- <span style="font-size:16px;">byte[] orients = new byte[width * height];// 梯度方向数组
- float[,] gradients = new float[width, height];// 梯度值数组
- double gx, gy;
- for (int i = 1; i < (height - 1);i++ )
- {
- for (int j = 1; j < (width - 1); j++)
- {
- //求水平和竖直导数
- gx = bufdata[(i - 1) * width + j] + bufdata[(i + 1) * width + j] - bufdata[(i -1) * width + j - 1] - bufdata[(i + 1) * width + j - 1]+ 2*(bufdata[i * width + j + 1] - bufdata[i * width + j - 1]);
- gy = bufdata[(i - 1) * width + j - 1] + bufdata[(i + 1) * width + j + 1] - bufdata[(i + 1) * width + j - 1] - bufdata[(i + 1) * width + j + 1]+ 2*(bufdata[(i - 1) * width + j] - bufdata[(i + 1) * width + j - 1]);
- gradients[j, i] = (float)Math.Sqrt(gx * gx + gy * gy);
- if (gx == 0)
- {
- orientation = (gy == 0) ? 0 : 90;
- }
- else
- {
- double div = (double)gy / gx;
- if (div < 0)
- {
- orientation = 180 - Math.Atan(-div) * toAngle;
- }
- else
- {
- orientation = Math.Atan(div) * toAngle;
- }
- //只保留成4个方向
- if (orientation < 22.5)
- orientation = 0;
- else if (orientation < 67.5)
- orientation = 45;
- else if (orientation < 112.5)
- orientation = 90;
- else if (orientation < 157.5)
- orientation = 135;
- else orientation = 0;
- }
- orients[i*width+j] = (byte)orientation;
- }
- } </span>
第三步:非极大值抑制
如果直接把梯度作为边缘的话,将得到一个粗边缘的图像,这不满足上面提到的准则,我们希望得到定位准确的单像素的边缘,所以将每个像素点的梯度与其梯度方向上的相邻像素比较,如果不是极大值,将其置0,否则置为某一不大于255的数,程序如下:
- <span style="font-size:16px;"> float leftPixel = 0, rightPixel = 0;
- for (int y = 1; y <height-1; y++)
- {
- for (int x = 1; x < width-1; x++)
- {
- //获得相邻两像素梯度值
- switch (orients[y * width + x])
- {
- case 0:
- leftPixel = gradients[x - 1, y];
- rightPixel = gradients[x + 1, y];
- break;
- case 45:
- leftPixel = gradients[x - 1, y + 1];
- rightPixel = gradients[x + 1, y - 1];
- break;
- case 90:
- leftPixel = gradients[x, y + 1];
- rightPixel = gradients[x, y - 1];
- break;
- case 135:
- leftPixel = gradients[x + 1, y + 1];
- rightPixel = gradients[x - 1, y - 1];
- break;
- }
- if ((gradients[x, y] < leftPixel) || (gradients[x, y] < rightPixel))
- {
- dis[y * disdata.Stride + x] = 0;
- }
- else
- {
- dis[y * disdata.Stride + x] = (byte)(gradients[x, y] /maxGradient* 255);//maxGradient是最大梯度
- }
- }
- } </span>
第四步:双阈值边缘检测
由上一步得到的边缘还有很多伪边缘,我们通过设置高低双阈值的方法去除它们,具体思想是:梯度值大于高阈值的像素点认为其一定是边缘,置为255,梯度值小于低阈值的像素点认为其一定不是边缘置为0,介于两阈值之间的点像素点为待定边缘。然后,考察这些待定边缘点,如果其像素点周围8邻域的梯度值都小于高阈值,认为其不是边缘点,置为0;至于,如何设定双阈值大小,我们可以根据实际情况设定,如设成100和20,也可以根据图像梯度值的统计信息设定,一般小阈值是大阈值的0.4倍即可。程序如下:
- <span style="font-size:16px;">fmean = fmean / maxGradient * 255;//某统计信息
- highThreshold = (byte)(fmean);//高阈值
- lowThreshold = (byte)(0.4 * highThreshold); //低阈值
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- if (dis[y * disdata.Stride + x] < highThreshold)
- {
- if (dis[y * disdata.Stride + x] < lowThreshold)
- {
- dis[y * disdata.Stride + x] = 0;
- }
- else
- {
- if ((dis[y * disdata.Stride + x - 1] < highThreshold) &&
- (dis[y * disdata.Stride + x + 1] < highThreshold) &&
- (dis[(y - 1) * disdata.Stride + x - 1] < highThreshold) &&
- (dis[(y - 1) * disdata.Stride + x] < highThreshold) &&
- (dis[(y - 1) * disdata.Stride + x + 1] < highThreshold) &&
- (dis[(y + 1) * disdata.Stride + x - 1] < highThreshold) &&
- (dis[(y + 1) * disdata.Stride + x] < highThreshold) &&
- (dis[(y + 1) * disdata.Stride + x + 1] < highThreshold))
- {
- dis[y * disdata.Stride + x] = 0;
- }
- }
- }
- }
- }</span>
最后,效果图如下:
原图:
灰度图:
边缘图:
Canny边缘检测原理及C#程序实现的更多相关文章
- 学习笔记-canny边缘检测
Canny边缘检测 声明:阅读本文需要了解线性代数里面的点乘(图像卷积的原理),高等数学里的二元函数的梯度,极大值定义,了解概率论里的二维高斯分布 1.canny边缘检测原理和简介 2.实现步骤 3. ...
- OpenCV: Canny边缘检测算法原理及其VC实现详解(转载)
原文地址:http://blog.csdn.net/likezhaobin/article/details/6892176 原文地址:http://blog.csdn.net/likezhaobin/ ...
- Canny边缘检测算法原理及其VC实现详解(一)
转自:http://blog.csdn.net/likezhaobin/article/details/6892176 图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个 ...
- Canny边缘检测算法原理及其VC实现详解(二)
转自:http://blog.csdn.net/likezhaobin/article/details/6892629 3. Canny算法的实现流程 由于本文主要目的在于学习和实现算法,而对于图像 ...
- [转载+原创]Emgu CV on C# (六) —— Emgu CV on Canny边缘检测
Canny边缘检测也是一种边缘检测方法,本文介绍了Canny边缘检测的函数及其使用方法,并利用emgucv方法将轮廓检测解算的结果与原文进行比较. 图像的边缘检测的原理是检测出图像中所有灰度值变化较大 ...
- openCV实例:Canny边缘检测
http://blog.sina.com.cn/s/blog_737adf530100z0jk.html 在第一次使用openCV程序成功对图像进行打开后,现在开始试验第二个例程试验:Canny边缘检 ...
- Atitit 边缘检测原理attilax总结
Atitit 边缘检测原理attilax总结 1. 边缘检测的概念1 1.1. 边缘检测的用途1 2. 边缘检测方法分类1 3. 边缘检测的基本方法2 3.1. Roberts边缘检测算子2 3.2. ...
- Canny边缘检测
1.Canny边缘检测基本原理 (1)图象边缘检测必须满足两个条件:一能有效地抑制噪声:二必须尽量精确确定边缘的位置. (2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子.这 ...
- OpenCV图像Canny边缘检测
Canny边缘检测 图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘函数原型: void cvCanny( ...
随机推荐
- 人脸检测及识别python实现系列(3)——为模型训练准备人脸数据
人脸检测及识别python实现系列(3)——为模型训练准备人脸数据 机器学习最本质的地方就是基于海量数据统计的学习,说白了,机器学习其实就是在模拟人类儿童的学习行为.举一个简单的例子,成年人并没有主动 ...
- Eclipse用java.util.Scanner时出现Resource leak: 'in' is never closed
Resource leak: 'in' is never closed : 直译为资源泄漏,‘in’一直没被关闭. 由于声明了数据输入扫描仪(Scanner in),从而获得了配置内存,但是结束时却没 ...
- Oracle数据库及图形化界面安装教程详解
百度云盘oracle数据库及图形化界面安装包 链接: https://pan.baidu.com/s/1DHfui-D2n1R6_ND3wDziQw 密码: f934 首先在电脑D盘(或者其他不是C盘 ...
- PHPCMS V9 的手机门户wap绑定单页面
当前的Phpcms V9手机网站的设置还有点弱,绑定的栏目不能设置选择模板,而且不能绑定单页面page.不过可以自定义做到绑定单页面page这一个功能:1.修改phpcms\modules\wap\i ...
- 数据库mysql的常规操作
1. 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的建立在计算机存储设备上的仓库. 简单来说是本身可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据进 ...
- python正则表达式re之compile函数解析
re正则表达式模块还包括一些有用的操作正则表达式的函数.下面主要介绍compile函数. 定义: compile(pattern[,flags] ) 根据包含正则表达式的字符串创建模式对象. 通过py ...
- Yii2 UploadedFile上传文件
通过 UploadFile::getInstance($model, $attribute); UploadFile::getInstances($model, $attribute); Upload ...
- 周总结<5>
周次 学习时间 新编写代码行数 博客量(篇) 学到知识点 12 10 100 1 路由器的设置(ospf协议):网页设计:哈夫曼树(C语言数构) Html案例: <!DOCTYPE html P ...
- lintcode-501-迷你推特
501-迷你推特 实现一个迷你的推特,支持下列几种方法 postTweet(user_id, tweet_text). 发布一条推特. getTimeline(user_id). 获得给定用户最新发布 ...
- lintcode-402-连续子数组求和
[402-连续子数组求和(http://www.lintcode.com/zh-cn/problem/continuous-subarray-sum/) 给定一个整数数组,请找出一个连续子数组,使得该 ...