数学形态学(mathematical morphology)关注的是图像中的形状,它提供了一些方法用于检测形状和改变形状。起初是基于二值图像提出的,后来扩展到灰度图像。二值图像就是:每个像素的值只能是0或1,1代表描绘图像的点,0代表背景。

基本的形态学运算包括:腐蚀(erosion)、膨胀(dilation)、开(opening)、闭(closing),对于这些运算,都需要用到被称为结构元素(Structuring element)的模板,一般为方形,以小矩阵的形式表示,但它的元素的值只能是0或1,它代表的是一个集合,这个集合罩在原图像上,可以跟原图像的形状进行集合运算。

腐蚀(erosion)

要讲清楚去处过程不容易,直接上图看效果:

图中(a)为原图像,(b)为腐蚀运算后结果,可以看出除了字母笔刷变细了之外,黑色背景的噪点也都不见了,(c)是膨胀运算结果,字母笔刷比原图像粗。

ok,现在看腐蚀是怎么实现的,还是先看图:

如图所示,(a)是3×3结构元素,相当于:

array([[ 1.,  1.,  1.],[ 1.,  1.,  1.],[ 1.,  1.,  1.]])

图中标识出了它的中心点。

结构元素的设置也可以是其它大小,也不一定全是1(黑点),比如是一个3×3十字形:

[[0,1,0],[1,1,1],[0,1,0]]

(b)为待处理的原图像,我们把其中由所有黑点组成的集合设为X

(c)为腐蚀后的结果,黑色点就是经过腐蚀之后保留下来的点,灰色的点表示被排除出去的点,我们看到的效果是X变小了一圈,这也之所以叫腐蚀的原因吧。

可以这样来形象理解腐蚀运算过程:将结构元素平移到原图像上某个位置,如果结构元素中所有的黑点(值为1)都落在X里,就把结构元素中心点对应的原图像的像素点保留下来,否则就排除出去,如(c)所示,假设结构元素盖在这个位置,这时结构元素下半部还有几个点没落在原图X中,所以将中心点对应的像素点排除出去,从黑色标记为灰色。将结构元素在原图像上进行平移,直到原图像的每一个像素都被处理过。

所以这个结果也会把形状以外的噪点排除掉。

腐蚀函数说明

scipy.ndimage.morphology.binary_erosion(input, structure=None, iterations=1,...)input: 原图像二值图
structure: 即结构元素,默认为3×3十字形
iterations: 表示要连续应用腐蚀多少次返回腐蚀后二值图结果,ndarray类型

示例:

>>> a = np.zeros((7,7), dtype=np.int)
>>> a[1:6, 2:5] = 1
>>> a  #原图像二值图,注意中间由1组成的矩形形状
array([[0, 0, 0, 0, 0, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 1, 1, 1, 0, 0],[0, 0, 0, 0, 0, 0, 0]])>>> ndimage.binary_erosion(a).astype(a.dtype)
#可以看出矩形形状被"腐蚀"了一圈
array([[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 1, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0]])     

膨胀(dilation)

类似地:

如图(c)就是膨胀的结果,运算过程跟腐蚀类似,只不过对像素的排除判断不一样,膨胀的判断方式是:只要结构元素中有一个黑点(值为1)落在X集合里,就把结构元素中心点对应的原图像的像素点保留下来,否则就排除出去。

膨胀函数scipy.ndimage.morphology.binary_dilation与腐蚀类似,使用示例:

>>> a = np.zeros((5, 5))
>>> a[2, 2] = 1
>>> a
array([
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.]])
>>> ndimage.binary_dilation(a).astype(a. dtype) #binary_dilation第二个参数可指定结构元素,默认为3×3十字形
array([
[ 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 1., 1., 1., 0.],
[ 0., 0., 1., 0., 0.],
[ 0., 0., 0., 0., 0.]])

我们从以上的效果图可以看到,腐蚀和膨胀可以改变形状,同时也可以去背景噪点。
另外,把形状的膨胀结果减去它的腐蚀结果,可以得到形状的粗略边缘以及角点。

开(opening)

先对原图像进行腐蚀,再膨胀,就是开运算。有什么用呢?简单点说它可以去除与结构元素大小相当的孔洞和碎片。如果一处图像中有多个形状,开运算可以把那些只有一点点粘连的形状分开。因为那点粘连的地方被去除了。
简单示例:

>>> a = np.zeros((5,5), dtype=np.int)
>>> a[1:4, 1:4] = 1; a[4, 4] = 1
>>> a #原图像,注意右下角有个1,表示零散的碎片
array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 1]])>>> ndimage.binary_opening(a, structure=np.ones((3,3))).astype(np.int)
array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0]]) #基于3×3全1的结构元素应用开运算,把原图像角落的1去掉>>> ndimage.binary_opening(a).astype(np.int)
array([[0, 0, 0, 0, 0],[0, 0, 1, 0, 0],[0, 1, 1, 1, 0],[0, 0, 1, 0, 0],[0, 0, 0, 0, 0]]) #还可以用于平滑边角,也就是四角处缩小变平滑了,如果形状与形状有边角的粘连,就可以分开

