Canny边缘检测算法总结

合集下载

canny边缘检测的原理

canny边缘检测的原理

canny边缘检测的原理
Canny边缘检测是一种多级检测算法,其基本原理如下:
首先,使用高斯滤波器对图像进行平滑处理,以减少图像中的噪声。

然后,计算图像的梯度大小和方向,以便确定边缘的位置和方向。

在计算梯度的过程中,会遍历每个像素点,判断该像素点是否为边缘点。

在Canny算法中,非极大值抑制和双阈值法是两个关键步骤。

非极大值抑制的目的是去除那些非边缘的像素点,保留可能的边缘点。

双阈值法则是为了进一步筛选出真正的边缘点,避免出现过多的假边缘。

最后,Canny算法会对检测到的边缘进行跟踪和连接,形成完整的边缘图像。

总的来说,Canny边缘检测算法是一种非常有效的边缘检测算法,能够准确地检测出图像中的边缘,并且在处理噪声和防止假边缘方面具有很好的性能。

最新Canny边缘检测与轮廓提取汇总

最新Canny边缘检测与轮廓提取汇总

C a n n y边缘检测与轮廓提取摘要................................................................................................................................................... Abstract (I)1 绪论 02 设计内容与OpenCV简介 (1)2.1 设计任务内容 (1)2.2 OpenCV简介 (1)3 理论分析 (2)3.1 边缘检测 (2)3.1.1 图像的边缘 (2)3.1.2 边缘检测的基本步骤 (2)3.2 轮廓提取 (3)4 边缘检测的算法比较 (4)4.1 Reborts算子 (4)4.2 Sobel算子 (5)4.3 Prewitt 算子 (5)4.4 Kirsch 算子 (7)4.5 LOG算子 (7)4.6 Canny算子 (8)5 实验仿真 (10)5.1算法设计 (10)5.2 实验结果 (11)6 分析与总结 (12)参考文献 (13)附录 (14)边缘检测是图像处理和计算机视觉中的基本问题,它的目的是标识出数字图像中亮度变化明显的点。

图像经过边沿检测处理之后,不仅大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。

事实上,边缘存在于图像的不规则结构和不平稳现象中,也即存在于信号的突变点处,这些点给出了图像轮廓的位置。

这些轮廓常常是我们在图像边缘检测时,所需要的非常重要的一些特征条件,这就需要我们对一幅图像检测并提取出它的边缘。

可用于图像边缘检测和轮廓提取的方法有很多,其中包括有常见的Robert边缘算子、Prewitt 边缘算子、Sobel边缘算子等等。

本文首先将会从数字图像处理的角度,对几种边缘检测算法进行详细的分析,然后会并选择其中一种边缘检测算法进行实验。

考虑到以后进一步的学习,本文将会使用openCV对算法进行实现。

改进的Canny图像边缘检测算法分析

改进的Canny图像边缘检测算法分析

网络天地171改进的Canny 图像边缘检测算法分析◆王 娟1 边缘检测的过程边缘检测主要用于解决图像边缘的真假,边缘的定向定位。

以此来初步分析图像和识别图像。

想要做好边缘检测,需要遵循以下五个方面的过程进行检测分析:(1)首先要明确的了解图像检测时图像的特性变化形式,运用合适的检测方法。

(2)根据特殊情况需求,利用多算子综合计算方法。

提取多范围的变化特性,以便检测图像上的所有特性变化。

(3)由于噪声的影响,使检测有一定的局限性。

检测时需要尽可能的滤除噪音。

还需要考虑到噪音的条件检测,进一步检测参数变化。

(4)尽可能用多种方法进行组合。

例如在检测时,先找到边缘,然后利用函数近似的放法,利用内插获得高精度定位。

(5)检测时,首先对原图像进行平滑处理,然后再进行边缘检测。

一方面可以有效地抑制噪音,另一方面也可以对边缘进行精准定位。

2 传统Canny 算子的基本工作原理由于系统固有的低筒滤波对实际的图像进行平滑,以至于边缘不明显。

所以,这就需要边缘检测通过寻找出图像局部具有最大梯度值的一些像素点。

同时由于摄影机以及周围环境的干扰,因此图片边缘检测必须满足两个条件:①逼近必须能够抑制噪音效应;②必须尽量精准的确定边缘的位置。

以高定位精准、高信噪比、单一边缘响应位判断标准。

Canny 算子的基本流程:输入原始图像→转为灰度图像→ 高斯平滑→ 梯度计算→ 非极大值抑制→ 双阈值检测→ 连接边缘→ 输出边缘图像。

作为一阶微分滤波器的Canny 算子属于边缘检测,有三大显著优点:1、最优过零点定位准则2、多峰值响应准则3、最大信噪比准则。

Canny 算子基本的工作原理首先便是利用高斯平滑滤波器对图像进行平滑处理,目的是为了去除噪音的影响,然后通过计算梯度差值,来完成领域局部强度值。

利用高阈值和低阈值以及双阈值的计算方法对图像边缘进行检测已达到增强边缘的效果。

3 Canny 算子的实现步骤Canny 算子在整体运算的过程中,其需要结合多个运算步骤进行整体的运算。

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

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

Canny边缘检测算法的⼀些改进传统的Canny边缘检测算法是⼀种有效⽽⼜相对简单的算法,可以得到很好的结果(可以参考上⼀篇)。

但是Canny算法本⾝也有⼀些缺陷,可以有改进的地⽅。

1. Canny边缘检测第⼀步⽤⾼斯模糊来去掉噪声,但是同时也会平滑边缘,使得边缘信息减弱,有可能使得在后⾯的步骤中漏掉⼀些需要的边缘,特别是弱边缘和孤⽴的边缘,可能在双阀值和联通计算中被剔除。

很⾃然地可以预见,如果加⼤⾼斯模糊的半径,对噪声的平滑⼒度加⼤,但也会使得最后得到的边缘图中的边缘明显减少。

这⾥依然⽤Lena图为例,保持Canny算法中⾼阀值100,低阀值50不变,⾼斯半径分别为2,3,5的Canny边缘⼆值图像如下。

可知⾼斯模糊把很多有⽤的边缘信息也模糊掉了,因此如何精确的选择⾼斯半径就相当重要。

⾼斯半径2 ⾼斯半径3 ⾼斯半径52. 在最初的Canny算法中是使⽤的最⼩的2x2领域来计算梯度幅值的。

这种⽅法对噪声很敏感,⽐较容易检测到伪边缘或漏掉真是边缘。

在上⼀篇算法实现中,实际上使⽤的是3x3的Sobel梯度算⼦,是⼀种⽐较好的选择。

3. 传统Canny算法的双阀值是全局固定的,因此双阀值⼤⼩的选取对最终的结果影响很⼤,也有⼀些经验,⽐如选择低阀值是⾼阀值的0.4或0.5。

然⽽这毕竟是⼀种经验选择,阀值的确定仍然很难决定⼀个最优值。

⽽且⼀个图像的不同局部区域可能需要各不相同的阀值来精确地找到真实边缘,因此全局阀值就不太合适了。

4. 传统算法仍然可能产⽣⼀条宽度⼤于1的边缘,达不到满意的⾼精度单点响应。

也就是需要继续细化边缘。

下⾯就⼀些可以改进的地⽅做⼀些讨论。

代替⾼斯模糊噪声是⾼频信号,边缘信号也属于⾼频信号。

既然⾼斯模糊不加区分的对所有的⾼频信息进⾏了模糊,效果⾃然不尽如⼈意。

那么⾃然就想到了带有保留边缘功能的各种选择性平滑⽅法,似乎在这⾥⽐⾼斯模糊会更加合适,那我们就来试⼀试。

带有保留边缘功能的平滑⽅法的基本思想不是让领域范围内的所有像素都参与该种平滑⽅法的计算,⽽是设定⼀个阀值,仅仅让和中⼼像素灰度的差值⼩于这个阀值的像素参与计算。

