文章目录
- 9形态学图像处理
- 9.1预备知识
- 9.2腐蚀与膨胀
- 9.2.1腐蚀
- 9.2.2膨胀
- 9.2.3对偶性
- 9.3开操作和闭操作
- 9.4击中或击不中变换
- 9.5一些基本形态学方法
- 9.5.1边界提取
- 9.5.2空洞填充
- 9.5.3连通分量的提取
- 9.5.4凸壳
- 9.5.5细化
- 9.5.6粗化
- 9.6灰度级形态学
- 9.6.3一些基本的形态学算法
9形态学图像处理
9.1预备知识
图像形态学也叫数学形态学,是指一系列处理图像形状特征的图像处理技术,是一门建立在格伦和拓扑学基础上的图像分析学科,是数学形态学图像处理的基本理论。其基本思想是利用一种特殊的结构元来测量或提取输入图像中相应的形状或特征,以便进一步进行图像分析和目标识别。形态学方法的基础是集合论。
常见图像形态学运算:腐蚀、膨胀、开运算、闭运算、骨架抽取、极线腐蚀、击中击不中变换、Top-hat变换、颗粒分析、流域变换、形态学梯度等。其中最基本的形态学操作是:膨胀(dilation)和腐蚀(erosion)两种操作,腐蚀和膨胀的主要作用有:消除噪声;分割出独立的图像元素,在图像中连接相邻的元素;寻找图像中明显的极大值或极小值区;求出图像的梯度等
不做特殊说明,输入图像为二值图像。图像中1是前景,0是背景。
构元(Structuring Elements,SE)可以是任意形状,SE中的的值可以是0或1。常见的结构元有矩形和十字形。结构元有一个锚点O,O一般定义为结构元的中心(也可以自由定义位置)。如下图所示是几个不同形状的结构元,紫红色区域为锚点O。
整个结构元我们可以使用一个 正方形的模板矩阵来表示,窗口大小wnd_size表示矩阵的宽高,模板数据元素值为0或者1,1表示结构元中对应元素要起作用。通过标记不同的元素值,我们可以使用模板矩阵来表示任意形状的结构元。
9.2腐蚀与膨胀
9.2.1腐蚀
表示为
A
⊖
B
A\ominus B
A⊖B的B对A的腐蚀定义为:
A
⊖
B
=
{
z
∣
(
B
)
z
⊆
A
}
A\ominus B=\left\{z\mid\left(B\right)_{z}\subseteq A\right\}
A⊖B={z∣(B)z⊆A}
表面上,该式指出B对A的腐蚀是一个用z平移的B包含在A中的所有的点z的集合。假定集合B是一个结构元,因为B必须包含在A中这一陈述等价于B不与背景共享任何公共元素,故我们可以将腐蚀表达为如下的等价形式:
A
⊖
B
=
{
z
∣
(
B
)
z
∩
A
c
=
∅
}
A\ominus B=\left\{z\mid\left(B\right)_z\cap A^c=\varnothing\right\}
A⊖B={z∣(B)z∩Ac=∅}
import cv2
import numpy as np
import matplotlib.pyplot as plt
import random
import math
def cv2_show(*args):
for ttt in range(len(args)):
img=args[ttt]
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def plt_show(*args):
plt.figure(figsize=(12, 6))
for ttt in range(len(args)):
img = args[ttt]
if (len(img.shape) == 3):
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
elif (len(img.shape) == 2):
img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
plt.subplot(231+ttt), plt.imshow(img)
img=cv2.imread('dige.png',0)
kernel = np.ones((3,3),np.uint8)
num=[[]]*6
for i in range(6):
num[i] = cv2.erode(img,kernel,iterations = i)
plt_show(num[0],num[1],num[2],num[3],num[4],num[5])
可看出腐蚀操作使得线条变细直至消失
9.2.2膨胀
表示为
A
⊕
B
A\oplus B
A⊕B的B对A的膨胀定义为
A
⊕
B
=
{
z
∣
(
B
^
)
z
∩
A
≠
∅
}
A\oplus B=\left\{z\mid(\hat{B})_{z}\cap A\neq\emptyset\right\}
A⊕B={z∣(B^)z∩A=∅}
这个公式是以B关于它的原点的映像,并且以z对映像进行平移为基础的。B对A的膨胀是所有位移z的集合,这样,B和A至少有一个元素是重叠的。根据这种解释,式子可以等价地写为
A
⊕
B
=
{
z
∣
[
(
B
^
)
z
∩
A
]
⊆
A
}
A\oplus B=\left\{z\mid[(\hat{B})_z\cap A]\subseteq A\right\}
A⊕B={z∣[(B^)z∩A]⊆A}
img=cv2.imread('dige.png',0)
kernel = np.ones((3,3),np.uint8)
num=[[]]*6
for i in range(6):
num[i] = cv2.dilate(img,kernel,iterations = i)
plt_show(num[0],num[1],num[2],num[3],num[4],num[5])
可看出线条不断变粗
9.2.3对偶性
膨胀和腐蚀彼此关于集合求补运算和反射运算是对偶的,即 ( A ⊖ B ) c = A c ⊕ B ^ (A\ominus B)^c=A^c\oplus\hat{B} (A⊖B)c=Ac⊕B^ 和 ( A ⊕ B ) c = A c ⊖ B ^ \left(A\oplus B\right)^{c}= A^{c}\ominus \hat{B} (A⊕B)c=Ac⊖B^
9.3开操作和闭操作
- 开运算:先腐蚀,后膨胀
- 闭运算:先膨胀,后腐蚀
结构元B对集合A的开操作,表示为
A
∘
B
A\circ B
A∘B,其定义如下:
A
∘
B
=
(
A
⊖
B
)
⊕
B
A\circ B=(A\ominus B)\oplus B
A∘B=(A⊖B)⊕B
结构元B对集合A的闭操作,表示为
A
∙
B
A\bullet B
A∙B,其定义如下:
A
∙
B
=
(
A
⊕
B
)
⊖
B
A \bullet B=(A\oplus B)\ominus B
A∙B=(A⊕B)⊖B
kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
plt_show(img,opening,closing)
图一为原图,图二和图三分别为开操作和闭操作后的结果
9.4击中或击不中变换
- 作用:在二值图像中找到匹配
- 公式: A ∗ ◯ B = ( A ⊖ B 1 ) ∩ ( A c ⊖ B 2 ) A\textcircled{*} B=(A\ominus B_1)\cap(A^c\ominus B_2) A∗◯B=(A⊖B1)∩(Ac⊖B2)
9.5一些基本形态学方法
9.5.1边界提取
原图-腐蚀: β ( A ) = A − ( A ⊖ B ) \beta\left(A\right) =A-\left(A\ominus B\right) β(A)=A−(A⊖B)
a=cv2.imread('am.png',0)
kernel = np.ones((3,3),np.uint8)
a2=cv2.erode(a,kernel,iterations = 1)
plt_show(a,a2,a-a2)
如图3所示,提取了边界
9.5.2空洞填充
填充公式: X k = ( X k − 1 ⊕ B ) ∩ A c k = 1 , 2 , 3 , ⋯ X_{k}=(X_{k-1}\oplus B)\cap A^c\quad k=1,2,3,\cdots Xk=(Xk−1⊕B)∩Ack=1,2,3,⋯
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread("dong.png")
# 二值化
imgray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
imgray[imgray < 100] = 0
imgray[imgray >= 100] = 255
# 原图取补得到MASK图像
mask = 255 - imgray
# 构造Marker图像
marker = np.zeros_like(imgray)
marker[0, :] = 255
marker[-1, :] = 255
marker[:, 0] = 255
marker[:, -1] = 255
marker_0 = marker.copy()
# 形态学重建
SE = cv.getStructuringElement(shape=cv.MORPH_CROSS, ksize=(3, 3))
while True:
marker_pre = marker
dilation = cv.dilate(marker, kernel=SE)
marker = np.min((dilation, mask), axis=0)
if (marker_pre == marker).all():
break
dst = 255 - marker
filling = dst - imgray
# 显示
plt.figure(figsize=(12, 6)) # width * height
plt.subplot(2, 3, 1), plt.imshow(imgray, cmap='gray'), plt.title('src'), plt.axis("off")
plt.subplot(2, 3, 2), plt.imshow(mask, cmap='gray'), plt.title('Mask'), plt.axis("off")
plt.subplot(2, 3, 3), plt.imshow(marker_0, cmap='gray'), plt.title('Marker 0'), plt.axis("off")
plt.subplot(2, 3, 4), plt.imshow(marker, cmap='gray'), plt.title('Marker'), plt.axis("off")
plt.subplot(2, 3, 5), plt.imshow(dst, cmap='gray'), plt.title('dst'), plt.axis("off")
plt.subplot(2, 3, 6), plt.imshow(filling, cmap='gray'), plt.title('Holes'), plt.axis("off")
plt.show()
图5为空洞填充效果图
9.5.3连通分量的提取
从二值图像中提取连通分量是许多自动图像分析应用中的核心。令A是包含一个或多个连通分量的集合,并形成一个阵列X。(该阵列的大小与包含A的阵列的大小相同).除了在对应于A中每个连通分量中的一个点的已知的每一个位置处我们已置为1(前景值)外,该阵列的所有其他元素均为0(背景值)。如下迭代过程可完成这一目的:$ X k = ( X k − 1 ⊕ B ) ∩ A k = 1 , 2 , 3 , ⋯ X_k=(X_{k-1}\oplus B)\cap A\quad k=1,2,3,\cdots Xk=(Xk−1⊕B)∩Ak=1,2,3,⋯
9.5.4凸壳
如果在集合A内连接任意两个点的直线段都在A的内部、则称集合A是凸形的。任意集合S的凸壳H是包含于S的最小凸集。集合差H-S称为S的凸缺。凸壳和凸缺对于物体描绘是很有用的。这里,我们介绍一种获得集合A的凸壳C(A)的简单形态学算法。
令
B
i
,
i
=
1
,
2
,
3
,
4
B^i,i= 1,2,3,4
Bi,i=1,2,3,4表示下图中的4个结构元。这个过程可通过执行下式实现:
X
k
i
=
(
X
k
−
1
∗
◯
B
i
)
∪
A
i
=
1
,
2
,
3
,
4
和
k
=
1
,
2
,
3
,
⋯
X_{k}^{i}=(X_{k-1}{\textcircled{*}}B^{i})\cup A \quad i=1,2,3,4 和 k=1,2,3,\cdots
Xki=(Xk−1∗◯Bi)∪Ai=1,2,3,4和k=1,2,3,⋯
其中
X
0
i
=
A
X_0^i=A
X0i=A。当该过程收敛时(即当
X
k
i
=
X
k
−
1
i
X_k^i=X_{k-1}^i
Xki=Xk−1i时),我们令
D
i
=
X
k
i
D^i=X^i_k
Di=Xki。则A 的凸壳为
C
(
A
)
=
⋃
i
=
1
4
D
i
C({A})=\bigcup_{i=1}^4D^i
C(A)=i=1⋃4Di
9.5.5细化
定义: A ⊗ B = A − ( A ∗ ◯ B ) = A ∩ ( A ∗ ◯ B ) C A\otimes B=A-(A\textcircled{*} B)=A\cap(A\textcircled{*} B)^{C} A⊗B=A−(A∗◯B)=A∩(A∗◯B)C
9.5.6粗化
定义: A ∙ B = A ∪ ( A ∗ ◯ B ) A\bullet B=A\cup(A\textcircled{*} B) A∙B=A∪(A∗◯B)
9.6灰度级形态学
9.6.3一些基本的形态学算法
形态学梯度:膨胀-腐蚀
img=cv2.imread('am.png',0)
kernel = np.ones((3,3),np.uint8)
dilate = cv2.dilate(img,kernel,iterations = 1)
erosion = cv2.erode(img,kernel,iterations = 1)
img2=dilate-erosion
plt_show(img,dilate,erosion,img2)
上图中,(膨胀-腐蚀)可得出梯度(最后一幅图)
礼帽与黑帽
- 礼帽 = 原始输入-开运算结果(取’刺’)
- 黑帽 = 闭运算-原始输入(取’整体’)
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, np.ones((7,7),np.uint8) )
blackhat = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, np.ones((15,15),np.uint8) )
plt_show(img,tophat,blackhat)