作者 | Sofiane Sahir

来源 | Towards data science

编辑 | 代码医生团队

今天的想法是使用Canny边缘检测算法构建一种能够绘制图片上任何对象边缘的算法。

首先描述Canny Edge Detector是什么:

Canny边缘检测器是一个边缘检测一个使用多级操作算法以检测宽范围图像中的边缘。它由John F. Canny于1986 年开发.Canny还制作了边缘检测的计算理论,解释了该技术的工作原理。(维基百科)

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

  1. 降噪;

  2. 梯度计算;

  3. 非最大抑制;

  4. 双重门槛;

  5. 滞后边缘跟踪。

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

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

最后一个重要的事情是,该算法基于灰度图片。因此先决条件是在遵循上述步骤之前将图像转换为灰度。

降噪

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

摆脱图像噪声的一种方法是使用高斯模糊来平滑它。为此图像卷积技术应用高斯核(3x3,5x5,7x7等...)。内核大小取决于预期的模糊效果。基本上内核最小,模糊不太明显。在例子中将使用5乘5高斯内核。

大小(2 k +1)×(2 k +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))) * normalreturn g

高斯核函数

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

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

梯度计算

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

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

当平滑图像时,计算导数Ix和Iy wrt x和y。它可以通过卷积来实现与索贝尔内核Kx的和肯塔基州分别为:

索贝尔滤波器用于两个方向(水平和垂直)

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

梯度强度和边缘方向

下面是Sobel滤镜应用于图像的方法,以及如何获得强度和边缘方向矩阵:

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) 如果边缘方向上没有像素具有更强的值,则保持当前像素的值。

现在关注另一个例子:

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

总结一下。每个像素有2个主要标准(弧度的边缘方向和像素强度(0-255之间))。基于这些输入,非最大抑制步骤是:

  • 创建一个初始化为0的矩阵,该矩阵与原始梯度强度矩阵的大小相同;

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

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

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

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] += 180for i in range(1,M-1):for j in range(1,N-1):try:
                q = 255
                r = 255#angle 0if (0 <= angle[i,j] 22.5) or (157.5 <= angle[i,j] <= 180):
                    q = img[i, j+1]
                    r = img[i, j-1]#angle 45elif (22.5 <= angle[i,j] 67.5):
                    q = img[i+1, j-1]
                    r = img[i-1, j+1]#angle 90elif (67.5 <= angle[i,j] 112.5):
                    q = img[i+1, j]
                    r = img[i-1, j]#angle 135elif (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] = 0except IndexError as e:passreturn 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 
    weak_i, weak_j = np.where((img <= highThreshold) & (img >= lowThreshold))
    res[strong_i, strong_j] = strong
    res[weak_i, weak_j] = weakreturn (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] = strongelse:
                        img[i, j] = 0except IndexError as e:passreturn img

滞后功能

滞后过程的结果

所有使用的代码都可以在以下Git存储库中找到

https://github.com/FienSoP/canny_edge_detector

参考书目

  • https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_canny/py_canny.html

  • https://homepages.inf.ed.ac.uk/rbf/HIPR2/canny.htm

  • http://justin-liang.com/tutorials/canny/

  • https://en.wikipedia.org/wiki/Canny_edge_detector

关于图书

《深度学习之TensorFlow:入门、原理与进阶实战》和《Python带我起飞——入门、进阶、商业实战》两本图书是代码医生团队精心编著的 AI入门与提高的精品图书。配套资源丰富:配套视频、QQ读者群、实例源码、 配套论坛:http://bbs.aianaconda.com   。更多请见:aianaconda.com

