如何利用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基于动态规划算法计算单词距离
Jul 25 Python
python 实现删除文件或文件夹实例详解
Dec 04 Python
python中实现延时回调普通函数示例代码
Sep 08 Python
python爬取微信公众号文章
Aug 31 Python
10个Python小技巧你值得拥有
Sep 29 Python
python实现二维插值的三维显示
Dec 17 Python
Python谱减法语音降噪实例
Dec 18 Python
Python 内置函数globals()和locals()对比详解
Dec 23 Python
Python动态声明变量赋值代码实例
Dec 30 Python
Python OpenCV读取中文路径图像的方法
Jul 02 Python
利用python汇总统计多张Excel
Sep 22 Python
python tkinter实现定时关机
Apr 21 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
极典R601SW收音机
2021/03/02 无线电
discuz7 phpMysql操作类
2009/06/21 PHP
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
2011/12/14 PHP
ThinkPHP3.1新特性之多层MVC的支持
2014/06/19 PHP
php+xml编程之SimpleXML的应用实例
2015/01/24 PHP
PHP实现抓取Google IP并自动修改hosts文件
2015/02/12 PHP
jQuery获取json后使用zy_tmpl生成下拉菜单
2015/03/27 PHP
PHP实现QQ、微信和支付宝三合一收款码实例代码
2018/02/19 PHP
JS获取html对象的几种方式介绍
2013/12/05 Javascript
js中window.open()的所有参数详细解析
2014/01/09 Javascript
js/jquery获取文本框输入焦点的方法
2014/03/04 Javascript
Bootstrap框架下下拉框select搜索功能
2020/03/26 Javascript
JavaScript里 ==与===区别详解
2016/08/16 Javascript
javascript深拷贝的原理与实现方法分析
2017/04/10 Javascript
Javascript别踩白块儿(钢琴块儿)小游戏实现代码
2017/07/20 Javascript
js实现登录注册框手机号和验证码校验(前端部分)
2017/09/28 Javascript
react-native中ListView组件点击跳转的方法示例
2017/09/30 Javascript
javascript+html5+css3自定义弹出窗口效果
2017/10/26 Javascript
Node.js API详解之 assert模块用法实例分析
2020/05/26 Javascript
Vue使用screenfull实现全屏效果
2020/09/17 Javascript
Python读写Redis数据库操作示例
2014/03/18 Python
Python优先队列实现方法示例
2017/09/21 Python
selenium+python 去除启动的黑色cmd窗口方法
2018/05/22 Python
wxPython电子表格功能wx.grid实例教程
2019/11/19 Python
python常用运维脚本实例小结
2020/02/14 Python
ubuntu16.04升级Python3.5到Python3.7的方法步骤
2020/08/20 Python
使用css3实现超炫的loading加载动画效果
2014/05/07 HTML / CSS
socket.io 和canvas 实现的共享画板功能
2019/05/22 HTML / CSS
中国跨境电子商务网站:NewFrog
2018/03/10 全球购物
庆国庆国旗下讲话稿2014
2014/09/21 职场文书
学生上课看漫画的检讨书
2014/09/26 职场文书
协议书范文
2015/01/27 职场文书
三十年同学聚会致辞
2015/07/28 职场文书
Python 数据可视化之Seaborn详解
2021/11/02 Python
mysql自增长id用完了该怎么办
2022/02/12 MySQL
Vue如何清空对象
2022/03/03 Vue.js