闭(closing)

与开运算相反,先对原图进行膨胀,再腐蚀,就是闭运算。闭运算可以填充图像中的孔洞,连接一些缺口和碎片,变成块状。举个应用场景——车牌定位,如下图:

右图是使用通过简单的算法得到车的粗略边角,车牌位置像是一堆散点,如果对这个边角图运用闭运算可以得到这样的效果:

车牌的位置变成一个接近车牌形状的矩形,为下一步检测提供了便利。

闭运算函数ndimage.binary_closing的用法:

>>> a = np.zeros((5,5), dtype=np.int)
>>> a[1:-1, 1:-1] = 1; a[2,2] = 0
>>> a #原图像,注意中间有个0,表示形状里面有个空洞
array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 0, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0]])>>> ndimage.binary_closing(a).astype(np.int)
array([[0, 0, 0, 0, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 1, 1, 1, 0],[0, 0, 0, 0, 0]])  #应用闭运算之后,空洞被填充了

开闭运算原理看似简单,但很强大,只要结构元素选取得当,可以做很多事情。

对象计数(Counting Objects)

这里说的对象是指图像中与周围没有连通的单独的形状,我们的目标是要计算这些对象的个数,计算对象个数可以使用函数:

label, num_features = scipy.ndimage.measurements.label(input, structure=None, output=None)
参数
input: 数组类型,其中元素非0值表示对象组成的点,0表示图像背景
structure: 结构元素,用于检测对象的连通特征,默认是3×3十字形返回值
label: 返回与input一样的大小,但是把对象标记出来
num_features:对象的个数

用法简单示例:

>>> a = np.array([[0,0,1,1,0,0],
...               [0,0,0,1,0,0],
...               [1,1,0,0,1,0],
...               [0,0,0,1,0,0]])
>>> labeled_array, num_features = measurements.label(a) #使用默认3×3十字形结构元素
>>> print(num_features)
4
>>> print(labeled_array) #打印被识别出来的对象的位置,分别用1,2,3...递增的下标标记出来,所以labeled_array可以当成灰度图打印出来,被标识的对象的灰度从黑到白变化
array([[0, 0, 1, 1, 0, 0],[0, 0, 0, 1, 0, 0],[2, 2, 0, 0, 3, 0],[0, 0, 0, 4, 0, 0]])

从上面例子看出,使用默认3×3十字形结构元素,检测时,只有水平和垂直连通才认为像素属于同一个对象,对角连通不算,如果要把对角连通当作是同一个对象来计算,可以指定结构元素为:

[[1,1,1],[1,1,1],[1,1,1]]

有时候,因受噪声影响,对象之间有一点边角的粘连,人眼可以很容易分辨出是两个对象,但要让label函数理解这一点,可以使用前面提到的开运算先对把对象稍微分开,再把结果传给label函数进行计数,下面给出一个具体的图像进行示例:

from PIL import Image
import numpy as np
from scipy.ndimage import measurements,morphology
import matplotlib.pyplot as pltim = np.array(Image.open('house.png').convert('L'))
im = 1 * (im < 128) #把灰度图像转为二值图,即灰度少于128的当成图像黑点,否则当作背景label_from_origin, num_from_origin = measurements.label(im)im_open = morphology.binary_opening(im, np.ones((9, 5)), iterations=2) #运用了一个9×5全1的结构元素,并连续应用两次开运算
label_from_open, num_from_open = measurements.label(im_open)#以下是画图
index = 221
plt.subplot(index)
plt.imshow(im)
plt.title('original')
plt.axis('off')plt.subplot(index + 1)
plt.imshow(label_from_origin)
plt.title('%d objects' % num_from_origin)
plt.axis('off')plt.subplot(index + 2)
plt.imshow(im_open)
plt.title('apply opening')
plt.axis('off')plt.subplot(index + 3)
plt.imshow(label_from_open)
plt.title('%d objects' % num_from_open)
plt.axis('off')#plt.gray()  #为了更好的看出对象的分离,故意不用灰度显示
plt.show()

效果图如下,第二组(即第二行)是应用开运算之后的图像及计算结果,跟第一组相比,对象计数增加了,我在第二组图中圈出了应用开运算之后的主要变化之处:

小结

上面介绍的用于二值图的一些函数,也有其对应的用于灰度图像的函数,包括:

  • grey_erosion()

  • grey_dilation()

  • grey_opening()

  • grey_closing()

下一节学习图像去噪。
你还可以查看其它笔记。

