如何利用Python 进行边缘检测


Posted in Python onOctober 14, 2020

为何检测边缘?

我们首先应该了解的问题是:“为什么要费尽心思去做边缘检测?”除了它的效果很酷外,为什么边缘检测还是一种实用的技术?为了更好地解答这个问题,请仔细思考并对比下面的风车图片和它的“仅含边缘的图”:

如何利用Python 进行边缘检测

可以看到,左边的原始图像有着各种各样的色彩、阴影,而右边的“仅含边缘的图”是黑白的。如果有人问,哪一张图片需要更多的存储空间,你肯定会告诉他原始图像会占用更多空间。这就是边缘检测的意义:通过对图片进行边缘检测,丢弃大多数的细节,从而得到“更轻量化”的图片。

因此,在无须保存图像的所有复杂细节,而 “只关心图像的整体形状” 的情况下,边缘检测会非常有用。

如何进行边缘检测 —— 数学

在讨论代码实现前,让我们先快速浏览一下边缘检测背后的数学原理。作为人类,我们非常擅长识别图像中的“边”,那如何让计算机做到同样的事呢?

首先,假设有一张很简单的图片,在白色背景上有一个黑色的正方形:

如何利用Python 进行边缘检测

在这个例子中,由于处理的是黑白图片,因此我们可以考虑将图中的每个像素的值都用 0(黑色) 或 1(白色) 来表示。除了黑白图片,同样的理论也完全适用于彩色图像。

现在,我们需要判断上图中绿色高亮的像素是不是这个图像边缘的一部分。作为人类,我们当然可以认出它是图像的边缘;但如何让计算机利用相邻的像素来得到同样的结果呢?

我们以绿色高亮的像素为中心,设定一个 3 x 3 像素大小的小框,在图中以红色示意。接着,对这个小方框“应用”一个过滤器(filter):

如何利用Python 进行边缘检测

上图展示了我们将要“应用”的过滤器。乍一看上去很神秘,让我们仔细研究它做的事情:当我们说 “将过滤器应用于一小块局部像素块” 时,具体是指红色框中的每个像素与过滤器中与之位置对应的像素进行相乘。因此,红色框中左上角像素值为 1,而过滤器中左上角像素值为 -1,它们相乘得到 -1,这也就是结果图中左上角像素显示的值。结果图中的每个像素都是用这种方式得到的。

下一步是对过滤结果中的所有像素值求和,得到 -4。请注意,-4 其实是我们应用这个过滤器可获得的“最小”值(因为原始图片中的像素值只能在 0 到 1 之间)。因此,当获得 -4 这个最小值的时候,我们就能知道,对应的像素点是图像中正方形顶部竖直方向边缘的一部分。

为了更好地掌握这种变换,我们可以看看将此过滤器应用于图中正方形底边上的一个像素会发生什么:

如何利用Python 进行边缘检测

可以看到,我们得到了与前文相似的结果,相加之后得到的结果是 4,这是应用此过滤器能得到的最大值。因此,由于我们得到了 4 这一最大值,可以知道这个像素是图像中正方形底部竖直方向边缘的一部分。

为了把这些值映射到 0-1 的范围内,我们可以简单地给其加上 4 再除以 8,这样就能把 -4 映射成 0(黑色),把 4 映射成 1(白色)。因此,我们将这种过滤器称为纵向 Sobel 过滤器,可以用它轻松检测图像中垂直方向的边缘。

那如何检测水平方向的边缘呢?只需简单地将纵向过滤器进行转置(按照其数值矩阵的对角线进行翻转)就能得到一个新的过滤器,可以用于检测水平方向的边缘。

如果需要同时检测水平方向、垂直方向以及介于两者之间的边缘,我们可以把纵向过滤器得分和横向过滤器得分进行结合,这个步骤在后面的代码中将有所体现。

希望上文已经讲清楚了这些理论!下面看一看代码是如何实现的。

如何进行边缘检测 —— 代码

首先进行一些设置:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# 定义纵向过滤器
vertical_filter = [[-1,-2,-1], [0,0,0], [1,2,1]]
# 定义横向过滤器
horizontal_filter = [[-1,0,1], [-2,0,2], [-1,0,1]]

# 读取纸风车的示例图片“pinwheel.jpg”
img = plt.imread('pinwheel.jpg')

# 得到图片的维数
n,m,d = img.shape

# 初始化边缘图像
edges_img = img.copy()

你可以把代码中的“pinwheel.jpg”替换成其它你想要找出边缘的图片文件!需要确保此文件和代码在同一工作目录中。

接着编写边缘检测代码本身:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
# 定义纵向过滤器
vertical_filter = [[-1,-2,-1], [0,0,0], [1,2,1]]
# 定义横向过滤器
horizontal_filter = [[-1,0,1], [-2,0,2], [-1,0,1]]
# 读取纸风车的示例图片“pinwheel.jpg”
img = plt.imread('pinwheel.jpg')
# 得到图片的维数
n,m,d = img.shape
# 初始化边缘图像
edges_img = img.copy()
# 循环遍历图片的全部像素
for row in range(3, n-2):
for col in range(3, m-2):

# 在当前位置创建一个 3x3 的小方框
local_pixels = img[row-1:row+2, col-1:col+2, 0]