(完整版)Canny边缘检测算法总结

(完整版)Canny边缘检测算法总结

一.Canny边缘检测算法原理JohnCanny于1986年提出Canny算子,属于是先平滑后求导数的方法。

其处理过程大体上分为下面四部分。

1. 对原始图像进行灰度化Canny算法通常处理的图像为灰度图,因此如果获取的是彩色图像,那首先就得进行灰度化。

对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。

以RGB格式的彩图为例,通常灰度化采用的方法主要有:方法1:Gray=(R+G+B)/3;方法2:Gray=0.299R+0.587G+0.114B;(这种参数考虑到了人眼的生理特点)至于其他格式的彩色图像,可以根据相应的转换关系转为RGB然后再进行灰度化;在编程时要注意图像格式中RGB的顺序通常为BGR。

2. 对图像进行高斯滤波图像高斯滤波的实现可以用两个一维高斯核分别两次加权实现,也可以通过一个二维高斯核一次卷积实现。

1)高斯核实现上式为离散化的一维高斯函数,确定参数就可以得到一维核向量。

上式为离散化的二维高斯函数,确定参数就可以得到二维核向量。

在求得高斯核后,要对整个核进行归一化处理。

2)图像高斯滤波对图像进行高斯滤波,其实就是根据待滤波的像素点及其邻域点的灰度值按照一定的参数规则进行加权平均。

这样可以有效滤去理想图像中叠加的高频噪声。

通常滤波和边缘检测是矛盾的概念,抑制了噪声会使得图像边缘模糊,这会增加边缘定位的不确定性;而如果要提高边缘检测的灵敏度,同时对噪声也提高了灵敏度。

实际工程经验表明,高斯函数确定的核可以在抗噪声干扰和边缘检测精确定位之间提供较好的折衷方案。

3. 用一阶偏导的有限差分来计算梯度的幅值和方向关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y 方向上偏导数的两个矩阵。

常用的梯度算子有如下几种:1)Roberts算子上式为其x和y方向偏导数计算模板,可用数学公式表达其每个点的梯度幅值为:2)Sobel算子上式三个矩阵分别为该算子的x向卷积模板、y向卷积模板以及待处理点的邻域点标记矩阵,据此可用数学公式表达其每个点的梯度幅值为:3)Prewitt算子和Sobel算子原理一样,在此仅给出其卷积模板。

学习笔记-canny边缘检测

学习笔记-canny边缘检测

学习笔记-canny边缘检测Canny边缘检测声明:阅读本⽂需要了解线性代数⾥⾯的点乘(图像卷积的原理),⾼等数学⾥的⼆元函数的梯度,极⼤值定义,了解概率论⾥的⼆维⾼斯分布1.canny边缘检测原理和简介2.实现步骤3.总结⼀、 Canny边缘检测算法的发展历史 边缘检测是从图像中提取有⽤的结构信息的⼀种技术,如果学过信息论就会知道,⼀⾯充满花纹的墙要⽐⼀⾯⽩墙的信息量⼤很多,没学过也没关系,直观上也能理解:充满花纹的图像要⽐单⾊图像信息更丰富。

为什么要检测边缘?因为我们需要计算机⾃动的提取图像的底层(纹理等)或者⾼层(时间地点⼈物等)的信息,边缘可以说是最直观、最容易发现的⼀种信息了。

Canny提出了⼀个对于边缘检测算法的评价标准,包括:1) 以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。

2) 检测到的边缘应精确定位在真实边缘的中⼼。

3) 图像中给定的边缘应只被标记⼀次,并且在可能的情况下,图像的噪声不应产⽣假的边缘。

简单来说就是,检测算法要做到:边缘要全,位置要准,抵抗噪声的能⼒要强。

接下来介绍最经典的canny边缘检测算法,很多边缘检测算法都是在此基础上进⾏改进的,学习它有利于⼀通百通。

⼆、实现步骤 step1:⾼斯平滑滤波没有哪张图⽚是没有噪声的。

————鲁迅 滤波是为了去除噪声,选⽤⾼斯滤波也是因为在众多噪声滤波器中,⾼斯表现最好(表现怎么定义的?最好好到什么程度?),你也可以试试其他滤波器如均值滤波、中值滤波等等。

⼀个⼤⼩为(2k+1)x(2k+1)的⾼斯滤波器核(核⼀般都是奇数尺⼨的)的⽣成⽅程式由下式给出:‘ 下⾯是⼀个sigma = 1.4,尺⼨为3x3的⾼斯卷积核的例⼦,注意矩阵求和值为1(归⼀化): 举个例⼦:若图像中⼀个3x3的窗⼝为A,要滤波的像素点为e,则经过⾼斯滤波之后,像素点e的亮度值为: 其中*为卷积符号,sum表⽰矩阵中所有元素相加求和,简单说,就是滤波后的每个像素值=其原像素中⼼值及其相邻像素的加权求和。

简述canny边缘检测方法

简述canny边缘检测方法

简述canny边缘检测方法
Canny边缘检测方法是一种广泛应用于数字图像处理领域的算法,用于检测图像中的边缘。

它是由John Canny在1986年开发的,是一种基于多级梯度计算和非极大值抑制(Non-Maximum Suppression)的方法。

该算法的主要步骤包括以下几个步骤:
1. 高斯滤波:对图像进行高斯平滑滤波以去除噪声,同时模糊图像,使边缘在进行梯度计算时更平滑。

2. 梯度计算:使用Sobel等算子计算图像中每个像素点的梯度、方向和大小,从而找到边缘的位置。

3. 非极大值抑制:将检测到的梯度方向沿垂直方向上进行“压缩”,将每个像素点的位置更新为其在梯度方向上的最大值处。

4. 双重阈值:对非极大值抑制后的图像进行二值化操作,设定一个高阈值和低阈值,比较每个像素点的梯度大小是否高于高阈值或低于低阈值。

高于高阈值的点被标记为强边缘,低于低阈值的点被标记为背景,介于高低阈值之间的点被标记为弱边缘。

5. 边缘跟踪:将弱边缘与强边缘连接起来,最终得到连续的边缘。

Canny边缘检测方法具有较高的精度和鲁棒性,广泛应用于计算机视觉、机器视觉、物体检测等领域。

canny算子边缘检测原理

canny算子边缘检测原理

canny算子边缘检测原理
Canny算子是一种常用的边缘检测算法,其原理如下:
1. 高斯滤波:首先对图像进行高斯滤波,以减少噪声的影响。

高斯滤波是利用高斯函数对图像进行平滑操作,可以抑制高频噪声。

2. 计算梯度幅值和方向:对平滑后的图像进行梯度计算,通过计算像素点的梯度幅值和方向,可以找到图像中的边缘。

常用的梯度算子包括Sobel算子和Prewitt算子。

3. 非极大值抑制:在梯度图像中,对于每个像素点,通过比较其梯度方向上的两个相邻像素点的梯度幅值,将梯度幅值取最大值的点保留下来,其他点置为0。

这样可以剔除非边缘的像素。

4. 双阈值处理:将梯度幅值图像中的像素分为强边缘、弱边缘和非边缘三类。

设置两个阈值:高阈值和低阈值。

如果某个像素的梯度幅值大于高阈值,则将其标记为强边缘。

如果某个像素的梯度幅值小于低阈值,则将其剔除。

对于梯度幅值介于低阈值和高阈值之间的像素,如果其与某个强边缘像素相连,则将其标记为强边缘,否则将其标记为弱边缘。

5. 边缘连接:通过将强边缘和与其相连的弱边缘进行连接,找到完整的边缘。

这里通常使用8连通或4连通算法来判断两个像素是否相连。

通过以上步骤,Canny算子可以得到图像中的边缘信息,并且相对其他算法能够更好地抑制噪声和保持边缘的连续性。

Canny 边缘检测算法

Canny 边缘检测算法

