以前的博文大部分都写的非常详细,有很多分析过程,不过写起来确实很累人,一般一篇好的文章要整理个三四天,但是,时间越来越紧张,后续的一些算法可能就以随记的方式,把实现过程的一些比较容易出错和有价值的细节部分加以描述,并且可能需要对算法本身有一定了解的朋友才能明白我所描述的一些过程了。

  那这个系列的开篇,我们以Canny边缘检测算法为头吧。

  相关参考资料:

  1、Canny边缘检测算法的实现

  2、OpenCV(五)——超细节的Canny原理及算法实现

  3、OpenCV 之 边缘检测

  4、Opencv 3.0\opencv\sources\modules\imgproc\src\canny.cpp

Canny边缘检测是边缘检测算法领域里最为著名的算法,其标准实现过程就是下面五个步骤:

   1、高斯模糊。

  2、 计算梯度幅值和方向。

  3、非最大值抑制。

  4、 双阀值。

  5、 滞后边界跟踪

  个人认为,第一步可以不作为标准流程,在OpenCV的实现里,也没有他,有些文章里也说了可以用保边滤波来代替标准的高斯模糊的过程,其实,用高斯模糊,在其他参数相同的情况下,模糊后的结果线条会更少,这是因为模糊后边缘部分的细节有所丢失,这样在后续的非最大值抑制步骤里强边缘和若边缘的数据量会有所减少。

不过这个步骤带来的另外一个好处就是,算法的计算时间会减少,这主要是由于边缘信息的减少让最后一步的滞后边界跟踪计算量大为减少。

  在计算梯度幅值和方向时,最开始的Canny算法使用的时2领域,这个计算量要少,但能表达的边缘信息还是不够强烈,所以现在一般都采用Sobel算子的方式,计算处X和Y方向的梯度和幅值,在幅值计算过程中,可以使用L1范数(绝对值之和),也可以使用L2范数(欧式距离),两者在结果上还是稍有差异的,总体来说,L2范数的结果稍微好一点。

  计算这个的过程,我们可以借助SSE图像算法优化系列九:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms) 一文的相关技巧来加速,当我们需要使用L2范数时,这个结果是个浮点数,为了提高后续的处理速度,我们可采用 LinePM[X] = sqrtf((float)((Gx * Gx + Gy * Gy) << 12));这样的过程把他定点化,保存到unsigned short类型里,即可以节省内存,有能有利于后续的处理,而且在本例中计算的精度也能够保证了。

  非最大值抑制过程,也就是根据当前点的梯度的方向,比较当前幅值及其周边2个位置的幅值,如果当前幅值是他们的最大值,则该点需保留,否则,该点舍弃。那么这里的实现过程可以和后面的双阈值处理放置在同一个过程中,这样可以多方面提高算法速度,因为,如果当前的幅值小于小的阈值,哪怕他是局部最大值,我们也要舍弃他,所以就根本不用计算他,而在确定某个点是改保留时,通过进一步判断其和大的阈值之间的关系,可以同时确定他是否为强边缘,或者弱边缘。

  在这个过程中,我们可以像OpenCV那样采用简易的判断,把梯度分为[0,22.5],[22.5  67.5],[67.5 90]三个范围,在小于22.5度时,只需比较当前左右两个点的幅值,当大于67.5度时,比较上下两个点的幅值,否则,比较对角线上两个点的幅值。

  另外一种方式就是如下图所示,进行插值比较:

  这种方式理论上讲更为精确一些,但是带来主要时计算量的增加。

在采用OpenCV方法实现时,前面的幅值计算部分,我们可以考虑不用开平方,因为我们只需要比较大小,平方值大,同样也就代表原始值大,当然这个时候我们需要对用户输入的LowThreshold和HighThreshold进行同步的平方处理。

在滞后边界跟踪方面,我觉得有一篇文章讲的比较好,我直接引用他的说法:

    那么问题来了,弱边缘到底是边缘,还是由于噪点导致的梯度突变。

    判定依据有多种。有的人是判定弱边缘点的8邻域中是否存在强边缘,如果有则将弱边缘设置成强的。没有就认为是假边缘。

    另一种方案是用搜索算法,通过强边缘点,搜索8领域是否存在弱边缘,如果有,以弱边缘点为中心继续搜索,直到搜索不到弱边缘截止。

   我看大部分人用的都是第二种方案,第二种方案也有二种实现方式,一个时递归法,一个是普通的循环法,递归容易导致堆栈溢出,还是使用循环法,速度快,而且效果稳定。

  整个实现过程种,除了计算梯度和幅值时,可以使用SSE加速外,其他的过程由于有太多的判断和使用不连续位置的内存等原因,是不太可能用SSE进行加速的。

在内存占用方面,只需要梯度和幅值方面的数据(分别用signed short和unsigned short类型来保存),大约用6倍大小的图像内存,另外,为了能处理边缘像素,还需要一点点额外的小内存(注意在非最大值抑制时也可以使用Sobel边缘检测算法那个文章里的那种技巧来处理边缘)。

  针对这个过程,我们编制了一个UI界面,比较测试各个算法的结果:

  下面进行一些简单的测试:

       

          原图                                 无预处理,L1Gradient, OpenCV版抑制,低阈值50,高阈值150

      

无预处理,L2Gradient, OpenCV版抑制,低阈值50,高阈值150           无预处理,L1Gradient, 精确版抑制,低阈值50,高阈值150

      

