如何利用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中使用scapy模拟数据包实现arp攻击、dns放大攻击例子
Oct 23 Python
Python内置函数之filter map reduce介绍
Nov 30 Python
python中split方法用法分析
Apr 17 Python
用Python编写一个基于终端的实现翻译的脚本
Apr 24 Python
Python 调用Java实例详解
Jun 02 Python
python批量导入数据进Elasticsearch的实例
May 30 Python
Django 路由控制的实现代码
Nov 08 Python
应用OpenCV和Python进行SIFT算法的实现详解
Aug 21 Python
python编程进阶之异常处理用法实例分析
Feb 21 Python
将python字符串转化成长表达式的函数eval实例
May 11 Python
在keras下实现多个模型的融合方式
May 23 Python
Django中使用Celery的方法步骤
Dec 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
用PHP和ACCESS写聊天室(七)
2006/10/09 PHP
php邮件发送的两种方式
2020/04/28 PHP
Yii实现显示静态页的方法
2016/04/25 PHP
apache php mysql开发环境安装教程
2016/07/28 PHP
thinkPHP5.0框架引入Traits功能实例分析
2017/03/18 PHP
关于 Laravel Redis 多个进程同时取队列问题详解
2017/12/25 PHP
PHP数字金额转换成中文大写显示
2019/01/05 PHP
jQuery中判断一个元素是否为另一个元素的子元素(或者其本身)
2012/03/21 Javascript
jquery ajax请求实例深入解析
2012/11/26 Javascript
浅析JavaScript中两种类型的全局对象/函数
2013/12/05 Javascript
深入解析JavaScript中的数字对象与字符串对象
2015/10/21 Javascript
jqGrid 学习笔记整理——进阶篇(一 )
2016/04/17 Javascript
jQuery mobile的header和footer在点击屏幕的时候消失的解决办法
2016/07/01 Javascript
jQuery 获取页面li数组并删除不在数组中的key
2016/08/02 Javascript
JavaScript仿网易选项卡制作代码
2016/10/06 Javascript
Java与JavaScript中判断两字符串是否相等的区别
2017/03/13 Javascript
AngularJS 最常用的八种功能(基础知识)
2017/06/26 Javascript
jQuery+Ajax请求本地数据加载商品列表页并跳转详情页的实现方法
2017/07/12 jQuery
想用好React的你必须要知道的一些事情
2017/07/24 Javascript
ES6知识点整理之数组解构和字符串解构的应用示例
2019/04/17 Javascript
Django调用百度AI接口实现人脸注册登录代码实例
2020/04/23 Python
使用Python3 poplib模块删除服务器多天前的邮件实现代码
2020/04/24 Python
Python中如何添加自定义模块
2020/06/09 Python
意大利和国际奢侈品牌购物网站:Suitnegozi.com
2021/01/15 全球购物
军训心得体会
2013/12/31 职场文书
监理资料员岗位职责
2014/01/03 职场文书
实习生岗位职责
2014/04/12 职场文书
小学生操行评语大全
2014/04/22 职场文书
心理健康日活动总结
2014/05/08 职场文书
村主任“四风”问题个人整改措施
2014/10/04 职场文书
离婚协议书怎样才有法律效力
2014/10/10 职场文书
2016春季幼儿园小班开学寄语
2015/12/03 职场文书
幼儿教师继续教育培训心得体会
2016/01/19 职场文书
幼儿园心得体会范文
2016/01/21 职场文书
Python OpenCV实现传统图片格式与base64转换
2021/06/13 Python
使用CSS实现六边形的图片效果
2022/08/05 HTML / CSS