设为首页 收藏本站
查看: 1687|回复: 0

[经验分享] python计算机视觉2:图像边缘检测

[复制链接]

尚未签到

发表于 2015-12-1 09:59:09 | 显示全部楼层 |阅读模式
  
我是一名初学者,如果你发现文中有错误,请留言告诉我,谢谢

  
  如果需要检测到图像里面的边缘,首先我们需要知道边缘处具有什么特征。
  对于一幅灰度图像来说,边缘两边的灰度值肯定不相同,这样我们才能分辨出哪里是边缘,哪里不是。
  因此,如果我们需要检测一个灰度图像的边缘,我们需要找出哪里的灰度变化最大。显然,灰度变化越大,对比度越强,边缘就越明显。
  那么问题来了,我们怎么知道哪里灰度变化大,哪里灰度变化小呢?

   导数,梯度,边缘信息
  在数学中,与变化率有关的就是导数。
  如果灰度图像的像素是连续的(实际不是),那么我们可以分别原图像G对x方向和y方向求导数
DSC0000.jpg DSC0001.jpg
  获得x方向的导数图像Gx和y方向的导数图像Gy。Gx和Gy分别隐含了x和y方向的灰度变化信息,也就隐含了边缘信息。
  如果要在同一图像上包含两个方向的边缘信息,我们可以用到梯度。(梯度是一个向量)
  原图像的梯度向量Gxy为(Gx,Gy),梯度向量的大小和方向可以用下面两个式子计算
DSC0002.jpg
DSC0003.jpg
  角度值好像需要根据向量所在象限不同适当+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算子为
DSC0004.jpg
  y方向的Prewitt算子为
DSC0005.jpg
  ---------------------------------------------
  原图像和一个算子进行卷积的大概过程如下
  如果图像矩阵中一块区域为
DSC0006.jpg
  那么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 Image
def 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-255
image = 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方向导数图像,右图为梯度图像
DSC0007.png
  从图中可以看出,Prewitt算子虽然能检测出图像边缘,但是检测结果较为粗糙,还带有大量的噪声。

  
   近似导数的Sobel算子
  Sobel算子与Prewitt比较类似,但是它比Prewitt算子要好一些。
  x方向的Sobel算子为
DSC0008.jpg
  y方向的Sobel算子为
DSC0009.jpg
  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方向导数图像,右图为梯度图像
DSC00010.png
  从图中看出,比较Prewitt算子和Sobel算子,Sobel算子稍微减少了一点噪声,但噪声还是比较多的。

   近似二阶导数的Laplace算子
  Laplace算子是一个二阶导数的算子,它实际上是一个x方向二阶导数和y方向二阶导数的和的近似求导算子。
  实际上,Laplace算子是通过Sobel算子推导出来的。
  Laplace算子为
DSC00011.jpg
  Laplace还有一种扩展算子为
DSC00012.jpg
  为了不再重复造轮子,这次我们运用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扩展算子结果
DSC00013.jpg
  从结果可以看出,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()
  
  结果如下图
DSC00014.jpg
  从图中可以看出,经过降噪处理后,边缘效果较为明显。

  
  参考列表
  1. 《python计算机视觉编程》
  2. 网络(感谢百度,感觉网络上分享知识的网友)

  实际上,一些现成的Python库已经对边缘检测过程进行了封装,效果和效率更为出色。
  文中以自己的python代码进行边缘检测,实际上是想对实际过程有更好的认识和了解
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-145738-1-1.html 上篇帖子: Python学习 Day 4 函数 切片 迭代 列表生成式 生成器 下篇帖子: 让Python代码更快运行的 5 种方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表