今天的想法是用Canny边缘检测算法,建立一种可以勾画出图像上任何物体的边缘的算法。

首先,我们来描述一下Canny边缘检测器:Canny边缘检测算子是一种边缘检测算子,它采用多级算法检测图像中广泛的边缘。它是由John F. Canny在1986年开发的。Canny还提出了边缘检测的计算理论,解释了该技术的工作原理。

Canny边缘检测算法由5个步骤组成:降噪;

梯度计算;

非最大抑制;

双阈值;

滞后边缘跟踪。

应用这些步骤后,您将能够获得以下结果:

左侧的原始图像 - 右侧的已处理图像

最后值得一提的是,该算法是基于灰度图像的。因此,在进行上述步骤之前,首先要将图像转换为灰度。

降噪

由于场景背后涉及的数学主要基于导数(参见步骤2:梯度计算),边缘检测结果对图像噪声高度敏感。

消除图像噪声的一种方法是使用高斯模糊平滑图像。为此,图像卷积技术应用高斯核(3x3, 5x5, 7x7等)。核大小取决于预期的模糊效果。基本上,核越小,模糊就越不明显。在我们的例子中,我们将使用一个5×5的高斯核函数。

大小为(2k+1)×(2k+1)的高斯滤波核的方程为:

高斯滤波器核方程

用于生成Gaussian 5x5内核的Python代码:

import numpy as npdef gaussian_kernel(size, sigma=1): size = int(size) // 2 x, y = np.mgrid[-size:size+1, -size:size+1] normal = 1 / (2.0 * np.pi * sigma**2) g = np.exp(-((x**2 + y**2) / (2.0*sigma**2))) * normal return g

应用高斯模糊后,我们得到以下结果:

原始图像(左) - 带有高斯滤波器的模糊图像(sigma = 1.4,核大小为5x5)

梯度计算

梯度计算步骤通过使用边缘检测算子计算图像的梯度来检测边缘强度和方向。

边缘对应于像素强度的变化。要检测它,最简单的方法是应用filters,在两个方向上突出这种强度变化:水平(x)和垂直(y)

当平滑图像时,计算导数Ix和Iy。它可以通过分别用Sobel kernelsKx和Ky分别卷积I来实现:

Sobel filters用于两个方向(水平和垂直)

然后,梯度的幅度G和斜率θ计算如下:

梯度强度和边缘方向

下面是Sobel滤镜应用于图像的方法,以及如何获得强度和边缘方向矩阵,Python代码如下:from scipy import ndimagedef sobel_filters(img): Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], np.float32) Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]], np.float32) Ix = ndimage.filters.convolve(img, Kx) Iy = ndimage.filters.convolve(img, Ky) G = np.hypot(Ix, Iy) G = G / G.max() * 255 theta = np.arctan2(Iy, Ix) return (G, theta)

模糊图像(左) - 梯度强度(右)

结果几乎是预期的,我们可以看到,一些边缘是厚的,另一些是薄的。非最大抑制步骤将有助于我们减轻厚的。

此外,梯度强度水平在0到255之间,这是不均匀的。最终结果的边缘应具有相同的强度(即白色像素= 255)。

非最大抑制

理想情况下,最终的图像应该有细边。因此,我们必须执行非最大抑制以使边缘变细。

原理很简单:算法遍历梯度强度矩阵上的所有点,并找到边缘方向上具有最大值的像素。

让我们举一个简单的例子:

上图左上角的红色框表示被处理的梯度强度矩阵的一个强度像素。对应的边缘方向由橙色箭头表示,其角度为-pi弧度(+/- 180度)。

聚焦左上角的红色方块像素

边缘方向是橙色虚线(从左到右水平)。该算法的目的是检查在相同方向上的像素是否比被处理的像素强度高或低。在上面的例子中,正在处理像素(i,j),相同方向上的像素用蓝色(i, j-1)和(i, j+1)高亮显示。如果这两个像素中的一个比正在处理的那个更强,那么只保留更强的那个。像素(i, j-1)似乎更强,因为它是白色的(值255)。因此,当前像素(i, j)的强度值设置为0。如果边缘方向上没有具有更强值的像素,则保留当前像素的值。

现在让我们关注另一个例子:

在这种情况下,方向是橙色虚线对角线。因此,该方向上最强的像素是像素(i-1,j + 1)。