canny算子_在Python中逐步检测Canny边缘 计算机视觉相关推荐

  1. python股票趋势线_在Python中自动检测股票价格的高低并绘制趋势线

    我正在编写一个脚本来查找OHLC数据的高低.我一直在寻找高潮和低谷,这看起来有点管用.但这并不能给我一个很好的解决方案.以下是我目前为止编写的代码:df['Highs'] = (df['High'] ...

  2. python检测异常数据_用Python中从头开始的实现完整的异常检测算法

    利用概率的异常检测算法 异常检测可以作为离群分析的统计任务来对待. 但是,如果我们开发一个机器学习模型,它可以自动化,并且像往常一样可以节省大量时间. 有很多异常检测用例. 信用卡欺诈检测,故障机器检 ...

  3. python对象引用计数器_在Python中借助计数器对象对项目进行计数

    python对象引用计数器 前提 (The Premise) When we deal with data containers, such as tuples and lists, in Pytho ...

  4. python实现多语言语种识别_用Python进行语言检测

    最近正好碰到这个需求,总结一下用Python进行语言检测的方法. 1.用unicode编码检测 汉字.韩文.日文等都有对应的unicode字符集范围,只要用正则表达式匹配出来即可. 在判断的时候,往往 ...

  5. keras构建卷积神经网络_在python中使用tensorflow s keras api构建卷积神经网络的初学者指南...

    keras构建卷积神经网络 初学者的深度学习 (DEEP LEARNING FOR BEGINNERS) Welcome to Part 2 of the Neural Network series! ...

  6. python图片识别是否p过_用 Python 和 OpenCV 检测图片上的条形码

    用 Python 和 OpenCV 检测图片上的的条形码 这篇博文的目的是应用计算机视觉和图像处理技术,展示一个条形码检测的基本实现.我所实现的算法本质上基于StackOverflow 上的这个问题, ...

  7. python中变量和函数的区别_关于python中带下划线的变量和函数 的意义

    Python 的代码风格由 PEP 8 描述.这个文档描述了 Python 编程风格的方方面面.在遵守这个文档的条件下,不同程序员编写的 Python 代码可以保持最大程度的相似风格.这样就易于阅读, ...

  8. python 强制结束线程_在python中实现强制关闭线程的示例

    Python 如何强制关闭线程过去只可以用来回忆,别沉迷在阴影中,否则永远看不清前面的路. Python用sleep停止一个线程的运行,而不影响主线程的运行,案例代码如下: from threadin ...

  9. python中的序列类型数据结构元素的切片操作_浅析python中的分片与截断序列

    浅析python中的分片与截断序列 序列概念 在分片规则里list.tuple.str(字符串)都可以称为序列,都可以按规则进行切片操作 切片操作 注意切片的下标0代表顺序的第一个元素,-1代表倒序的 ...

最新文章

  1. 独家|OpenCV1.11 使用OpenCV制作一个低成本立体摄像机
  2. 高德地图和百度地图接口封装遇到的问题(一):重复添加实时路况图层后无法移除
  3. 云炬随笔20170201
  4. PHP权重算法-用于游戏根据权限来随机物品
  5. 如何利用SQL求取微信的共同好友数?
  6. linux编写运行shell程序,Linux的Shell编程运行Shell程序的方法有哪些呢?
  7. c语言栈的实现以及操作_python模拟栈的操作实现非递归方式的快速排序算法
  8. vod点歌系统服务器,冰河家庭VOD点歌系统
  9. 计算机主机有自带的声音吗,Windows XP 系统中没有音频设备,怎么办?
  10. 电视制式及声音制式的划分
  11. python下载b站视频_python怎么下载b站视频
  12. 中文可以声明java类吗_JSP中可以声明基本类型和结构类型变量,但不能申明类,类必须放在JavaBean中。...
  13. OA实施方法论的重要性
  14. 0CTF-2016-Web-piapiapia
  15. 用ctrl+鼠标滚动调节字体大小
  16. 著名画家赵准旺的名人评语
  17. 判定覆盖 与 条件覆盖 的区别
  18. MCE | 第二代 HIV-INSTI 的作用方式
  19. 什么是BGP服务器?
  20. android播放器删除视频教程,列表条目 的播放视频按钮、删除条目按钮的点击事件监听接口的实...

热门文章

  1. ActionBar -- Toolbar
  2. 基于JAVA+SpringBoot+Vue+Mybatis+MYSQL的在线音乐网站
  3. android java 调用栈_Android开发中打印方法调用栈
  4. python-实现栈结构
  5. FileReader图片上传并预览
  6. Codeforces 40 E. Number Table
  7. 四川方言说唱《管我锤子事》
  8. 【2010】asp.net GridView分页的实现
  9. mysql 外键和事务,MySQL-约束和事务
  10. erwin怎么设置编码_Word页码的设置方式