python计算机视觉2:图像边缘检测



我是一名初学者,如果你发现文中有错误,请留言告诉我,谢谢


 

如果需要检测到图像里面的边缘,首先我们需要知道边缘处具有什么特征。

对于一幅灰度图像来说,边缘两边的灰度值肯定不相同,这样我们才能分辨出哪里是边缘,哪里不是。

因此,如果我们需要检测一个灰度图像的边缘,我们需要找出哪里的灰度变化最大。显然,灰度变化越大,对比度越强,边缘就越明显。

那么问题来了,我们怎么知道哪里灰度变化大,哪里灰度变化小呢?


 导数,梯度,边缘信息 

在数学中,与变化率有关的就是导数

如果灰度图像的像素是连续的(实际不是),那么我们可以分别原图像G对x方向和y方向求导数

获得x方向的导数图像Gx和y方向的导数图像Gy。Gx和Gy分别隐含了x和y方向的灰度变化信息,也就隐含了边缘信息。

如果要在同一图像上包含两个方向的边缘信息,我们可以用到梯度。(梯度是一个向量)

原图像的梯度向量Gxy为(Gx,Gy),梯度向量的大小和方向可以用下面两个式子计算

角度值好像需要根据向量所在象限不同适当+pi或者-pi。

梯度向量大小就包含了x方向和y方向的边缘信息。


 

 图像导数 

实际上,图像矩阵是离散的。

连续函数求变化率用的是导数,而离散函数求变化率用的是差分。

差分的概念很容易理解,就是用相邻两个数的差来表示变化率。

下面公式是向后差分

x方向的差分:Gx(n,y) = G(n,y)-G(n-1,y)

y方向的差分:Gy(x,n) = G(x,n)-G(x,n-1)

 实际计算图像导数时,我们是通过原图像和一个算子进行卷积来完成的(这种方法是求图像的近似导数)。

最简单的求图像导数的算子是 Prewitt算子

x方向的Prewitt算子为

y方向的Prewitt算子为

---------------------------------------------

原图像和一个算子进行卷积的大概过程如下

如果图像矩阵中一块区域为

那么x5处的x方向的导数是,将x方向算子的中心和x5重合,然后对应元素相乘再求和,即

x5处的x方向导数为x3+x6+x9-x1-x4-x7

对矩阵中所有元素进行上述计算,就是卷积的过程。

--------------------------------------------

因此,利用原图像和x方向Prewitt算子进行卷积就可以得到图像的x方向导数矩阵Gx,

利用原图像和y方向Prewitt算子进行卷积就可以得到图像的y方向导数矩阵Gy。

利用公式

就可以得到图像的梯度矩阵Gxy,这个矩阵包含图像x方向和y方向的边缘信息。


 

 Python实现卷积及Prewitt算子的边缘检测 

 首先我们把图像卷积函数封装在一个名为imconv的函数中  ( 实际上,scipy库中的signal模块含有一个二维卷积的方法convolve2d()  )

import numpy as np
from PIL import Imagedef imconv(image_array,suanzi):'''计算卷积参数image_array 原灰度图像矩阵suanzi      算子返回原图像与算子卷积后的结果矩阵'''image = image_array.copy()     # 原图像矩阵的深拷贝
    dim1,dim2 = image.shape# 对每个元素与算子进行乘积再求和(忽略最外圈边框像素)for i in range(1,dim1-1):for j in range(1,dim2-1):image[i,j] = (image_array[(i-1):(i+2),(j-1):(j+2)]*suanzi).sum()# 由于卷积后灰度值不一定在0-255之间,统一化成0-255image = image*(255.0/image.max())# 返回结果矩阵return image

 

然后我们利用Prewitt算子计算x方向导数矩阵Gx,y方向导数矩阵Gy,和梯度矩阵Gxy。

import numpy as np
import matplotlib.pyplot as plt# x方向的Prewitt算子
suanzi_x = np.array([[-1, 0, 1],[ -1, 0, 1],[ -1, 0, 1]])# y方向的Prewitt算子
suanzi_y = np.array([[-1,-1,-1],[ 0, 0, 0],[ 1, 1, 1]])# 打开图像并转化成灰度图像
image = Image.open("pika.jpg").convert("L")# 转化成图像矩阵
image_array = np.array(image)# 得到x方向矩阵
image_x = imconv(image_array,suanzi_x)# 得到y方向矩阵
image_y = imconv(image_array,suanzi_y)# 得到梯度矩阵
image_xy = np.sqrt(image_x**2+image_y**2)
# 梯度矩阵统一到0-255
image_xy = (255.0/image_xy.max())*image_xy# 绘出图像
plt.subplot(2,2,1)
plt.imshow(image_array,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,2,2)
plt.imshow(image_x,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,2,3)
plt.imshow(image_y,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,2,4)
plt.imshow(image_xy,cmap=cm.gray)
plt.axis("off")
plt.show()

 

 Prewitt算子 的结果如下图所示