Programming Computer Vision with Python (学习笔记七)相关推荐

  1. Programming Computer Vision with Python (学习笔记一)

    转载自:http://segmentfault.com/a/1190000003941588 介绍 <Programming Computer Vision with Python>是一本 ...

  2. Programming Computer Vision with Python【学习笔记】【第一章】

    第1章 基本的图像操作和处理 1.1 PIL:Python图像处理类库 1.1.1 转换图像格式--save()函数 1.1.2 创建缩略图 1.1.3 复制并粘贴图像区域 1.1.4 调整尺寸和旋转 ...

  3. Programming Computer Vision with Python (学习笔记四)

    上一个笔记主要是讲了PCA的原理,并给出了二维图像降一维的示例代码.但还遗留了以下几个问题: 在计算协方差和特征向量的方法上,书上使用的是一种被作者称为compact trick的技巧,以及奇异值分解 ...

  4. Programming Computer Vision with Python (学习笔记十二)

    ORB(Oriented FAST and Rotated BRIEF)可用来替代SIFT(或SURF),它对图像更具有抗噪特性,是一种特征检测高效算法,其速度满足实时要求,可用于增强图像匹配应用. ...

  5. Programming Computer Vision with Python (学习笔记十一)

    尺度不变特征变换(Scale-invariant feature transform, 简称SIFT)是图像局部特征提取的现代方法--基于区域/图像块的分析.在上篇笔记里我们使用的图像之间对应点的匹配 ...

  6. Programming Computer Vision with Python (学习笔记九)

    角检测(Corner detection)是指检测图像中具有代表性的(我们感兴趣的)角点,一般讲为形状或边缘的拐角处,这些点可以大略标记对象在图像中的轮廓和位置,如果从一个图像序列中检测每个图像的角点 ...

  7. Programming Computer Vision with Python (学习笔记八)

    图像去噪(Image Denoising)的过程就是将噪点从图像中去除的同时尽可能的保留原图像的细节和结构.这里讲的去噪跟前面笔记提过的去噪不一样,这里是指高级去噪技术,前面提过的高斯平滑也能去噪,但 ...

  8. Programming Computer Vision with Python (学习笔记五)

    SciPy库 SciPy库,与之前我们使用的NumPy和Matplotlib,都是scipy.org提供的用于科学计算方面的核心库.相对NumPy,SciPy库提供了面向更高层应用的算法和函数(其实也 ...

  9. Programming Computer Vision with Python (学习笔记二)

    首先介绍跟图像处理.显示有关两个库:NumPy和Matplotlib,然后介绍增强图像对比度的实现原理. NumPy NumPy是Python用于科学计算的基础库,提供了一些很有用的概念,如:N维数组 ...

最新文章

  1. C# 使用HttpWebRequest提交ASP.NET表单并保持Session和Cookie
  2. 基于现有工程解读stm32的工程文件
  3. bootstrap 弹框使用
  4. 拼音输入法的数学原理
  5. 2岁男童入住自如后患白血病离世 自如回复:道歉做不到
  6. linux 访问samba共享文件夹权限,请教:linux下 利用samba网络共享文件夹修改权限的问题...
  7. oracle控制文件还原,Oracle的控制文件的恢复与重建
  8. 本地语音控制模块 | 带语音识别的智能家居方案
  9. 如何用tomcat发布自己的Java项目
  10. python 下载文件 限速-突破xx云盘的限速,利用python实现加速下载
  11. 【VS插件】VS2012设置透明背景
  12. OA办公自动化系统设计方案
  13. 基于FPGA打地鼠游戏的设计与实现
  14. SpringCloud 学习笔记 前端(二) ES6语法相关介绍
  15. SIM800C实现GPRS上网的AT指令
  16. Mono.Cecil 修改目标.NET的IL代码保存时报异常的处理。
  17. Local time zone must be set-see zic manual page
  18. 客户端和服务器的关系
  19. gcc编译工具常用命令以及汇编语言
  20. Ubuntu定时开关机

热门文章

  1. 利用开源社区打造微服务生态体系
  2. Java 编程的动态性, 第4部分: 用 Javassist 进行类转换--转载
  3. 【Python】merge:数据规整化:清理、转换、合并、重塑
  4. 【待继续研究】解析机器学习技术在反欺诈领域的应用
  5. 二值化每个特征,微软用1350亿参数稀疏神经网络改进搜索结果
  6. 融资租赁基础干货知识大整理
  7. linux https重定向,Linux | Apache环境下强制http跳转至https的配置总结
  8. Java - Java集合中的快速失败Fail Fast 机制
  9. RocketMQ-初体验RocketMQ(06)-使用API操作RocketMQ ,理解RocketMQ的存储结构
  10. RocketMQ-初体验RocketMQ(05)_RocketMQ架构解读