Canny 边缘检测算法【OpenCV】Canny 边缘检测分类:【OpenCV】2012-08-08 10:17 490人阅读评论(10) 收藏举报Canny 边缘检测算法1986年,JOHN CANNY 提出一个很好的边缘检测算法,被称为Canny编边缘检测器[1]。

Canny边缘检测根据对信噪比与定位乘积进行测度,得到最优化逼近算子,也就是Canny算子。

类似与LoG边缘检测方法,也属于先平滑后求导数的方法。

使用Canny边缘检测器,图象边缘检测必须满足两个条件:能有效地抑制噪声;必须尽量精确确定边缘的位置。

算法大致流程:1、求图像与高斯平滑滤波器卷积:2、使用一阶有限差分计算偏导数的两个阵列P与Q:3、幅值和方位角:4、非极大值抑制(NMS ):细化幅值图像中的屋脊带,即只保留幅值局部变化最大的点。

将梯度角的变化范围减小到圆周的四个扇区之一,方向角和幅值分别为:非极大值抑制通过抑制梯度线上所有非屋脊峰值的幅值来细化M[i,j],中的梯度幅值屋脊.这一算法首先将梯度角θ[i,j]的变化范围减小到圆周的四个扇区之一,如下图所示:5、取阈值将低于阈值的所有值赋零,得到图像的边缘阵列阈值τ取得太低->假边缘阈值τ取得太高->部分轮廊丢失选用两个阈值: 更有效的阈值方案.相关代码Canny算法实现:用高斯滤波器平滑图像(在调用Canny之前自己用blur平滑)用一阶偏导的有限差分来计算梯度的幅值和方向.对梯度幅值应用非极大值抑制.用双阈值算法检测和连接边缘.[cpp] view plaincopyprint?void cv::Canny( InputArray _src, OutputArray _dst,double low_thresh, double high_thresh,int aperture_size, bool L2gradient ){Mat src = _src.getMat();CV_Assert( src.depth() == CV_8U );_dst.create(src.size(), CV_8U);Mat dst = _dst.getMat();if (!L2gradient && (aperture_size &CV_CANNY_L2_GRADIENT) ==CV_CANNY_L2_GRADIENT){//backward compatibilityaperture_size &= ~CV_CANNY_L2_GRADIENT;L2gradient = true;}if ((aperture_size & 1) == 0 || (aperture_size != -1 && (aperture_size 7)))CV_Error(CV_StsBadFlag, "");#ifdef HA VE_TEGRA_OPTIMIZATIONif (tegra::canny(src, dst, low_thresh, high_thresh, aperture_size, L2gradient))return;#endifconst int cn = src.channels();cv::Mat dx(src.rows, src.cols, CV_16SC(cn));cv::Mat dy(src.rows, src.cols, CV_16SC(cn));cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0,cv::BORDER_REPLICATE);cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE);if (low_thresh > high_thresh)std::swap(low_thresh, high_thresh);if (L2gradient){low_thresh = std::min(32767.0, low_thresh);high_thresh = std::min(32767.0, high_thresh);if (low_thresh > 0) low_thresh *= low_thresh;if (high_thresh > 0) high_thresh *= high_thresh;}int low = cvFloor(low_thresh);int high = cvFloor(high_thresh);ptrdiff_t mapstep = src.cols + 2;cv::AutoBuffer buffer((src.cols+2)*(src.rows+2) + cn * mapstep * 3 * sizeof(int));int* mag_buf[3];mag_buf[0] = (int*)(uchar*)buffer;mag_buf[1] = mag_buf[0] + mapstep*cn;mag_buf[2] = mag_buf[1] + mapstep*cn;memset(mag_buf[0], 0, /* cn* */mapstep*sizeof(int));uchar* map = (uchar*)(mag_buf[2] + mapstep*cn); memset(map, 1, mapstep);memset(map + mapstep*(src.rows + 1), 1, mapstep);int maxsize = std::max(1std::vector stack(maxsize);uchar **stack_top = &stack[0];uchar **stack_bottom = &stack[0];/* sector numbers(Top-Left Origin)1 2 3* * ** * *0*******0* * ** * *3 2 1*/#define CANNY_PUSH(d) *(d) = uchar(2), *stack_top++ = (d)#define CANNY_POP(d) (d) = *--stack_top// calculate magnitude and angle of gradient, performnon-maxima supression.// fill the map with one of the following values:// 0 - the pixel might belong to an edge// 1 - the pixel can not belong to an edge// 2 - the pixel does belong to an edgefor (int i = 0; i{int* _norm = mag_buf[(i > 0) + 1] + 1;if (i{short* _dx = dx.ptrshort>(i);short* _dy = dy.ptrshort>(i);if (!L2gradient){for (int j = 0; j_norm[j] = std::abs(int(_dx[j])) + std::abs(int(_dy[j])); }else{for (int j = 0; j_norm[j] = int(_dx[j])*_dx[j] + int(_dy[j])*_dy[j];}if (cn > 1){for(int j = 0, jn = 0; j{int maxIdx = jn;for(int k = 1; kif(_norm[jn + k] > _norm[maxIdx]) maxIdx = jn + k;_norm[j] = _norm[maxIdx];_dx[j] = _dx[maxIdx];_dy[j] = _dy[maxIdx];}}_norm[-1] = _norm[src.cols] = 0;}elsememset(_norm-1, 0, /* cn* */mapstep*sizeof(int));// at the very beginning we do not have a complete ring// buffer of 3 magnitude rows for non-maxima suppressionif (i == 0)continue;uchar* _map = map + mapstep*i + 1;_map[-1] = _map[src.cols] = 1;int* _mag = mag_buf[1] + 1; // take the central row ptrdiff_t magstep1 = mag_buf[2] - mag_buf[1]; ptrdiff_t magstep2 = mag_buf[0] - mag_buf[1];const short* _x = dx.ptrshort>(i-1);const short* _y = dy.ptrshort>(i-1);if ((stack_top - stack_bottom) + src.cols > maxsize) {int sz = (int)(stack_top - stack_bottom);maxsize = maxsize * 3/2;stack.resize(maxsize);stack_bottom = &stack[0];stack_top = stack_bottom + sz;}int prev_flag = 0;for (int j = 0; j{#define CANNY_SHIFT 15const int TG22 =(int)(0.4142135623730950488016887242097*(1 int m = _mag[j];if (m > low){int xs = _x[j];int ys = _y[j];int x = std::abs(xs);int y = std::abs(ys)int tg22x = x * TG22;if (y{if (m > _mag[j-1] && m >= _mag[j+1]) goto __ocv_canny_push;}else{int tg67x = tg22x + (xif (y > tg67x){if (m > _mag[j+magstep2] && m >= _mag[j+magstep1]) goto __ocv_canny_push;}else{int s = (xs ^ ys)if (m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s]) goto __ocv_canny_push;}}}prev_flag = 0;_map[j] = uchar(1);continue;__ocv_canny_push:if (!prev_flag && m > high && _map[j-mapstep] != 2){CANNY_PUSH(_map + j);prev_flag = 1;}else_map[j] = 0;}// scroll the ring buffer_mag = mag_buf[0];mag_buf[0] = mag_buf[1];mag_buf[1] = mag_buf[2];mag_buf[2] = _mag;}// now track the edges (hysteresis thresholding)while (stack_top > stack_bottom){uchar* m;if ((stack_top - stack_bottom) + 8 > maxsize) {int sz = (int)(stack_top - stack_bottom); maxsize = maxsize * 3/2;stack.resize(maxsize);stack_bottom = &stack[0];stack_top = stack_bottom + sz;}CANNY_POP(m);if (!m[-1]) CANNY_PUSH(m - 1);if (!m[1]) CANNY_PUSH(m + 1);if (!m[-mapstep-1]) CANNY_PUSH(m - mapstep - 1); if (!m[-mapstep]) CANNY_PUSH(m - mapstep);if (!m[-mapstep+1]) CANNY_PUSH(m - mapstep + 1); if (!m[mapstep-1]) CANNY_PUSH(m + mapstep - 1); if (!m[mapstep]) CANNY_PUSH(m + mapstep);if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1); }// the final pass, form the final imageconst uchar* pmap = map + mapstep + 1;uchar* pdst = dst.ptr();for (int i = 0; i{for (int j = 0; jpdst[j] = (uchar)-(pmap[j] >> 1);}} void cv::Canny( InputArray _src, OutputArray _dst,double low_thresh, double high_thresh,int aperture_size, bool L2gradient ){Mat src = _src.getMat();CV_Assert( src.depth() == CV_8U );_dst.create(src.size(), CV_8U);Mat dst = _dst.getMat(); if (!L2gradient && (aperture_size & CV_CANNY_L2_GRADIENT) ==CV_CANNY_L2_GRADIENT){//backward compatibilityaperture_size &= ~CV_CANNY_L2_GRADIENT;L2gradient = true;} if ((aperture_size & 1) == 0 || (aperture_size != -1 && (aperture_size 7)))CV_Error(CV_StsBadFlag, "");#ifdefHA VE_TEGRA_OPTIMIZATIONif (tegra::canny(src, dst, low_thresh, high_thresh, aperture_size, L2gradient))return;#endif const int cn = src.channels();cv::Mat dx(src.rows, src.cols, CV_16SC(cn));cv::Mat dy(src.rows, src.cols, CV_16SC(cn));cv::Sobel(src, dx, CV_16S, 1, 0, aperture_size, 1, 0,cv::BORDER_REPLICATE);cv::Sobel(src, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE); if (low_thresh > high_thresh) std::swap(low_thresh, high_thresh); if(L2gradient){low_thresh = std::min(32767.0, low_thresh);high_thresh = std::min(32767.0, high_thresh);if (low_thresh > 0) low_thresh *= low_thresh;if (high_thresh > 0) high_thresh *= high_thresh;}int low = cvFloor(low_thresh);int high = cvFloor(high_thresh); ptrdiff_t mapstep =src.cols + 2;cv::AutoBuffer buffer((src.cols+2)*(src.rows+2) + cn * mapstep * 3 * sizeof(int));int* mag_buf[3];mag_buf[0] = (int*)(uchar*)buffer;mag_buf[1] = mag_buf[0] + mapstep*cn;mag_buf[2] = mag_buf[1] + mapstep*cn;memset(mag_buf[0], 0, /* cn* */mapstep*sizeof(int)); uchar* map = (uchar*)(mag_buf[2] + mapstep*cn);memset(map, 1, mapstep);memset(map + mapstep*(src.rows + 1), 1, mapstep);int maxsize = std::max(1 << 10, src.cols * src.rows / 10);std::vector stack(maxsize);uchar **stack_top = &stack[0];uchar **stack_bottom = &stack[0]; /* sector numbers (Top-Left Origin) 1 2 3* * ** * *0*******0* * ** * *3 2 1*/ #define CANNY_PUSH(d) *(d) = uchar(2),*stack_top++ = (d)#define CANNY_POP(d) (d) = *--stack_top // calculate magnitude and angle of gradient, perform non-maxima supression.// fill the map with one of the following values:// 0 - the pixel might belong to an edge// 1 - the pixel can not belong to an edge// 2 - the pixel does belong to an edgefor (int i = 0; i 0) + 1] + 1;if (i < src.rows){short* _dx = dx.ptr(i);short* _dy = dy.ptr(i); if(!L2gradient){for (int j = 0; j < src.cols*cn; j++)_norm[j] = std::abs(int(_dx[j])) + std::abs(int(_dy[j]));}else{for (int j = 0; j 1){for(int j = 0, jn = 0; j _norm[maxIdx]) maxIdx = jn + k;_norm[j] = _norm[maxIdx];_dx[j] = _dx[maxIdx];_dy[j] = _dy[maxIdx];}}_norm[-1] = _norm[src.cols] = 0;}elsememset(_norm-1, 0, /* cn**/mapstep*sizeof(int));// at the very beginning we do not have a complete ring// buffer of 3 magnitude rows for non-maxima suppressionif (i == 0)continue; uchar* _map = map +mapstep*i + 1;_map[-1] = _map[src.cols] = 1; int* _mag = mag_buf[1] + 1; // take the central rowptrdiff_t magstep1 = mag_buf[2] - mag_buf[1];ptrdiff_t magstep2 = mag_buf[0] - mag_buf[1]; const short* _x = dx.ptr(i-1);const short* _y = dy.ptr(i-1); if ((stack_top - stack_bottom) + src.cols > maxsize){int sz = (int)(stack_top - stack_bottom);maxsize = maxsize * 3/2;stack.resize(maxsize);stack_bottom = &stack[0];stack_top = stack_bottom + sz;} int prev_flag = 0;for (int j = 0; j < src.cols; j++){#define CANNY_SHIFT 15const int TG22 =(int)(0.4142135623730950488016887242097*(1 low){int xs = _x[j];int ys = _y[j];int x = std::abs(xs);int y = std::abs(ys) = _mag[j+1]) goto __ocv_canny_push;}else{int tg67x = tg22x + (x =_mag[j+magstep1]) goto __ocv_canny_push;}else{int s = (xs ^ ys)_mag[j+magstep1+s]) goto __ocv_canny_push;}}}prev_flag = 0;_map[j] = uchar(1);continue;__ocv_canny_push:if (!prev_flag && m > high &&_map[j-mapstep] != 2){CANNY_PUSH(_map + j);prev_flag = 1;}else_map[j] = 0;} // scroll the ring buffer_mag = mag_buf[0];mag_buf[0] = mag_buf[1];mag_buf[1] = mag_buf[2];mag_buf[2] = _mag;} // now track the edges (hysteresis thresholding) while (stack_top > stack_bottom){uchar* m;if ((stack_top - stack_bottom) + 8 > maxsize){int sz = (int)(stack_top - stack_bottom);maxsize = maxsize * 3/2;stack.resize(maxsize);stack_bottom = &stack[0];stack_top = stack_bottom + sz;} CANNY_POP(m); if (!m[-1]) CANNY_PUSH(m - 1);if (!m[1]) CANNY_PUSH(m + 1);if (!m[-mapstep-1]) CANNY_PUSH(m - mapstep - 1);if (!m[-mapstep]) CANNY_PUSH(m - mapstep);if (!m[-mapstep+1]) CANNY_PUSH(m - mapstep + 1);if (!m[mapstep-1]) CANNY_PUSH(m + mapstep - 1);if (!m[mapstep]) CANNY_PUSH(m + mapstep);if (!m[mapstep+1]) CANNY_PUSH(m + mapstep + 1);} // the final pass, form the final imageconst uchar* pmap = map + mapstep + 1;uchar* pdst = dst.ptr();for (int i = 0; i 1);}}Canny() 调用接口(C++):[cpp] viewplaincopyprint?void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2,int apertureSize=3, bool L2gradient=false ) voidCanny(InputArray image, OutputArray edges, double threshold1, double threshold2,int apertureSize=3, boolL2gradient=false )实践示例[cpp] viewplaincopyprint?Mat src, src_gray;Mat dst, detected_edges;int edgeThresh = 1;int lowThreshold;int const max_lowThreshold = 100;int ratio = 3;int kernel_size = 3;char* window_name = "Edge Map";void CannyThreshold(int, void*){/// Reduce noise with a kernel 3x3blur( src_gray, detected_edges, Size(3,3) );/// Canny detectorCanny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );dst = Scalar::all(0);src.copyTo( dst, detected_edges);imshow( window_name, dst );}int main( ){src = imread( "images\\happycat.png" );if( !src.data ){ return -1; }dst.create( src.size(), src.type() );cvtColor( src, src_gray, CV_BGR2GRAY ); namedWindow( window_name,CV_WINDOW_AUTOSIZE );createTrackbar( "Min Threshold:", window_name,&lowThreshold, max_lowThreshold, CannyThreshold ); CannyThreshold(0, 0);waitKey(0);return 0;} Mat src, src_gray;Mat dst, detected_edges;int edgeThresh = 1;int lowThreshold;int const max_lowThreshold = 100;int ratio = 3;int kernel_size = 3;char* window_name = "Edge Map";void CannyThreshold(int, void*){/// Reduce noise with a kernel 3x3blur( src_gray, detected_edges, Size(3,3) );/// Canny detectorCanny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );dst = Scalar::all(0);src.copyTo( dst, detected_edges);imshow( window_name, dst );}int main( ){src = imread( "images\\happycat.png" );if( !src.data ){ return -1; }dst.create( src.size(), src.type() );cvtColor( src, src_gray, CV_BGR2GRAY ); namedWindow( window_name,CV_WINDOW_AUTOSIZE );createTrackbar( "Min Threshold:", window_name,&lowThreshold, max_lowThreshold, CannyThreshold ); CannyThreshold(0, 0);waitKey(0);return 0;} 原图:边缘检测效果图:(从左到右lowThread分别为0、50、100)参考文献:[1] Canny. A Computational Approach to Edge Detection, IEEE Trans. on PatternAnalysis and Machine Intelligence, 8(6), pp. 679-698 (1986).转载请注明出处:/xiaowei_cqu/article/details/7839140资源下载:/detail/xiaowei_cqu/4483966。

canny算子

canny算子

经典图像边缘检测(综合法思想)——Canny算子John Canny于1986年提出Canny算子,它与Marr(LoG)边缘检测方法类似,也属于是先平滑后求导数的方法。

John Canny研究了最优边缘检测方法所需的特性,给出了评价边缘检测性能优劣的三个指标:l好的信噪比,即将非边缘点判定为边缘点的概率要低,将边缘点判为非边缘点的概率要低;l高的定位性能,即检测出的边缘点要尽可能在实际边缘的中心;l对单一边缘仅有唯一响应,即单个边缘产生多个响应的概率要低,并且虚假响应边缘应该得到最大抑制。

用一句话说,就是希望在提高对景物边缘的敏感性的同时,可以抑制噪声的方法才是好的边缘提取方法。

Canny算子求边缘点具体算法步骤如下:1. 用高斯滤波器平滑图像.2. 用一阶偏导有限差分计算梯度幅值和方向.3. 对梯度幅值进行非极大值抑制.4. 用双阈值算法检测和连接边缘.步1. 图像与高斯平滑滤波器卷积:步3. 对梯度幅值进行非极大值抑制(non_maxima suppression,NMS):仅仅得到全局的梯度并不足以确定边缘,因此为确定边缘,必须保留局部梯度最大的点,而抑制非极大值。

解决方法:利用梯度的方向:步4. 用双阈值算法检测和连接边缘:对非极大值抑制图像作用两个阈值th1和th2,两者关系th1=0.4th2。

我们把梯度值小于th1的像素的灰度值设为0,得到图像1。

然后把梯度值小于th2的像素的灰度值设为0,得到图像2。

由于图像2的阈值较高,去除大部分噪音,但同时也损失了有用的边缘信息。

而图像1的阈值较低,保留了较多的信息,我们可以以图像2为基础,以图像1为补充来连结图像的边缘。

链接边缘的具体步骤如下:对图像2进行扫描,当遇到一个非零灰度的像素p(x,y)时,跟踪以p(x,y)为开始点的轮廓线,直到轮廓线的终点q(x,y)。

考察图像1中与图像2中q(x,y)点位置对应的点s(x,y)的8邻近区域。

Canny_算子边缘检测解析

Canny_算子边缘检测解析
w w


w
w
f '2 ( x)dx
③ 边缘响应次数最少:要保证只有一个像素响应,检 测算子的脉冲响应导数的零交叉点平均距离D(f)满 1 足 2
'2 f ( x ) dx ' D(f ) f ( x ) dx
| G(x) f ( x)dx | | G ' (x) f ' ( x)dx | J(f) SNR(f) Location w w w w 2 f ( x)dx f '2 ( x)dx
边缘检测算法
传统的边缘检测算子:Sobel算子,Prewitt算子,Roberts 算子,Krich算子等,大部分处理的效果都不很好,实际处理 中不太实用,而Canny算子检测的性能较好,常被作为其他实 验的标准来参考。Canny算子是John Canny在1986年发表的论 文中首次提出的一种边缘检测算法,当时弥补了其他算法的不 太好的缺点,因此Canny算子被认为是边缘检测领域较好的算 法,并一直被引用,近几年来,随着研究的深入,性能更加完 善的改性型的Canny算子也层出不穷,例如自适应Canny算子等。
阈值th1----图像1
遍历图像2
非零轮廓 在图像1中找 线终点 对应非零点
阈值th2----图像2
补充到图像2中作为 新的起点继续遍历
SNR(f) | G(x ) f ( x)dx |
w w


w
w
f 2 ( x)dx
G(-x)表示图像边函数 f(x)滤波器函数 表示噪声的均方差
Canny算子详细原理
②高的定位精度:Location越大越好
Location | G ' ( x ) f ' ( x)dx |

基于改进Canny算法的边缘检测技术应用及发展趋势

基于改进Canny算法的边缘检测技术应用及发展趋势

基于改进Canny算法的边缘检测技术应用及发展趋势边缘检测是计算机视觉中一项重要的任务,它在图像处理、物体识别和特征提取等领域具有广泛的应用。

Canny算法是一种经典的边缘检测算法,但其在保持边缘连续性和准确度方面存在一定的限制。

本文将讨论基于改进Canny算法的边缘检测技术的应用及发展趋势。

一、改进Canny算法的原理及优点改进Canny算法是对传统Canny算法的改进和优化。

传统Canny算法主要包括以下几个步骤:高斯滤波、计算梯度强度和方向、非极大值抑制和双阈值处理。

改进Canny算法通过对这些步骤的改进,提高了边缘检测的准确性和性能。

改进Canny算法的优点主要包括:1.减少了边缘的丢失:传统Canny算法在高斯滤波和非极大值抑制过程中可能会造成边缘的模糊或丢失。

改进Canny算法通过优化参数和增加边缘细化步骤,减少了这种问题的发生。

2.增强了边缘的连续性:传统Canny算法在边缘连接方面存在一定的局限性。

改进Canny算法通过引入更复杂的边缘连接策略,提高了边缘连接的准确性和连续性。

3.降低了计算复杂度:改进Canny算法通过优化计算步骤和算法结构,降低了算法的复杂度。

这使得改进Canny算法更适合于实时边缘检测应用。

二、基于改进Canny算法的边缘检测技术应用基于改进Canny算法的边缘检测技术在各个领域都有广泛的应用。

以下是几个典型的应用案例:1.医学图像处理:医学图像中的边缘信息对于病理分析和疾病诊断具有重要意义。

基于改进Canny算法的边缘检测技术可以有效地提取出图像中的器官边缘、病变区域等信息,为医生提供更准确的诊断依据。

2.工业质检:在工业生产中,边缘检测用于检测产品的质量以及表面缺陷。

基于改进Canny算法的边缘检测技术可以对产品进行快速而准确的边缘检测,从而提高产品质量控制的效率和准确性。

3.智能交通系统:基于改进Canny算法的边缘检测技术可以应用于智能交通系统中的车辆检测和行人识别。

Canny算法详解

Canny算法详解

Canny算法详解边缘提取以及边缘增强是不少图像处理软件都具有的基本功能,它的增强效果很明显,在用于识别的应用中,图像边缘也是非常重要的特征之一。

图像边缘保留了原始图像中相当重要的部分信息,而又使得总的数据量减小了很多,这正符合特征提取的要求。

在以后要谈到的霍夫变换(检测图像中的几何形状)中,边缘提取就是前提步骤。

这里我们只考虑灰度图像,用于图像识别的边缘提取比起仅仅用于视觉效果增强的边缘提取要复杂一些。

要给图像的边缘下一个定义还挺困难的,从人的直观感受来说,边缘对应于物体的边界。

图像上灰度变化剧烈的区域比较符合这个要求,我们一般会以这个特征来提取图像的边缘。

但在遇到包含纹理的图像上,这有点问题,比如说,图像中的人穿了黑白格子的衣服,我们往往不希望提取出来的边缘包括衣服上的方格。

但这个比较困难,涉及到纹理图像的处理等方法。

好了,既然边缘提取是要保留图像的灰度变化剧烈的区域,从数学上,最直观的方法就是微分(对于数字图像来说就是差分),在信号处理的角度来看,也可以说是用高通滤波器,即保留高频信号。

这是最关键的一步,在此之前有时需要对输入图像进行消除噪声的处理。

用于图像识别的边缘提取往往需要输出的边缘是二值图像,即只有黑白两个灰度的图像,其中一个灰度代表边缘,另一个代表背景。

此外,还需要把边缘细化成只有一个像素的宽度。

总的说来边缘提取的步骤如下:1,去噪声2,微分运算3,2值化处理4,细化第二步是关键,有不少书把第二步就直接称为边缘提取。

实现它的算法也有很多,一般的图像处理教科书上都会介绍好几种,如拉普拉兹算子,索贝尔算子,罗伯特算子等等。

这些都是模板运算,首先定义一个模板,模板的大小以3*3的较常见,也有2*2,5*5或更大尺寸的。

运算时,把模板中心对应到图像的每一个像素位置,然后按照模板对应的公式对中心像素和它周围的像素进行数学运算,算出的结果作为输出图像对应像素点的值。

需要说明的是,模板运算是图像的一种处理手段--邻域处理,有许多图像增强效果都可以采用模板运算实现,如平滑效果,中值滤波(一种消除噪声的方法),油画效果,图像的凹凸效果等等。

canny边缘检测公式

canny边缘检测公式

Canny边缘检测是一种常用的图像处理算法,用于检测图像中的边缘。

以下是Canny边缘检测的基本公式和步骤:
高斯滤波:
首先对输入图像应用高斯滤波器,以减少噪声的影响。

高斯滤波器的公式如下:
G(x, y) = (1 / (2 * π* σ^2)) * exp(-(x^2 + y^2) / (2 * σ^2))
计算梯度幅值和方向:
在经过高斯滤波后的图像上,使用Sobel算子计算每个像素的梯度幅值和方向。

梯度幅值的计算公式如下:
G = sqrt(Gx^2 + Gy^2)
其中,Gx和Gy分别是在x和y方向上的梯度。

非极大值抑制:
对梯度幅值图像进行非极大值抑制,保留局部梯度幅值的峰值点,抑制非峰值点。

这样可以细化边缘。

双阈值处理:
将非极大值抑制后的图像进行阈值处理,将梯度幅值划分为强边缘、弱边缘和非边缘三个阈值区间。

根据强边缘和弱边缘之间的连通性关系,确定最终的边缘。

Canny边缘检测算法的具体参数设置和阈值选择可以根据具体应用进行调整。

这些公式和步骤提供了Canny边缘检测的基本原理和流程,但实际应用中可能还会有其他优化和改进的技术。

Canny-算子边缘检测原理解析

Canny-算子边缘检测原理解析
SNR(f) | G(x ) f ( x)dx |
w w


w
w
f 2 ( x)dx
G(-x)表示图像边函数 f(x)滤波器函数 表示噪声的均方差
Canny算子详细原理
②高的定位精度:Location越大越好
Location | G ' ( x ) f ' ( x)dx |
• 链接边缘的具体步骤如下:
• 对图像2进行扫描,当遇到一个非零灰度的像素p(x,y)时, 跟踪以p(x,y)为开始点的轮廓线,直到轮廓线的终点q(x,y)。 • 考察图像1中与图像2中q(x,y)点位置对应的点s(x,y)的8邻 近区域。如果在s(x,y)点的8邻近区域中有非零像素s(x,y) 存在,则将其包括到图像2中,作为r(x,y)点。从r(x,y)开 始,重复第一步,直到我们在图像1和图像2中都无法继续为 止。 • 当完成对包含p(x,y)的轮廓线的连结之后,将这条轮廓线标 记为已经访问。回到第一步,寻找下一条轮廓线。重复第一 步、第二步、第三步,直到图像2中找不到新轮廓线为止。 • 至此,完成canny算子的边缘检测。
w w
w
w
通过以上算式得出算子的近似实现:边缘点位于图 像被高斯平滑后的梯度值的极大值点。
算法过程
原始图像 A(x,yBy)
极大 值抑 制非
图像边缘
双阈值检测 连结边缘
初步得到 边缘点
详细算法过程
I. 高斯函数
x2 y2 n 1 G(x, y) exp | |, m , n表示 2 2 2 x m y m 高斯滤波器窗口大小
边缘检测算法
传统的边缘检测算子:Sobel算子,Prewitt算子,Roberts 算子,Krich算子等,大部分处理的效果都不很好,实际处理 中不太实用,而Canny算子检测的性能较好,常被作为其他实 验的标准来参考。Canny算子是John Canny在1986年发表的论 文中首次提出的一种边缘检测算法,当时弥补了其他算法的不 太好的缺点,因此Canny算子被认为是边缘检测领域较好的算 法,并一直被引用,近几年来,随着研究的深入,性能更加完 善的改性型的Canny算子也层出不穷,例如自适应Canny算子等。

一种边缘检测方法

一种边缘检测方法

一种边缘检测方法边缘检测是计算机视觉领域中的重要问题之一,其目标是找到图像中物体之间的边界或边缘。

边缘检测在图像处理、目标识别和图像分割等领域具有广泛的应用。

本文介绍一种常用的边缘检测方法——Canny边缘检测算法,并说明其原理、优缺点以及应用。

1. Canny边缘检测算法原理Canny边缘检测算法由John Canny于1986年提出,被广泛认为是目前最好的边缘检测算法之一。

该算法主要包括以下几个步骤:1.1 噪声消除首先对原始图像进行高斯滤波,以减小图像中的噪声。

高斯滤波能够降低高频噪声的干扰,并保留边缘的信息。

1.2 计算梯度对滤波后的图像使用一阶偏导数进行梯度计算。

常用的方法是使用Sobel算子对图像进行卷积操作,得到水平和垂直方向的梯度。

1.3 非极大值抑制在计算梯度的基础上,对图像中的每个像素点进行非极大值抑制。

该步骤的目的是将像素点的灰度值转化为边缘强度,并滤除非局部最大值点。

1.4 双阈值检测将抑制后的图像进行双阈值检测,将梯度值划分为强边缘、弱边缘和非边缘三个分类。

通常情况下,我们选择一个较高的阈值作为强边缘的阈值,一个较低的阈值作为弱边缘的阈值。

只有当某个像素点的梯度值大于高阈值时,才认为其为强边缘;当某个像素点的梯度值介于高阈值和低阈值之间时,才认为其为弱边缘。

1.5 边缘连接在完成强边缘和弱边缘的分类后,通过连接强边缘像素点和与之相邻的弱边缘像素点来形成完整的边缘。

2. Canny边缘检测的优缺点Canny边缘检测算法具有以下几个优点:- 优秀的检测能力:Canny算法能够准确地检测出图像中的边缘,而且对边缘的方向和位置具有精确的描述能力。

- 良好的鲁棒性:Canny算法对噪声具有较强的抵抗能力,能够在对噪声有一定容忍度的情况下进行边缘检测。

- 多阈值灵活设置:Canny算法通过设置不同的阈值,能够提供灵活的边缘检测结果。

然而,Canny边缘检测算法也存在一些缺点:- 计算复杂度较高:由于Canny算法中包含多个步骤,需要进行多次图像处理和计算,因此其计算复杂度较高,不适用于对速度要求较高的应用场景。

canny边缘检测分析毕业论文.doc

canny边缘检测分析毕业论文.doc

Canny边缘检测分析毕业论文目录引言 (1)第一章图像分割与边缘检测 (2)1.1图像分割简介 (2)1.2图像分割定义 (2)1.3图像分割基本原理 (3)第二章基于边界的分割——边缘检测 (6)2.1边缘的类型 (6)2.2边缘的类型 (6)2.3边缘的判定 (7)第三章常见边缘检测算法的研究与分析 (9)3.1边缘检测过程概述 (9)3.2典型一阶边缘检测算子 (9)3.2.1梯度算子 (10)3.2.2 Roberts边缘算子 (10)3.2.3 Sobel算子 (11)3.2.4 Prewitt算子 (13)3.3 典型二阶边缘检测算子 (14)I3.3.1 Laplacian算子 (14)3.3.2 LOG算子 (16)3.4 各边缘检测算子的仿真结果分析 (18)第四章Canny边缘检测算子 (20)4.1 Canny边缘检测基本原理: (20)4.2 Canny边缘算子评价指标: (20)4.2.1 Canny提出检测三准则【5】 (20)4.2.2边缘检测滤波器对性能指标的影响【10】 (22)4.2.3 尺度对性能指标的影响【10】 (23)4.3 Canny边缘检测流程 (24)4.4 Canny边缘检测仿真结果及分析 (28)第五章Canny算子改进 (29)5.1对传统Canny算法局限性分析 (29)5.2滤波改进 (30)5.3阈值改进——自适应的阈值 (31)5.3.1最大熵原算法过程 (31)5.3.2最大熵算法的改进 (32)5.4改进的Canny算法的仿真实验 (33)第六章本实验结果及展望 (36)6.1 本算法的实验结果 (36)II6.2实验结果分析 (39)6.3 展望 (39)结论 (40)致谢 (41)参考文献 (42)III引言20世纪20年代,图像处理首次应用于改善伦敦和纽约之间海底电缆发送的图片质量,20世纪60年代中期,随电子计算机的发展得到普遍应用。

图像处理之Canny边缘检测

图像处理之Canny边缘检测

图像处理之Canny边缘检测图像处理之Canny 边缘检测一:历史Canny边缘检测算法是1986年有John F. Canny开发出来一种基于图像梯度计算的边缘检测算法,同时Canny本人对计算图像边缘提取学科的发展也是做出了很多的贡献。

尽管至今已经许多年过去,但是该算法仍然是图像边缘检测方法经典算法之一。

二:Canny边缘检测算法经典的Canny边缘检测算法通常都是从高斯模糊开始,到基于双阈值实现边缘连接结束。

但是在实际工程应用中,考虑到输入图像都是彩色图像,最终边缘连接之后的图像要二值化输出显示,所以完整的Canny边缘检测算法实现步骤如下:1. 彩色图像转换为灰度图像2. 对图像进行高斯模糊3. 计算图像梯度,根据梯度计算图像边缘幅值与角度4. 非最大信号压制处理(边缘细化)5. 双阈值边缘连接处理6. 二值化图像输出结果三:各步详解与代码实现1. 彩色图像转灰度图像根据彩色图像RGB转灰度公式:gray = R * 0.299 + G * 0.587 + B * 0.114将彩色图像中每个RGB像素转为灰度值的代码如下:[java] view plain copy print?&lt;spanstyle="font-size:18px;"&gt;int gray = (int) (0.299 * tr + 0.587 * tg + 0.114 * tb);&lt;/span&gt; 2. 对图像进行高斯模糊图像高斯模糊时,首先要根据输入参数确定高斯方差与窗口大小,这里我设置默认方差值窗口大小为16x16,根据这两个参数生成高斯卷积核算子的代码如下:[java] view plain copy print?&lt;span style="font-size:18px;"&gt; float kernel[][] = newfloat[gaussianKernelWidth][gaussianKernelWidth];for(int x=0; x&lt;gaussianKernelWidth; x++){ for(int y=0; y&lt;gaussianKernelWidth; y++) { kernel[x][y] = gaussian(x, y, gaussianKernelRadius); } }&lt;/spa n&gt; 获取了高斯卷积算子之后,我们就可以对图像高斯卷积模糊,关于高斯图像模糊更详细的解释可以参见这里:/jia20003/article/details/7234741实现图像高斯卷积模糊的代码如下:[java] view plain copyprint?&lt;span style="font-size:18px;"&gt;// 高斯模糊-灰度图像int krr = (int)gaussianKernelRadius; for (int row = 0; row &lt; height; row++) { for (int col = 0; col &lt; width; col++) { index = row * width + col;double weightSum = 0.0; double redSum = 0;for(int subRow=-krr; subRow&lt;=krr; subRow++){ int nrow = row + subRow;if(nrow &gt;= height || nrow &lt; 0){ nrow = 0; }for(int subCol=-krr; subCol&lt;=krr; subCol++){ int ncol = col + subCol;if(ncol &gt;= width || ncol &lt;=0){ ncol = 0; }int index2 = nrow * width + ncol; int tr1 = (inPixels[index2] &gt;&gt; 16) &amp; 0xff;redSum += tr1*kernel[subRow+krr][subCol+krr];weightSum +=kernel[subRow+krr][subCol+krr]; }} int gray = (int)(redSum / weightSum);outPixels[index] = gray; } }&lt;/span&gt; 3.计算图像X方向与Y方向梯度,根据梯度计算图像边缘幅值与角度大小高斯模糊的目的主要为了整体降低图像噪声,目的是为了更准确计算图像梯度及边缘幅值。

Canny边缘检测

Canny边缘检测

Canny 边缘检测本文档尝试解答如下问题:, 使用OpenCV函数 Canny 检测边缘. 原理?1. Canny 是 John F. Canny 于 1986边缘检测算法年开发出来的一个多级边缘检测算法,也被很多人认为是边缘检测的最优算法, 最优边缘检测的三个主要评价标准是:, 低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。

, 高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。

, 最小响应: 图像中的边缘只能标识一次。

步骤? 1. 消除噪声。

使用高斯平滑滤波器卷积降噪。

下面显示了一个的高斯内核示例:2. 计算梯度幅值和方向。

此处,按照Sobel滤波器的步骤:a. 运用一对卷积阵列 (分别作用于和方向):b. 使用下列公式计算梯度幅值和方向:梯度方向近似到四个可能角度之一(一般 0,45, 90, 135)3. 非极大值抑制。

这一步排除非边缘像素,仅仅保留了一些细线条(候选边缘)。

4. : 最后一步,Canny 使用了滞后阈值,滞后滞后阈值阈值需要两个阈值(高阈值和低阈值):a. 如果某一像素位置的幅值超过高阈值, 该像素被保留为边缘像素。

b. 如果某一像素位置的幅值小于阈值, 该低像素被排除。

c. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于高阈值的像素时被保留。

Canny 推荐的高:低阈值比在 2:1 到3:1之间。

5. 想要了解更多细节,你可以参考任何你喜欢的计算机视觉书籍。

源码?1. 本程序做什么?o 要求使用者输入一个数字,设置 Canny EdgeDetector 的低阈值 (通过trackbar)o 使用 Canny 边缘检测产生一个 mask (白线代表边缘,黑色代表背景)。

o 使用 mask 作为掩码显示原图像。

2. 本教程的源码如下,你也可以从这里下载#include "opencv2/imgproc/imgproc.hpp"#include "opencv2/highgui/highgui.hpp"#include <stdlib.h>#include <stdio.h>using namespace cv;/// 全局变量Mat src, src_gray;Mat dst, detected_edges;int edgeThresh = 1;int lowThreshold;int const max_lowThreshold = 100;int ratio = 3;int kernel_size = 3;char* window_name = "Edge Map";/*** @函数 CannyThreshold* @简介: trackbar 交互回调 - Canny阈值输入比例1:3*/void CannyThreshold(int, void*){/// 使用 3x3内核降噪blur( src_gray, detected_edges, Size(3,3) );/// 运行Canny算子Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );/// 使用 Canny算子输出边缘作为掩码显示原图像dst = Scalar::all(0);src.copyTo( dst, detected_edges);imshow( window_name, dst );}/** @函数 main */int main( int argc, char** argv ){/// 装载图像src = imread( argv[1] );if( !src.data ){ return -1; }/// 创建与src同类型和大小的矩阵(dst)dst.create( src.size(), src.type() );/// 原图像转换为灰度图像cvtColor( src, src_gray, CV_BGR2GRAY );/// 创建显示窗口namedWindow( window_name, CV_WINDOW_AUTOSIZE );/// 创建trackbarcreateTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );/// 显示图像CannyThreshold(0, 0);/// 等待用户反应waitKey(0);return 0;}解释?1. 创建程序中要用到的变量:2. Mat src, src_gray;3. Mat dst, detected_edges;4.5. int edgeThresh = 1;6. int lowThreshold;7. int const max_lowThreshold = 100;8. int ratio = 3;9. int kernel_size = 3;10. char* window_name = "Edge Map";11.12. 注意:13.14. a. 我们首先设定高:低阈值比为 3:1 (通过变量 *ratio* )15. b. 设定内核尺寸为 :math:`3` (Canny函数内部调用Sobel操作)c. 将低阈值的上限设定为 :math:`100`. 16. 装载原图像:17. /// 装载图像18. src = imread( argv[1] );19.20. if( !src.data )21. { return -1; }22. 创建与 src 同类型和大小的矩阵(dst)23. dst.create( src.size(), src.type() );24. 将输入图像转换到灰度空间 (使用函数 cvtColor): 25. cvtColor( src, src_gray, CV_BGR2GRAY );26. 创建显示窗口27. namedWindow( window_name,CV_WINDOW_AUTOSIZE );28. 创建trackbar,来获取用户交互输入的低阈值:29. createTrackbar( "Min Threshold:",window_name, &lowThreshold, max_lowThreshold,CannyThreshold );注意:a. 通过trackbar控制的变量为 lowThreshold ,上限为 max_lowThreshold (我们已经设定为100)b. 每次用户通过trackbar产生变动,回调函数CannyThreshold 被调用.30. 让我们一步一步的来观察 CannyThreshold 函数:a. 首先, 使用 3x3的内核平滑图像:b. blur( src_gray, detected_edges,Size(3,3) );c. 其次,运用 Canny 寻找边缘:d. Canny( detected_edges, detected_edges,lowThreshold, lowThreshold*ratio,kernel_size );输入参数:, detected_edges: 原灰度图像, detected_edges: 输出图像 (支持原地计算,可为输入图像), lowThreshold: 用户通过 trackbar设定的值。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

一.Canny边缘检测算法原理
JohnCanny于1986年提出Canny算子,属于是先平滑后求导数的方法。

其处理过程大体上分为下面四部分。

1. 对原始图像进行灰度化
Canny算法通常处理的图像为灰度图,因此如果获取的是彩色图像,那首先就得进行灰度化。

对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。

以RGB格式的彩图为例,通常灰度化采用的方法主要有:
方法1:Gray=(R+G+B)/3;
方法2:Gray=0.299R+0.587G+0.114B;(这种参数考虑到了人眼的生理特点)
至于其他格式的彩色图像,可以根据相应的转换关系转为RGB然后再进行灰度化;在编程时要注意图像格式中RGB的顺序通常为BGR。

2. 对图像进行高斯滤波
图像高斯滤波的实现可以用两个一维高斯核分别两次加权实现,也可以通过一个二维高斯核一次卷积实现。

1)高斯核实现
上式为离散化的一维高斯函数,确定参数就可以得到一维核向量。

上式为离散化的二维高斯函数,确定参数就可以得到二维核向量。

在求得高斯核后,要对整个核进行归一化处理。

2)图像高斯滤波
对图像进行高斯滤波,其实就是根据待滤波的像素点及其邻域点的灰度值按照一定的参数规则进行加权平均。