无预处理,L2Gradient, 精确版抑制,低阈值50,高阈值150      高斯模糊0.5,L2Gradient, 精确版抑制,低阈值50,高阈值150

     

高斯模糊1,L2Gradient, 精确版抑制,低阈值50,高阈值150       保边滤波,L2Gradient, 精确版抑制,低阈值50,高阈值150

  看得出,不同的配置还是有不同的效果的。但是硬要说谁号谁坏,可能还是难以肯定。

  另外,测试中发现使用精确版本的抑制在线条拐角的地方可能连续性会比OpenCV版本要好一些,不会出现断裂。

  在耗时方面,其实这个函数的效果和很多参数还是有关的,正如前面所说,如果进行了预处理,耗时会短一些,如果低阈值越大,耗时也越短,选用OpenCV的抑制算法速度也能有所提高。测试2500*2000的灰度图,大概需要60ms.

  测试Demo:http://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar

【算法随记】Canny边缘检测算法实现和优化分析。的更多相关文章

  1. Canny边缘检测算法(基于OpenCV的Java实现)

    目录 Canny边缘检测算法(基于OpenCV的Java实现) 绪论 Canny边缘检测算法的发展历史 Canny边缘检测算法的处理流程 用高斯滤波器平滑图像 彩色RGB图像转换为灰度图像 一维,二维 ...

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

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

  3. 一些关于Canny边缘检测算法的改进

    传统的Canny边缘检测算法是一种有效而又相对简单的算法,可以得到很好的结果(可以参考上一篇Canny边缘检测算法的实现).但是Canny算法本身也有一些缺陷,可以有改进的地方. 1. Canny边缘 ...

  4. Canny边缘检测算法的一些改进

    传统的Canny边缘检测算法是一种有效而又相对简单的算法,可以得到很好的结果(可以参考上一篇Canny边缘检测算法的实现).但是Canny算法本身也有一些缺陷,可以有改进的地方. 1. Canny边缘 ...

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

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

  6. 十五 Canny边缘检测算法

    一.Canny算法介绍 Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是: 好的检测- 算法能够尽可能多地标识出图像中的实际边缘. 好的定位- 标识出的边缘要尽可能与实际图像中的实 ...

  7. 【数字图像分析】基于Python实现 Canny Edge Detection(Canny 边缘检测算法)

    Canny 边缘检测算法 Steps: 高斯滤波平滑 计算梯度大小和方向 非极大值抑制 双阈值检测和连接 代码结构: Canny Edge Detection | Gaussian_Smoothing ...

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

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

  9. Canny边缘检测算法原理及C语言实现详解

    Canny算子是John Canny在1986年提出的,那年老大爷才28岁,该文章发表在PAMI顶级期刊上的(1986. A computational approach to edge detect ...

随机推荐

  1. 51Nod1362 搬箱子 排列组合,中国剩余定理

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1362.html 题目传送门 - 51Nod1362 题意 题解 首先考虑枚举斜着走了几次.假设走了 ...

  2. Ubuntu18.04上安装Docker-Compose

    1.进入https://github.com/docker/compose/releases 查看最新版本,当前版本为1.23.1 sudo curl -L https://github.com/do ...

  3. JAVA 关键字及其作用解释

    1. 访问控制 1) private 私有的 private 关键字是访问控制修饰符,可以应用于类.方法或字段(在类中声明的变量). 只能在声明 private(内部)类.方法或字段的类中引用这些类. ...

  4. js(javaScript)的各种事件触发,以常见为主eg:onclick

    js的各种事件触发,以常见为主eg:onclick1.onclick,点击后触发事件 (1)<h1 onclick="this.innerHTML='谢谢!'">请点击 ...

  5. bootstrapTable

    一个详细的教程 table参数 bootstrap table使用总结 BootstrapTable使用实例 事件event 事件函数的用法: 方法1 $('#table').bootstrapTab ...

  6. MongDB-基础

    首先吐槽一下,MongDB用到了JS的引擎,只要涉及到了JS,语法就变得又臭又长,真是无语 还有,MongDB的安装真是麻烦,我用的是win10环境,怎么装都报服务错误,redis一装就可以用,希望m ...

  7. HYSBZ 4034 【树链剖分】+【线段树 】

    <题目链接> 题目大意: 有一棵点数为 N 的树,以点 1 为根,且树点有权值.然后有 M 个 操作,分为三种: 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x ...

  8. vue笔记-模板,计算属性,class与style,data属性

    数据和方法 1:只有当实例被创建时 data 中存在的属性才是响应式的,也可以预定义一些空的属性,唯一的意外就是Object.freeze(obj),这会阻止修改现有的属性;也就是说一个数据在放到根实 ...

  9. 从零搭建 ES 搜索服务(三)同义词搜索

    一.前言 上篇介绍了 ES 的基础搜索,能满足我们基本的需求,然而在实际使用中还可能希望搜索「番茄」能将包含「西红柿」的结果也罗列出来,本篇将介绍如何实现同义词之间的搜索. 二.安装 ES 同义词插件 ...

  10. Certbot让网站拥有免费https证书

    网站使用http协议,在chrome浏览器中总是报不安全,看着就让人不爽,自己建的网站,不安全总是会让自己心慌慌.看到有头有脸的网站都是https开头,心中自然也想装逼一把,让自己的网站高端大气上档次 ...