让我们总结一下。每个像素有2个主要标准(弧度的边缘方向和像素强度(0-255之间))。基于这些输入,非最大抑制步骤是:创建一个初始化为0的矩阵,该矩阵与原始梯度强度矩阵的大小相同;

根据角度矩阵的角度值识别边缘方向;

检查相同方向的像素是否具有比当前处理的像素更高的强度;

返回使用非最大抑制算法处理的图像。

Python代码如下:

def non_max_suppression(img, D): M, N = img.shape Z = np.zeros((M,N), dtype=np.int32) angle = D * 180. / np.pi angle[angle < 0] += 180 for i in range(1,M-1): for j in range(1,N-1): try: q = 255 r = 255 #angle 0 if (0 <= angle[i,j] < 22.5) or (157.5 <= angle[i,j] <= 180): q = img[i, j+1] r = img[i, j-1] #angle 45 elif (22.5 <= angle[i,j] < 67.5): q = img[i+1, j-1] r = img[i-1, j+1] #angle 90 elif (67.5 <= angle[i,j] < 112.5): q = img[i+1, j] r = img[i-1, j] #angle 135 elif (112.5 <= angle[i,j] < 157.5): q = img[i-1, j-1] r = img[i+1, j+1] if (img[i,j] >= q) and (img[i,j] >= r): Z[i,j] = img[i,j] else: Z[i,j] = 0 except IndexError as e: pass return Z

结果是相同的图像,但边缘更薄。然而,我们仍然可以注意到边缘亮度的一些变化:一些像素似乎比其他像素更亮,我们将尝试在最后两个步骤中弥补这一缺陷。

非最大抑制的结果

双阈值

双阈值步骤旨在识别3种像素:强,弱和不相关:强像素是指像素的强度如此之高,以至于我们确信它们有助于最终的边缘。

弱像素是具有不足以被视为强的强度值的像素,但是还不足以被认为与边缘检测不相关。

其他像素被认为与边缘无关。

现在你可以看到这两个阈值代表什么:高阈值用于识别强像素(强度高于高阈值)

低阈值用于识别不相关的像素(强度低于低阈值)

具有两个阈值之间的强度的所有像素被标记为弱,滞后机制(下一步骤)将帮助我们识别可被视为强的那些和被认为是不相关的那些。def threshold(img, lowThresholdRatio=0.05, highThresholdRatio=0.09): highThreshold = img.max() * highThresholdRatio; lowThreshold = highThreshold * lowThresholdRatio; M, N = img.shape res = np.zeros((M,N), dtype=np.int32) weak = np.int32(25) strong = np.int32(255) strong_i, strong_j = np.where(img >= highThreshold) zeros_i, zeros_j = np.where(img < lowThreshold) weak_i, weak_j = np.where((img <= highThreshold) & (img >= lowThreshold)) res[strong_i, strong_j] = strong res[weak_i, weak_j] = weak return (res, weak, strong)

此步骤的结果是只有2个像素强度值(强弱)的图像:

非最大抑制图像(左) - 阈值结果(右)

滞后边缘跟踪

根据阈值结果,当且仅当被处理像素周围至少有一个像素为强像素时,滞后由弱像素转换为强像素构成,如下所述:

def hysteresis(img, weak, strong=255): M, N = img.shape for i in range(1, M-1): for j in range(1, N-1): if (img[i,j] == weak): try: if ((img[i+1, j-1] == strong) or (img[i+1, j] == strong) or (img[i+1, j+1] == strong) or (img[i, j-1] == strong) or (img[i, j+1] == strong) or (img[i-1, j-1] == strong) or (img[i-1, j] == strong) or (img[i-1, j+1] == strong)): img[i, j] = strong else: img[i, j] = 0 except IndexError as e: pass return img

