原文:Canny边缘检测原理及C#程序实现

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)

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

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;}} 

第三步:非极大值抑制

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

  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是最大梯度}}
}   

第四步:双阈值边缘检测

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

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;}}}}}

最后,效果图如下:

原图:

灰度图:

边缘图:

Canny边缘检测原理及C#程序实现相关推荐

  1. 【canny边缘检测】canny边缘检测原理及代码详解

    文章目录 前言 canny边缘检测算法主要流程 一.高斯模糊 二.图像梯度计算 三.非极大值抑制 四.双阈值边界跟踪 前言 本文通过介绍canny边缘检测原理与代码解析,希望能让大家深入理解canny ...

  2. Canny边缘检测原理及C++实现

    从11.21开始写博客,到今天一个多月了,写了20多篇了,希望自己能坚持下去吧! 在这里祝大家圣诞节快乐! Canny边缘检测算法是澳大利亚科学家John F. Canny在1986年提出来的,不得不 ...

  3. opencv学习笔记18:canny算子边缘检测原理及其函数使用

    canny边缘检测原理 去噪:边缘检测容易受到噪声的影响,在此之间,先去噪,通常采用高斯滤波器.opencv学习笔记11:图像滤波(均值,方框,高斯,中值) 梯度:对去噪后的图像采用sobel算子计算 ...

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

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

  5. Python OpenCV -- Canny 边缘检测 (十一)

    Canny 边缘检测 原理 Canny 边缘检测算法 是 John F. Canny 于 1986年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的 最优算法, 最优边缘检测的三个主要评价标 ...

  6. python canny优化_python实现canny边缘检测

    canny边缘检测原理 canny边缘检测共有5部分组成,下边我会分别来介绍. 1 高斯模糊(略) 2 计算梯度幅值和方向. 可选用的模板:soble算子.Prewitt算子.Roberts模板等等; ...

  7. OpenCV中Canny边缘检测

    OpenCV中Canny边缘检测 具体的Canny边缘检测原理: 1.消除噪声,使用高斯平滑滤波器卷积降噪 2.计算梯度幅值和方向.利用Sobel滤波器. 得到x和y方向的导数Gx和Gy 计算梯度的幅 ...

  8. CV笔记6:图像边缘检测之一阶微分算子、二阶微分算子、Canny边缘检测(基于python-opencv实现)

    目录 一.边缘简介 1.1 何为边缘 1.2 产生原因 二.边缘检测方法 2.1 一阶微分算子计算原理 2.2 噪声对一阶微分算子的影响及解决方案 2.3 常见的一阶微分算子 2.3.1 Robert ...

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

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

最新文章

  1. 【C++】【三】单向链表
  2. 编程方法学15:指针要点回顾
  3. 设置mysql最大连接数的方法
  4. 深入理解connect by
  5. 图解LinkedHashMap原理
  6. c语言遍历文件内容_C语言学习第28篇---动态内存分配剖析
  7. Vscode合并develop代码分支到master开发分支
  8. v-show在elementui中表格组件失效问题
  9. docker commit新镜像之后删除旧镜像
  10. 原神个人测评:不好玩的精品
  11. 文章同一位置引用多篇参考文献标注方法
  12. S3C2440裸机------GPIO
  13. 2022好用的便签记事日程提醒软件有哪些
  14. Torch是什么,如何使用Torch,为什么选择Torch?
  15. 对自己的反思 (闲暇中的面试总结)
  16. 微信小程序开发之——map组件
  17. Linux文件目录颜色的含义
  18. VLookup函数详细教程
  19. Arduino使用HC05蓝牙模块与手机连接
  20. 欧暇·地中海酒店深圳再布局 深圳国际会展中心和平店进入试营业

热门文章

  1. win7关闭开机启动项_电脑开机全是各种广告?来看看我怎么解决的吧
  2. php 获取鼠标的坐标,如何实时获取鼠标的当前坐标-
  3. SQL Select语句完整的执行顺序:
  4. dede 怎样调用其它栏目的文章或者缩略图列表且有分页效果?
  5. MySQL基本命令 (二)
  6. javascript Date 格式化
  7. Hibernate 异常 Unable to instantiate default tuplizer
  8. 初涉程序员之路的感悟
  9. chrome浏览器下“多余”的2px
  10. 使用动态代理,提高工作效率