数学形态学
数学形态学汇集了基于集合论、格论和拓扑学的几种技术。 它最初专门用于二值图像,但现在已扩展到灰度图像。 在本课程中,我们将演示限制在二值图像上, 因此我们认为图像中的对象定义了一个像素集合。
import numpy as np
import matplotlib.pyplot as plt
# 定义两个二值图像(集合A和B)
A = np.array([
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 0, 0],
[0, 1, 1, 0, 0, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
], dtype=bool)
B = np.array([
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[0, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0]
], dtype=bool)
# 执行集合运算
complement_A = ~A
union_AB = A | B
intersection_AB = A & B
difference_AB = A & (~B)
# 绘图
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
ax = axes.ravel()
ax[0].imshow(A, cmap='gray_r', interpolation='nearest')
ax[0].set_title('集合 A')
ax[1].imshow(B, cmap='gray_r', interpolation='nearest')
ax[1].set_title('集合 B')
ax[2].imshow(complement_A, cmap='gray_r', interpolation='nearest')
ax[2].set_title('A的补集 (A^c)')
ax[3].imshow(union_AB, cmap='gray_r', interpolation='nearest')
ax[3].set_title('并集 (A U B)')
ax[4].imshow(intersection_AB, cmap='gray_r', interpolation='nearest')
ax[4].set_title('交集 (A ∩ B)')
ax[5].imshow(difference_AB, cmap='gray_r', interpolation='nearest')
ax[5].set_title('差集 (A \ B)')
for a in ax:
a.axis('off')
plt.tight_layout()
plt.show()结构元素¶
所有形态学算子都使用结构元素(SE),也称为核。结构元素是一个小的二值图像——一个形状——用来探测主图像。它定义了每个操作要考虑的像素邻域。
结构元素 是一个具有定义原点(或锚点)的像素集合。原点是结构元素上用于在图像上定位的点。虽然原点通常是结构元素的中心,但它可以是SE前景区域内甚至外部的任何像素。这允许在形态学操作中进行偏移。在下面的文本中,我们用 表示原点位于主图像中像素 处的结构元素。
结构元素的大小和形状至关重要,因为它们决定了形态学操作的效果。常见的形状包括正方形、圆盘和十字形。结构元素的选择应该以图像中对象的几何形状为指导。例如,圆形SE可能用于查找圆孔,而线性SE可能用于查找水平线。

Figure 7:数学形态学操作中使用的常见结构元素示例。

Figure 8:显示膨胀过程的动画,展示结构元素在图像上逐步移动的过程。
from skimage.morphology import square, disk, star
import matplotlib.pyplot as plt
# 创建不同的结构元素
se_square = square(5)
se_disk = disk(3)
se_star = star(4)
# 绘图
fig, axes = plt.subplots(1, 3, figsize=(10, 5))
ax = axes.ravel()
ax[0].imshow(se_square, cmap='gray_r', interpolation='nearest')
ax[0].set_title('正方形SE (5x5)')
ax[1].imshow(se_disk, cmap='gray_r', interpolation='nearest')
ax[1].set_title('圆形SE (半径 3)')
ax[2].imshow(se_star, cmap='gray_r', interpolation='nearest')
ax[2].set_title('星形SE (半径 4)')
for a in ax:
a.axis('off')
plt.show()