上方:左图为原图像,右图为x方向导数图像

下方:左图为y方向导数图像,右图为梯度图像

从图中可以看出,Prewitt算子虽然能检测出图像边缘,但是检测结果较为粗糙,还带有大量的噪声。


 

 近似导数的Sobel算子 

Sobel算子与Prewitt比较类似,但是它比Prewitt算子要好一些。

x方向的Sobel算子为

y方向的Sobel算子为

python代码只需要将上面代码中的Prewitt算子改成Sobel算子即可。

# x方向的Sobel算子
suanzi_x = np.array([[-1, 0, 1],[ -2, 0, 2],[ -1, 0, 1]])# y方向的Sobel算子
suanzi_y = np.array([[-1,-2,-1],[ 0, 0, 0],[ 1, 2, 1]])

 Sobel算子 的结果如下图所示

上方:左图为原图像,右图为x方向导数图像

下方:左图为y方向导数图像,右图为梯度图像

从图中看出,比较Prewitt算子和Sobel算子,Sobel算子稍微减少了一点噪声,但噪声还是比较多的。


 近似二阶导数的Laplace算子 

Laplace算子是一个二阶导数的算子,它实际上是一个x方向二阶导数和y方向二阶导数的和的近似求导算子。

实际上,Laplace算子是通过Sobel算子推导出来的。

Laplace算子为

Laplace还有一种扩展算子为

为了不再重复造轮子,这次我们运用scipy库中signal模块的convolve()方法来计算图像卷积。

convolve()的第一个参数是原图像矩阵,第二个参数为卷积算子,然后指定关键字参数mode="same"(输出矩阵大小和原图像矩阵相同)。

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import scipy.signal as signal     # 导入sicpy的signal模块# Laplace算子
suanzi1 = np.array([[0, 1, 0],  [1,-4, 1],[0, 1, 0]])# Laplace扩展算子
suanzi2 = np.array([[1, 1, 1],[1,-8, 1],[1, 1, 1]])# 打开图像并转化成灰度图像
image = Image.open("pika.jpg").convert("L")
image_array = np.array(image)# 利用signal的convolve计算卷积
image_suanzi1 = signal.convolve2d(image_array,suanzi1,mode="same")
image_suanzi2 = signal.convolve2d(image_array,suanzi2,mode="same")# 将卷积结果转化成0~255
image_suanzi1 = (image_suanzi1/float(image_suanzi1.max()))*255
image_suanzi2 = (image_suanzi2/float(image_suanzi2.max()))*255# 为了使看清边缘检测结果,将大于灰度平均值的灰度变成255(白色)
image_suanzi1[image_suanzi1>image_suanzi1.mean()] = 255
image_suanzi2[image_suanzi2>image_suanzi2.mean()] = 255# 显示图像
plt.subplot(2,1,1)
plt.imshow(image_array,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,2,3)
plt.imshow(image_suanzi1,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,2,4)
plt.imshow(image_suanzi2,cmap=cm.gray)
plt.axis("off")
plt.show()

 

结果如下图

其中上方为原图像

下方:左边为Laplace算子结果,右边为Laplace扩展算子结果

从结果可以看出,laplace算子似乎比前面两个算子(prewitt算子和Sobel算子)要好一些,噪声减少了,但还是比较多。

而Laplace扩展算子的结果看上去比Laplace的结果少一些噪声。


 降噪后进行边缘检测 

 为了获得更好的边缘检测效果,可以先对图像进行模糊平滑处理,目的是去除图像中的高频噪声。

python程序如下