这样可以有效滤去理想图像中叠加的高频噪声。

通常滤波和边缘检测是矛盾的概念,抑制了噪声会使得图像边缘模糊,这会增加边缘定位的不确定性;而如果要提高边缘检测的灵敏度,同时对噪声也提高了灵敏度。

实际工程经验表明,高斯函数确定的核可以在抗噪声干扰和边缘检测精确定位之间提供较好的折衷方案。

3. 用一阶偏导的有限差分来计算梯度的幅值和方向
关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y 方向上偏导数的两个矩阵。

常用的梯度算子有如下几种:
1)Roberts算子
上式为其x和y方向偏导数计算模板,可用数学公式表达其每个点的梯度幅值为:
2)Sobel算子
上式三个矩阵分别为该算子的x向卷积模板、y向卷积模板以及待处理点的邻域点标记矩阵,据此可用数学公式表达其每个点的梯度幅值为:
3)Prewitt算子
和Sobel算子原理一样,在此仅给出其卷积模板。

4)Canny算法所采用的方法
在这里实现的Canny算法中所采用的卷积算子比较简单,表达如下:
其x向、y向的一阶偏导数矩阵,梯度幅值以及梯度方向的数学表达式为:
求出这几个矩阵后,就可以进行下一步的检测过程。

4. 对梯度幅值进行非极大值抑制
图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。