Figure 9:显示腐蚀过程的动画,展示结构元素在图像上逐步移动的过程。
基本运算¶
膨胀¶
膨胀是一种形态学运算,用于扩大或加粗图像的前景区域。直观效果是它会增长对象的边界。
形式上,图像 经过结构元素 的膨胀是所有像素位置 的集合,其中 的反射平移到 后与 有非空交集。设 是 关于其原点的反射。则膨胀定义为:
这意味着,如果将结构元素的原点放置在某个像素上时,它与输入图像中的至少一个前景像素重叠,则输出图像中的该像素被设置为1。此过程有效地扩展了图像中的对象。膨胀对于填充小间隙和连接不相交的对象很有用。
Figure 10:在小图像 上使用结构元素 进行膨胀的示例(原点 位于中心,用蓝点表示)。
结构元素通常用矩阵描述。因此,Figure 10 中的结构元素写作:
注意,该矩阵不考虑围绕 主体的零像素。
膨胀具有以下性质:
膨胀是二元运算,且不是线性的。因此,它不能表示为线性的数学算子卷积。
膨胀是可结合的,即两个连续膨胀的应用可以按任何顺序进行:
(这里,下标1和2表示两个不同的结构元素。)
膨胀是单调运算,因为包含关系被保留:
腐蚀¶
腐蚀是膨胀的对偶运算。它收缩或细化图像的前景区域,有效地腐蚀对象的边界。
图像 经过结构元素 的腐蚀是所有像素位置 的集合,其中平移到 的结构元素 完全包含在 中。
只有当结构元素在某个像素居中时完全位于输入图像的前景内,输出图像中的该像素才被设置为1。此操作可移除小对象、平滑对象边界并加宽对象之间的间隙。
Figure 11:在小图像 上使用结构元素 进行腐蚀的示例(原点 位于中心,用蓝点表示)。
腐蚀具有与膨胀相似的性质:
腐蚀不能表示为卷积。
腐蚀是可结合的:
注意,两次连续腐蚀的结果等效于一次腐蚀,其结构元素是前两个结构元素的膨胀。
腐蚀是单调运算:
对偶性¶
膨胀和腐蚀是对偶算子。 将背景视为主体,主体视为背景(即通过处理图像的补集),膨胀会转换成腐蚀,反之亦然:
import numpy as np
import matplotlib.pyplot as plt
from skimage.morphology import dilation, erosion, disk
from skimage.draw import rectangle, circle_perimeter
# 创建一个包含不同形状的示例二值图像
image = np.zeros((100, 100), dtype=np.uint8)
rr, cc = rectangle(start=(10, 10), end=(40, 50), shape=image.shape)
image[rr, cc] = 1
rr, cc = rectangle(start=(50, 60), end=(80, 80), shape=image.shape)
image[rr, cc] = 1
rr, cc = circle_perimeter(60, 25, 10)
image[rr, cc] = 1
image[90, 90] = 1 # 添加一个小的孤立点
# 定义一个结构元素
selem = disk(5)
# 执行膨胀和腐蚀
dilated_image = dilation(image, selem)
eroded_image = erosion(image, selem)
# 绘图
fig, axes = plt.subplots(1, 3, figsize=(15, 7))
ax = axes.ravel()
ax[0].imshow(image, cmap='gray_r', interpolation='nearest')
ax[0].set_title('原始图像')
ax[1].imshow(dilated_image, cmap='gray_r', interpolation='nearest')
ax[1].set_title('膨胀图像')
ax[2].imshow(eroded_image, cmap='gray_r', interpolation='nearest')
ax[2].set_title('腐蚀图像')
for a in ax:
a.axis('off')
plt.show()
基本算子的组合¶
开运算¶
开运算是另一个基本的形态学过程。它被定义为先进行腐蚀,然后进行膨胀,两次操作使用相同的结构元素。
开运算的主要效果是移除小的前景对象(通常被认为是噪声),并断开较大对象之间的细微连接或狭窄部分。它倾向于从内部平滑对象的轮廓。开运算的一个关键性质是它是幂等的,这意味着多次应用该操作不会产生超出第一次应用的效果。
Figure 12:在小图像 上使用结构元素 进行开运算的示例(原点 位于中心,用蓝点表示)。
开运算是幂等运算,也就是说,应用两次相同的开运算与只应用一次的结果相同:
闭运算¶
闭运算是开运算的对偶。它包括先进行膨胀,然后进行腐蚀,同样使用相同的结构元素。
闭运算用于填充前景对象中的小孔,并通过填充它们之间的小间隙来连接附近的对象。它倾向于从外部平滑对象的轮廓。与开运算一样,闭运算也是幂等的。
Figure 13:在小图像 上使用结构元素 进行闭运算的示例(原点 位于中心,用蓝点表示)。
与开运算类似,闭运算是幂等运算:
import numpy as np
import matplotlib.pyplot as plt
from skimage.morphology import opening, closing, disk
# 创建一个带有噪声和间隙的示例二值图像
image = np.zeros((100, 100), dtype=np.uint8)
image[10:90, 20:30] = 1 # 一条垂直线
image[10:20, 20:80] = 1 # 一条水平线
image[45:55, 20:80] = 0 # 在垂直线中创建一个间隙
image[10, 70] = 1; image[12, 72] = 1; image[9, 68] = 1 # 添加一些噪声像素
# 在水平线中创建一个孔洞
image[12:18, 40:60] = 0
# 定义一个结构元素
selem = disk(3)
# 执行开运算和闭运算
opened_image = opening(image, selem)
closed_image = closing(image, selem)
# 绘图
fig, axes = plt.subplots(1, 3, figsize=(15, 7))
ax = axes.ravel()
ax[0].imshow(image, cmap='gray_r', interpolation='nearest')
ax[0].set_title('原始图像')
ax[1].imshow(opened_image, cmap='gray_r', interpolation='nearest')
ax[1].set_title('开运算图像')
ax[2].imshow(closed_image, cmap='gray_r', interpolation='nearest')
ax[2].set_title('闭运算图像')
for a in ax:
a.axis('off')
plt.show()