首先用标准差为5的5*5高斯算子对图像进行平滑处理,然后利用Laplace的扩展算子对图像进行边缘检测。

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import scipy.signal as signal# 生成高斯算子的函数
def func(x,y,sigma=1):return 100*(1/(2*np.pi*sigma))*np.exp(-((x-2)**2+(y-2)**2)/(2.0*sigma**2))# 生成标准差为5的5*5高斯算子
suanzi1 = np.fromfunction(func,(5,5),sigma=5)# Laplace扩展算子
suanzi2 = np.array([[1, 1, 1],[1,-8, 1],[1, 1, 1]])# 打开图像并转化成灰度图像
image = Image.open("pika.jpg").convert("L")
image_array = np.array(image)# 利用生成的高斯算子与原图像进行卷积对图像进行平滑处理
image_blur = signal.convolve2d(image_array, suanzi1, mode="same")# 对平滑后的图像进行边缘检测
image2 = signal.convolve2d(image_blur, suanzi2, mode="same")# 结果转化到0-255
image2 = (image2/float(image2.max()))*255# 将大于灰度平均值的灰度值变成255(白色),便于观察边缘
image2[image2>image2.mean()] = 255# 显示图像
plt.subplot(2,1,1)
plt.imshow(image_array,cmap=cm.gray)
plt.axis("off")
plt.subplot(2,1,2)
plt.imshow(image2,cmap=cm.gray)
plt.axis("off")
plt.show()

 

结果如下图

从图中可以看出,经过降噪处理后,边缘效果较为明显。


 

参考列表

1. 《python计算机视觉编程》 

2. 网络(感谢百度,感觉网络上分享知识的网友)


实际上,一些现成的Python库已经对边缘检测过程进行了封装,效果和效率更为出色。

文中以自己的python代码进行边缘检测,实际上是想对实际过程有更好的认识和了解

 

转载于:https://www.cnblogs.com/smallpi/p/4555854.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460118.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java集合---Arrays类源码解析

