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)

然后,将每个像素点的梯度的值和方向分别放入两个数组中,程序如下:

  1. <span style="font-size:16px;">byte[] orients = new byte[width * height];// 梯度方向数组
  2. float[,] gradients = new float[width, height];// 梯度值数组
  3. double gx, gy;
  4. for (int i = 1; i < (height - 1);i++ )
  5. {
  6. for (int j = 1; j < (width - 1); j++)
  7. {
  8. //求水平和竖直导数
  9. 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]);
  10. 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]);
  11. gradients[j, i] = (float)Math.Sqrt(gx * gx + gy * gy);
  12. if (gx == 0)
  13. {
  14. orientation = (gy == 0) ? 0 : 90;
  15. }
  16. else
  17. {
  18. double div = (double)gy / gx;
  19. if (div < 0)
  20. {
  21. orientation = 180 - Math.Atan(-div) * toAngle;
  22. }
  23. else
  24. {
  25. orientation = Math.Atan(div) * toAngle;
  26. }
  27. //只保留成4个方向
  28. if (orientation < 22.5)
  29. orientation = 0;
  30. else if (orientation < 67.5)
  31. orientation = 45;
  32. else if (orientation < 112.5)
  33. orientation = 90;
  34. else if (orientation < 157.5)
  35. orientation = 135;
  36. else orientation = 0;
  37. }
  38. orients[i*width+j] = (byte)orientation;
  39. }
  40. } </span>

第三步:非极大值抑制

如果直接把梯度作为边缘的话,将得到一个粗边缘的图像,这不满足上面提到的准则,我们希望得到定位准确的单像素的边缘,所以将每个像素点的梯度与其梯度方向上的相邻像素比较,如果不是极大值,将其置0,否则置为某一不大于255的数,程序如下:

  1. <span style="font-size:16px;"> float leftPixel = 0, rightPixel = 0;
  2. for (int y = 1; y <height-1; y++)
  3. {
  4. for (int x = 1; x < width-1; x++)
  5. {
  6. //获得相邻两像素梯度值
  7. switch (orients[y * width + x])
  8. {
  9. case 0:
  10. leftPixel = gradients[x - 1, y];
  11. rightPixel = gradients[x + 1, y];
  12. break;
  13. case 45:
  14. leftPixel = gradients[x - 1, y + 1];
  15. rightPixel = gradients[x + 1, y - 1];
  16. break;
  17. case 90:
  18. leftPixel = gradients[x, y + 1];
  19. rightPixel = gradients[x, y - 1];
  20. break;
  21. case 135:
  22. leftPixel = gradients[x + 1, y + 1];
  23. rightPixel = gradients[x - 1, y - 1];
  24. break;
  25. }
  26. if ((gradients[x, y] < leftPixel) || (gradients[x, y] < rightPixel))
  27. {
  28. dis[y * disdata.Stride + x] = 0;
  29. }
  30. else
  31. {
  32. dis[y * disdata.Stride + x] = (byte)(gradients[x, y] /maxGradient* 255);//maxGradient是最大梯度
  33. }
  34. }
  35. }   </span>

第四步:双阈值边缘检测

由上一步得到的边缘还有很多伪边缘,我们通过设置高低双阈值的方法去除它们,具体思想是:梯度值大于高阈值的像素点认为其一定是边缘,置为255,梯度值小于低阈值的像素点认为其一定不是边缘置为0,介于两阈值之间的点像素点为待定边缘。然后,考察这些待定边缘点,如果其像素点周围8邻域的梯度值都小于高阈值,认为其不是边缘点,置为0;至于,如何设定双阈值大小,我们可以根据实际情况设定,如设成100和20,也可以根据图像梯度值的统计信息设定,一般小阈值是大阈值的0.4倍即可。程序如下:

  1. <span style="font-size:16px;">fmean = fmean / maxGradient * 255;//某统计信息
  2. highThreshold = (byte)(fmean);//高阈值
  3. lowThreshold = (byte)(0.4 * highThreshold); //低阈值
  4. for (int y = 0; y < height; y++)
  5. {
  6. for (int x = 0; x < width; x++)
  7. {
  8. if (dis[y * disdata.Stride + x] < highThreshold)
  9. {
  10. if (dis[y * disdata.Stride + x] < lowThreshold)
  11. {
  12. dis[y * disdata.Stride + x] = 0;
  13. }
  14. else
  15. {
  16. if ((dis[y * disdata.Stride + x - 1] < highThreshold) &&
  17. (dis[y * disdata.Stride + x + 1] < highThreshold) &&
  18. (dis[(y - 1) * disdata.Stride + x - 1] < highThreshold) &&
  19. (dis[(y - 1) * disdata.Stride + x] < highThreshold) &&
  20. (dis[(y - 1) * disdata.Stride + x + 1] < highThreshold) &&
  21. (dis[(y + 1) * disdata.Stride + x - 1] < highThreshold) &&
  22. (dis[(y + 1) * disdata.Stride + x] < highThreshold) &&
  23. (dis[(y + 1) * disdata.Stride + x + 1] < highThreshold))
  24. {
  25. dis[y * disdata.Stride + x] = 0;
  26. }
  27. }
  28. }
  29. }
  30. }</span>

最后,效果图如下:

原图:

灰度图:

边缘图:

Canny边缘检测原理及C#程序实现的更多相关文章

  1. 学习笔记-canny边缘检测

    Canny边缘检测 声明:阅读本文需要了解线性代数里面的点乘(图像卷积的原理),高等数学里的二元函数的梯度,极大值定义,了解概率论里的二维高斯分布 1.canny边缘检测原理和简介 2.实现步骤 3. ...

  2. OpenCV: Canny边缘检测算法原理及其VC实现详解(转载)

    原文地址:http://blog.csdn.net/likezhaobin/article/details/6892176 原文地址:http://blog.csdn.net/likezhaobin/ ...

  3. Canny边缘检测算法原理及其VC实现详解(一)

    转自:http://blog.csdn.net/likezhaobin/article/details/6892176 图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个 ...

  4. Canny边缘检测算法原理及其VC实现详解(二)

    转自:http://blog.csdn.net/likezhaobin/article/details/6892629 3.  Canny算法的实现流程 由于本文主要目的在于学习和实现算法,而对于图像 ...

  5. [转载+原创]Emgu CV on C# (六) —— Emgu CV on Canny边缘检测

    Canny边缘检测也是一种边缘检测方法,本文介绍了Canny边缘检测的函数及其使用方法,并利用emgucv方法将轮廓检测解算的结果与原文进行比较. 图像的边缘检测的原理是检测出图像中所有灰度值变化较大 ...

  6. openCV实例:Canny边缘检测

    http://blog.sina.com.cn/s/blog_737adf530100z0jk.html 在第一次使用openCV程序成功对图像进行打开后,现在开始试验第二个例程试验:Canny边缘检 ...

  7. Atitit 边缘检测原理attilax总结

    Atitit 边缘检测原理attilax总结 1. 边缘检测的概念1 1.1. 边缘检测的用途1 2. 边缘检测方法分类1 3. 边缘检测的基本方法2 3.1. Roberts边缘检测算子2 3.2. ...

  8. Canny边缘检测

    1.Canny边缘检测基本原理      (1)图象边缘检测必须满足两个条件:一能有效地抑制噪声:二必须尽量精确确定边缘的位置.      (2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子.这 ...

  9. OpenCV图像Canny边缘检测

    Canny边缘检测 图像的边缘检测的原理是检测出图像中所有灰度值变化较大的点,而且这些点连接起来就构成了若干线条,这些线条就可以称为图像的边缘函数原型:     void cvCanny(       ...

随机推荐

  1. MySQL高性能优化实战总结

    1.1 前言 MySQL对于很多Linux从业者而言,是一个非常棘手的问题,多数情况都是因为对数据库出现问题的情况和处理思路不清晰.在进行MySQL的优化之前必须要了解的就是MySQL的查询过程,很多 ...

  2. 深入了解MySQL存储索引

    (一)关于存储引擎 创建合适的索引是SQL性能调优中最重要的技术之一.在学习创建索引之前,要先了解MySql的架构细节,包括在硬盘上面如何组织的,索引和内存用法和操作方式,以及存储引擎的差异如何影响到 ...

  3. 【Shell 开发】Shell 目录

    目录 [第一章]Shell 概述 [第二章]Shell 变量 [第三章]Shell 变量的数值计算 [第四章]Shell 条件测试表达式 [shell 练习1]编写Shell条件句练习 [shell ...

  4. 袋鼠云研发手记 | 袋鼠云EasyManager的TypeScript重构纪要

    作为一家创新驱动的科技公司,袋鼠云每年研发投入达数千万,公司80%员工都是技术人员,袋鼠云产品家族包括企业级一站式数据中台PaaS数栈.交互式数据可视化大屏开发平台Easy[V]等产品也在迅速迭代.在 ...

  5. eBay:美国各州最受欢迎的产品品类

    雨果网从美国媒体<商业内幕>8月26日的报道中获悉,电商巨头eBay近日发布了美国各州最受欢迎的产品品类.包括:加州人青睐女性高端配件,而新泽西 州的男人喜欢古龙香水.相比这些华丽配饰而言 ...

  6. win10 redis安装教程

    下载解压,没什么好说的,在解压后的目录下有以下这些文件: 在 命令行 启动服务端 redis目录下执行: redis-server.exe redis.windows.conf 如果需要 开机启动:执 ...

  7. 关于双系统下Ubuntu不能访问Windows中某个盘的问题

    1.问题描述   在Ubuntu系统下访问Windows系统中磁盘时出现无法访问的情况,具体如下显示:   该问题为磁盘挂载错误,需要进行修复. 2.解决办法   (1)打开终端:如果没有安装ntfs ...

  8. lintcode-419-罗马数字转整数

    419-罗马数字转整数 给定一个罗马数字,将其转换成整数. 返回的结果要求在1到3999的范围内. 说明 什么是 罗马数字? https://en.wikipedia.org/wiki/Roman_n ...

  9. 第6题 ZigZag转换

    题目描述如下: The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of ro ...

  10. Jarvis OJ平台basic部分wirteup

    Base64? 题目描述: GUYDIMZVGQ2DMN3CGRQTONJXGM3TINLGG42DGMZXGM3TINLGGY4DGNBXGYZTGNLGGY3DGNBWMU3WI=== Base3 ...