之前的博文大部分都写的很是详细,有不少分析过程,不过写起来确实很累人,通常一篇好的文章要整理个三四天,可是,时间愈来愈紧张,后续的一些算法可能就以随记的方式,把实现过程的一些比较容易出错和有价值的细节部分加以描述,而且可能须要对算法自己有必定了解的朋友才能明白我所描述的一些过程了。html
那这个系列的开篇,咱们以Canny边缘检测算法为头吧。算法
相关参考资料:函数
一、Canny边缘检测算法的实现。 测试
二、OpenCV(五)——超细节的Canny原理及算法实现 优化
三、OpenCV 之 边缘检测 spa
四、Opencv 3.0\opencv\sources\modules\imgproc\src\canny.cpp.net
Canny边缘检测是边缘检测算法领域里最为著名的算法,其标准实现过程就是下面五个步骤:code
一、高斯模糊。htm
二、 计算梯度幅值和方向。blog
三、非最大值抑制。
四、 双阀值。
五、 滞后边界跟踪
我的认为,第一步能够不做为标准流程,在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。