一、Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序。其中主要分为Primitive(8种基本类型)和Object两大类。 基本类型:采用调优的快速排序; 对象类型:采用改进的归并排序。 1、对于基本类型源码分析如下(以int[]为例…

oracle12c多个pdb,ProxmoxVE 之 oracle12C 多CDB和PDB

上面左边是我的个人微信,如需进一步沟通,请加微信。 右边是我的公众号“Openstack私有云”,如有兴趣,请关注。继上次在PVE环境上搭建了oracle12C RAC环境(请参考博文“ProxmoxVE 之 安装oracle12C rac集群”)并且安装使用CDB和PD…

【Pascal's Triangle II 】cpp

题目&#xff1a; Given an index k, return the kth row of the Pascals triangle. For example, given k 3,Return [1,3,3,1]. Note:Could you optimize your algorithm to use only O(k) extra space? 代码&#xff1a; class Solution { public:vector<int> getRow…

【DP】HDU 2189 悼念512汶川大地震遇难同胞——来生一起走

打一个小于150的素数表 为了分成的组的人数不重复用dp[i][j] 表示 i表示i个人 j表示分成的组内的最大素数序号 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <string> #include <iostream> #…

oracle高资源消耗sql,Oracle高资源消耗SQL语句定位

Oracle SQL语句资源消耗监控最常用的系统视图有v$sql、v$sqlarea、v$sqltext和v$session。本文我们先了解这些视图的作用与区别&#xff0c;然后了解如何定位高资源消耗SQL语句&#xff0c;最后再了解一下各视图字段具体含义。相关系统视图功能与区别v$sql和v$sqlarea基本相同&…

JavaScript动画知多少?

今天&#xff0c;小学生以自己浅薄的见地&#xff0c;在前辈大能的基础上写这篇文章&#xff0c;希望给大家打开一扇窥探JavaScript&#xff08;以下简称JS&#xff09;动画的窗户。 JS如何制造出动画效果&#xff1f; 结合浏览器提供的 setInterval 或 setTimeout API&#xf…

Socket网络编程--简单Web服务器(6)

本来是想实现ssl连接的&#xff0c;但是弄了好久都不成功&#xff0c;就索性不做了&#xff0c;等以后有能力再做了。所以这一小节就是本次的最后一节了。就简单的说几个注意点。 1.加个配置文件 使用单例模式&#xff0c;使用一个类&#xff0c;该类保存一些信息&#xff0c;例…

postgresql调优

http://blog.pgaddict.com/posts/performance-since-postgresql-7-4-to-9-4-pgbench硬件和系统配置操作系统Ubuntu13.04系统位数64CPUIntel(R) Core(TM)2 Duo CPU内存4G硬盘Seagate ST2000DM001-1CH164测试工具PostgreSQL-9.1.11测试工具工具名称pgbench数据量200W&#xff08;…

操作系统--进程状态切换以及cpu调度(转)

http://www.cnblogs.com/coder2012/p/3204730.html 进程的状态转换 进程在运行中不断地改变其运行状态。通常&#xff0c;一个运行进程必须具有以下三种基本状态。 进程状态 执行态run&#xff1a;进程正在使用CPU等待态wait&#xff1a;进程正在等待I/O完成&#xff0c;不在使…

oracle打开当前表的编辑,oracle sqlplus常用命令

Oracle sqlplus 常用命令1、查看当前库的所有数据表&#xff1a;SQL> select TABLE_NAME from all_tables;select * from all_tables;SQL> select table_name from all_tables where table_name like ‘u’; TABLE_NAME———————————————default_auditing_o…

在Exchange 2010中重建Exchange安全组

问题:有时候&#xff0c;重新安装Exchange 2010会corrupt掉Exchange安全组&#xff0c;或者出现重复的组。用户在这种情况下会出现访问被拒的情况。分析:我们可以删掉这些安全组&#xff0c;然后最这些安全组进行重建&#xff1a;当你prepare ad的时候&#xff0c;由于OtherWel…

1023:坑爹的黑店

题目描述 今天小明去了一个风景如画的地方散心&#xff0c;但是自己带的饮料喝完了&#xff0c;小明口渴难耐&#xff0c;见不远处有家小商店&#xff0c;于是跑去买饮料。小明&#xff1a;“我要买饮料&#xff01;”店主&#xff1a;“我们这里有三种饮料&#xff0c;矿泉水1…

php判断电脑浏览器模拟手机访问网页,在PC上测试移动端网站和模拟手机浏览器的5大方法...

查了很多资料&#xff0c;尝试了大部分方法&#xff0c;下面将这一天的努力总结下分享给大家&#xff0c;也让大家免去看那么多文章&#xff0c;以下介绍的方法&#xff0c;都是本人亲自测试成功的方法&#xff0c;测试环境winxp。一、Chrome*浏览器 chrome模拟手机总共有四种方…

带弹出列表的EditText

最近做的一个Andriod里有一个这个要求&#xff0c;一个东西需要输入&#xff0c;但同时可以在列表直接选择。看到这个需求&#xff0c;瞬间想到了QQ的登录界面&#xff0c;那个账号输入的控件正式我所需要的。 这个账号输入框右边有一个按钮&#xff0c;点击可以显示一个下拉列…

SICP第三章题解

目录 SICP第三章题解ex3-17ex3-18ex3-19队列ex3-21ex3-22ex3-24ex3-253.4 并发&#xff1a;时间是一个本质问题ex3-383.4.2 控制并发的机制ex3-39ex3-41ex3-42串行化、序列化ex3-44串行化的实现ex3-47死锁3.5 流ex3-50序列加速器SICP第三章题解 标签&#xff08;空格分隔&#…

linux cp 目录不存在自动创建,linux – 如果不存在,如何cp文件和创建目录?

我想将svn存储库中的修改文件复制到另一个目录,同时保留其目录结构.在阅读awk和xargs manpage之后,我找到了一种方法来获取更改的文件名,如下所示&#xff1a;$svn status -q | awk { print $2 } | xargs -d \\n -I {} cp {} /tmp/xen/但问题是以这种方式不保留目录结构,我想复…

ios 中 KVO

KVO&#xff08;Key value observe&#xff09;键值观察&#xff0c;是ios中的一种核心的概念&#xff0c;简单的理解为当某一个对象A&#xff08;或者多个对象&#xff09;要想监听对象的B的一个或者多个属性发生变化时&#xff0c;就是用这种机制。 KVO的优点 当某个对象有个…

获取win7时区所有信息

打开命令行工具&#xff1a; tzutil /l# 或者输入到文件中tzutil /l > data.txt 1 # -*- utf-8 -*-2 3 """获取win7所有时区信息&#xff0c;并写入到sql语句中4 5 Usage: python data.py -f data.txt -o data.sql6 """7 8 if __name__ &quo…

linux kill命令信号,Linux kill 命令详解

Linux kill 命令很容易让人产生误解&#xff0c;以为它仅仅就是用来杀死进程的。我们来看一下 man page 对它的解释&#xff1a;kill - send a signal to a process.从官方的解释不难看出&#xff0c;kill 是向进程发送信号的命令。当然我们可以向进程发送一个终止运行的信号&a…

VSTO学习笔记(二)Excel对象模型

原文:VSTO学习笔记&#xff08;二&#xff09;Excel对象模型上一次主要学习了VSTO的发展历史及其历代版本的新特性&#xff0c;概述了VSTO对开发人员的帮助和效率提升。从这次开始&#xff0c;将从VSTO 4.0开始&#xff0c;逐一探讨VSTO开发中方方面面&#xff0c;本人接触VSTO…