如何利用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正则匹配抓取豆瓣电影链接和评论代码分享
Dec 27 Python
python实现k均值算法示例(k均值聚类算法)
Mar 16 Python
基于Python对象引用、可变性和垃圾回收详解
Aug 21 Python
Python3中正则模块re.compile、re.match及re.search函数用法详解
Jun 11 Python
使用Python制作简单的小程序IP查看器功能
Apr 16 Python
Python静态类型检查新工具之pyright 使用指南
Apr 26 Python
python3实现mysql导出excel的方法
Jul 31 Python
python修改FTP服务器上的文件名
Sep 11 Python
Python实现自定义读写分离代码实例
Nov 16 Python
Python和Bash结合在一起的方法
Nov 13 Python
Pycharm配置autopep8实现流程解析
Nov 28 Python
Python中re模块的元字符使用小结
Apr 07 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
PHP4实际应用经验篇(6)
2006/10/09 PHP
Laravel中使用FormRequest进行表单验证方法及问题汇总
2016/06/19 PHP
php生成0~1随机小数的方法(必看)
2017/04/05 PHP
PHP实现文件上传功能实例代码
2017/05/18 PHP
Ajax请求PHP后台接口返回信息的实例代码
2018/08/21 PHP
CutePsWheel javascript libary 控制输入文本框为可使用滚轮控制的js库
2010/02/07 Javascript
js 固定悬浮效果实现思路代码
2013/08/02 Javascript
JavaScript模拟重力状态下抛物运动的方法
2015/03/03 Javascript
JS实现当前页居中分页效果的方法
2015/06/18 Javascript
jQuery实现获取隐藏div高度的方法示例
2017/02/09 Javascript
nodejs连接mysql数据库简单封装示例-mysql模块
2017/04/10 NodeJs
基于vue开发的在线付费课程应用过程
2018/01/25 Javascript
通过js实现压缩图片上传功能
2020/02/25 Javascript
vue-quill-editor 自定义工具栏和自定义图片上传路径操作
2020/08/03 Javascript
jQuery zTree如何改变指定节点文本样式
2020/10/16 jQuery
Ant design vue中的联动选择取消操作
2020/10/31 Javascript
[03:39]DOTA2英雄梦之声_第05期_幽鬼
2014/06/23 DOTA
一则python3的简单爬虫代码
2014/05/26 Python
跟老齐学Python之坑爹的字符编码
2014/09/28 Python
详解Django框架中用户的登录和退出的实现
2015/07/23 Python
使用Python进行AES加密和解密的示例代码
2018/02/02 Python
浅析Python与Mongodb数据库之间的操作方法
2019/07/01 Python
使用Pyinstaller转换.py文件为.exe可执行程序过程详解
2019/08/06 Python
浅谈tensorflow之内存暴涨问题
2020/02/05 Python
Python多进程编程multiprocessing代码实例
2020/03/12 Python
使用python把xmind转换成excel测试用例的实现代码
2020/10/12 Python
Speedo澳大利亚官网:全球领先游泳品牌
2018/02/04 全球购物
Zalando Lounge瑞士:时尚与生活方式购物俱乐部
2020/03/12 全球购物
Eclipse面试题
2014/03/22 面试题
什么是数据库锁?Oracle中都有哪些类型的锁?
2015/08/21 面试题
95%的面试官都会问到的50道Java线程题,附答案
2012/08/03 面试题
高三复习计划
2015/01/19 职场文书
2015年医德医风工作总结
2015/04/02 职场文书
2015年电话销售工作总结范文
2015/04/20 职场文书
入党积极分子培养人意见
2015/06/02 职场文书
源码安装apache脚本部署过程详解
2022/09/23 Servers