在Canny算法中,非极大值抑制是进行边缘检测的重要步骤,通俗意义上是指寻找像素点局部最大值,将非极大值点所对应的灰度值置为0,这样可以剔除掉一大部分非边缘的点。

图1 非极大值抑制原理
根据图1 可知,要进行非极大值抑制,就首先要确定像素点C的灰度值在其8值邻域内是否为最大。

图1中蓝色的线条方向为C点的梯度方向,这样就可以确定其局部的最大值肯定分布在这条线上,也即出了C点外,梯度方向的交点dTmp1和dTmp2这两个点的值也可能会是局部最大值。

因此,判断C点灰度与这两个点灰度大小即可判断C点是否为
其邻域内的局部最大灰度点。

如果经过判断,C点灰度值小于这两个点中的任一个,那就
说明C点不是局部极大值,那么则可以排除C点为边缘。

这就是非极大值抑制的工作原理。

非最大抑制是这样一个问题:“当前的梯度值在梯度方向上是一个局部最大值” 所以,要
把当前位置的梯度值与梯度方向上两侧的梯度值进行比较;且梯度方向垂直于边缘方向。

但实际上,我们只能得到C点邻域的8个点的值,而dTmp1和dTmp2并不在其中,
要得到这两个值就需要对该两个点两端的已知灰度进行线性插值,也即根据图1中的g1
和g2对dTmp1进行插值,根据g3和g4对dTmp2进行插值,这要用到其梯度方向,这
是上文Canny算法中要求解梯度方向矩阵Thita的原因。

完成非极大值抑制后,会得到一个二值图像,非边缘的点灰度值均为0,可能为边缘
的局部灰度极大值点可设置其灰度为128。

这样得到的图像可能包含了很多由噪声及其他
原因造成的假边缘。

因此还需要进一步的处理。

5. 用双阈值算法检测和连接边缘
Canny算法中减少假边缘数量的方法是采用双阈值法。

选择两个阈值,根据高阈值得
到一个边缘图像,这样一个图像含有很少的假边缘,但是由于阈值较高,产生的图像边缘
可能不闭合,为解决这样一个问题采用了另外一个低阈值。

在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域
点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。

二.Canny边缘检测算法实验结果
图2原图图3 高斯模糊后
图4 sobel边缘检测后图5 非极大抑制后
图6 上阈值120,下阈值100检测结果。

相关文档
最新文档