# 应用纵向过滤器
vertical_transformed_pixels = vertical_filter*local_pixels
# 计算纵向边缘得分
vertical_score = vertical_transformed_pixels.sum()/4

# 应用横向过滤器
horizontal_transformed_pixels = horizontal_filter*local_pixels
# 计算横向边缘得分
horizontal_score = horizontal_transformed_pixels.sum()/4

# 将纵向得分与横向得分结合,得到此像素总的边缘得分
edge_score = (vertical_score**2 + horizontal_score**2)**.5

# 将边缘得分插入边缘图像中
edges_img[row, col] = [edge_score]*3
# 对边缘图像中的得分值归一化,防止得分超出 0-1 的范围
edges_img = edges_img/edges_img.max()

有几点需要注意:

在图片的边界像素上,我们无法创建完整的 3 x 3 小方框,因此在图片的四周会有一个细边框。

既然是同时检测水平方向和垂直方向的边缘,我们可以直接将原始的纵向得分与横向得分分别除以 4(而不像前文描述的分别加 4 再除以 8)。这个改动无伤大雅,反而可以更好地突出图像的边缘。

将纵向得分与横向得分结合起来时,有可能会导致最终的边缘得分超出 0-1 的范围,因此最后还需要重新对最终得分进行标准化。

在更复杂的图片上运行上述代码:

如何利用Python 进行边缘检测

得到边缘检测的结果:

如何利用Python 进行边缘检测

以上就是本文的全部内容了!希望你了解到了一点新知识

到此这篇关于如何利用Python 进行边缘检测的文章就介绍到这了,更多相关python 边缘检测内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python使用fileinput模块实现逐行读取文件的方法
Apr 29 Python
python使用PyGame模块播放声音的方法
May 20 Python
浅析Python中的多条件排序实现
Jun 07 Python
Python3中的列表,元组,字典,字符串相关知识小结
Nov 10 Python
python3 enum模块的应用实例详解
Aug 12 Python
Python 如何提高元组的可读性
Aug 26 Python
python集合的创建、添加及删除操作示例
Oct 08 Python
Python 统计位数为偶数的数字代码详解
Mar 15 Python
浅谈python 类方法/静态方法
Sep 18 Python
如何使用python写截屏小工具
Sep 29 Python
python3.9.1环境安装的方法(图文)
Feb 02 Python
python绘制云雨图raincloud plot
Aug 05 Python
python从Oracle读取数据生成图表
Oct 14 #Python
python获取linux系统信息的三种方法
Oct 14 #Python
Python通过队列来实现进程间通信的示例
Oct 14 #Python
python利用xlsxwriter模块 操作 Excel
Oct 14 #Python
如何解决python多种版本冲突问题
Oct 13 #Python
Django配置Bootstrap, js实现过程详解
Oct 13 #Python
Python文件操作及内置函数flush原理解析
Oct 13 #Python
You might like
索尼ICF-SW100收音机评测
2021/03/02 无线电
PHP合并两个数组的两种方式的异同
2012/09/14 PHP
解析PHP对现有搜索引擎的调用
2013/06/25 PHP
大家都应该掌握的PHP关联数组使用技巧
2015/12/25 PHP
php中文乱码问题的终极解决方案汇总
2017/08/01 PHP
js导航菜单(自写)简单大方
2013/03/28 Javascript
jQuery客户端分页实例代码
2013/11/18 Javascript
使用JavaScript获取电池状态的方法
2014/05/03 Javascript
用JavaScript实现用一个DIV来包装文本元素节点
2014/09/09 Javascript
JavaScript中document.forms[0]与getElementByName区别
2015/01/21 Javascript
javascript先序遍历DOM树的方法
2016/02/27 Javascript
js中遍历Map对象的方法
2016/07/27 Javascript
vue.js 初体验之Chrome 插件开发实录
2017/05/13 Javascript
jQuery实现简单的回到顶部totop功能示例
2017/10/16 jQuery
Angular 如何使用第三方库的方法
2018/04/18 Javascript
加速vue组件渲染之性能优化
2020/04/09 Javascript
9个JavaScript日常开发小技巧
2020/10/06 Javascript
微信小程序自定义支持图片的弹窗
2020/12/21 Javascript
python双向链表实现实例代码
2013/11/21 Python
python直接访问私有属性的简单方法
2016/07/25 Python
python批量设置多个Excel文件页眉页脚的脚本
2018/03/14 Python
Python 类的特殊成员解析
2018/06/20 Python
Python 数据可视化pyecharts的使用详解
2019/06/26 Python
解决matplotlib.pyplot在Jupyter notebook中不显示图像问题
2020/04/22 Python
HTML5 声明兼容IE的写法
2011/05/16 HTML / CSS
理肤泉美国官网:La Roche-Posay
2018/01/17 全球购物
德国综合购物网站:OTTO
2018/11/13 全球购物
中学生个人自我评价
2014/02/06 职场文书
小学感恩教育活动总结
2014/07/07 职场文书
工作期间打牌检讨书范文
2014/11/20 职场文书
读《教育心理学》心得体会
2016/01/22 职场文书
2016干部作风整顿心得体会
2016/01/22 职场文书
初中物理教学反思
2016/02/19 职场文书
实用求职信模板范文
2019/05/13 职场文书
Python机器学习之逻辑回归
2021/05/11 Python
MySQL EXPLAIN输出列的详细解释
2021/05/12 MySQL