candy算子python_Python-计算机视觉中的Canny边缘检测方法相关推荐

  1. NNDL 实验六 卷积神经网络(1)卷积 边缘检测之传统边缘检测算子和基于pytorch的Canny边缘检测

    文章目录 卷积神经网络(Convolutional Neural Network,CNN) 5.1 卷积 5.1.1 二维卷积运算 5.1.2 二维卷积算子 5.1.3 二维卷积的参数量和计算量 5. ...

  2. Opencv中的Canny边缘检测

    文章目录 1.Canny引入 2.Canny算法实现步骤 (1)去噪 (2)计算梯度和梯度方向 (3)过滤非最大值 (4)使用阈值检测边缘 3.Canny函数实现 4.实例代码测试 (1)图片测试代码 ...

  3. Canny边缘检测方法中的非极大抑制

    什么是非极大抑制 在目标检测中,通常会使用各种各样的方法来让计算机找到目标的所在位置,然而,计算机的输出往往并不是单一的,也就是说,一个目标可能会输出多个结果(如下图所示),这些结果有好有坏,因此就需 ...

  4. 摄影测量(计算机视觉)中的三角化方法

    提到三角化大家都十分熟悉,在CV 领域中,由像点计算物点的过程称为三角化,但在摄影测量领域,其称作为前方交会.值得注意的是单张影像是无法恢复像点的三维坐标,至少需要两张影像才能得到像素点的真实坐标(这 ...

  5. 计算机视觉中常见图像扰动方法的Pytorch实现

    和另一种说法数据增强(data augmentation)十分类似.主要区别在于,数据增强是训练时进行的,用于提升模型的鲁棒程度:而这里说的扰动指测试时进行的,用于评估已训练模型的鲁棒程度,因此并不需 ...

  6. 计算机视觉中Canny算子详解

    文章目录 前言 一.Canny的实现步骤 二.具体实现 1.高斯平滑滤波 2.计算梯度大小和方向 3.非极大抑制 4.双阈值(Double Thresholding)和滞后边界跟踪 总结 前言 Can ...

  7. Canny边缘检测算法

    1. 写在前面 最近在做边缘检测方面的一些工作,在网络上也找了很多有用的资料,感谢那些积极分享知识的先辈们,自己在理解Canny边缘检测算法的过程中也走了一些弯路,在编程实现的过程中,也遇到了一个让我 ...

  8. 计算机视觉基础-图像处理(边缘检测)cpp+python

    6.1 简介 6.1.1 什么是边缘? 边缘是图像强度函数快速变化的地方 6.1.2 如何检测边缘? 为了检测边缘,我们需要检测图像中的不连续性,可以使用导数来检测不连续性. 如上图所示,上图的第一幅 ...

  9. 计算机视觉基础-图像处理 Task06 边缘检测

    文章目录 Sobel算子 Canny边缘检测 1.高斯滤波 2.计算梯度强度和方向 3.非极大值抑制 4.用双阈值算法检测和连接边缘 函数实现 Sobel算子 Canny算法 Sobel算子 我们可以 ...

最新文章

  1. svg: svg预定义的形状
  2. 神策数据迁移服务正式上线,以服务产品化迎战云迁移难题
  3. 机器学习小抄.pdf(像背托福单词一样理解机器学习)
  4. DataTable操作相关实例
  5. Netty工作笔记0038---Netty模型--通俗版
  6. 零散知识点总结(1) Gradle 使用配置总结
  7. 运用C#在采集时进行自动验证登录[转]
  8. Spring整合MyBatis之SqlSession对象的产生
  9. javaweb项目遇到错误
  10. 经典简约风格教师求职简历免费word模板
  11. 机器学习实战——决策树
  12. Ubuntu终端快捷复制粘贴
  13. 互联网周刊:草根创业选择题
  14. 手把手教你用FineBI做数据可视化
  15. Latex 各种处理论文操作-插图、插表格
  16. canvas中的橡皮檫
  17. mysql连接三个表
  18. 我叫MongoDb,不懂我的看完我的故事您就入门啦!
  19. 占星家眼中的十二星座--处女座
  20. A morphable model for the synthesis of 3D faces 学习笔记(未完)

热门文章

  1. Intel汇编语言程序设计学习-第三章 汇编语言基础-中
  2. hdu3074 线段树求区间乘积(单点更新)
  3. system , DOS 命令
  4. 【Linux 内核】调度器 ② ( sched_class 调度类结构体源码 | 源码路径 linux-5.6.18\kernel\sched\sched.h )
  5. 【Android 逆向】函数拦截实例 ( ② 插桩操作 | 保存实际函数入口 6 字节数据 | 在插桩的函数入口写入跳转指令 | 构造拼接桩函数 )
  6. 【Java 虚拟机原理】Java 引用类型 ( 强引用 | 软引用 | 弱引用 | 虚引用 | 静态变量 )
  7. 【数字信号处理】数字信号处理简介 ( DSP 定义 | DSP 知识领域 | A/D 转换 )
  8. 【Netty】NIO 网络通信 SelectionKey 常用 API 简介
  9. 【C 语言】结构体相关 的 函数 指针 数组
  10. ECharts简单入门demo