Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

数学形态学

数学形态学汇集了基于集合论、格论和拓扑学的几种技术。 它最初专门用于二值图像,但现在已扩展到灰度图像。 在本课程中,我们将演示限制在二值图像上, 因此我们认为图像中的对象定义了一个像素集合。

集合运算

考虑以下两个集合 AABB

集合 AA

集合 BB

然后我们有以下运算。

补集
并集
交集
差集

AA补集记作 AcA^\mathrm{c},是不在 AA 中的像素集合:

Ac={pA}A^\mathrm{c} = \{p \notin A \}
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),也称为核。结构元素是一个小的二值图像——一个形状——用来探测主图像。它定义了每个操作要考虑的像素邻域。

结构元素 EE 是一个具有定义原点(或锚点)的像素集合。原点是结构元素上用于在图像上定位的点。虽然原点通常是结构元素的中心,但它可以是SE前景区域内甚至外部的任何像素。这允许在形态学操作中进行偏移。在下面的文本中,我们用 EcE_c 表示原点位于主图像中像素 cc 处的结构元素。

结构元素的大小和形状至关重要,因为它们决定了形态学操作的效果。常见的形状包括正方形、圆盘和十字形。结构元素的选择应该以图像中对象的几何形状为指导。例如,圆形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:显示腐蚀过程的动画,展示结构元素在图像上逐步移动的过程。

基本运算

膨胀

膨胀是一种形态学运算,用于扩大或加粗图像的前景区域。直观效果是它会增长对象的边界。

形式上,图像 II 经过结构元素 EE 的膨胀是所有像素位置 cc 的集合,其中 EE 的反射平移到 cc 后与 II 有非空交集。设 E^\hat{E}EE 关于其原点的反射。则膨胀定义为:

IE={cE^cI}I \oplus E = \{ c \mid \hat{E}_c \cap I \neq \emptyset \}

这意味着,如果将结构元素的原点放置在某个像素上时,它与输入图像中的至少一个前景像素重叠,则输出图像中的该像素被设置为1。此过程有效地扩展了图像中的对象。膨胀对于填充小间隙和连接不相交的对象很有用。

在小图像 I 上使用结构元素 E_c 进行膨胀的示例(原点 c 位于中心,用蓝点表示)。

Figure 10:在小图像 II 上使用结构元素 EcE_c 进行膨胀的示例(原点 cc 位于中心,用蓝点表示)。

结构元素通常用矩阵描述。因此,Figure 10 中的结构元素写作:

E=(010111010).E = \begin{pmatrix} 0 & 1 & 0 \\ 1 & 1 & 1 \\ 0 & 1 & 0 \\ \end{pmatrix}.

注意,该矩阵不考虑围绕 EE 主体的零像素。

膨胀具有以下性质:

  • 膨胀是二元运算,且不是线性的。因此,它不能表示为线性的数学算子卷积。

  • 膨胀是可结合的,即两个连续膨胀的应用可以按任何顺序进行:

    (IE1)E2=(IE2)E1=I(E1E2)(I \oplus E_1 ) \oplus E_2 = (I \oplus E_2) \oplus E_1 = I \oplus (E_1 \oplus E_2)

    (这里,下标1和2表示两个不同的结构元素。)

  • 膨胀是单调运算,因为包含关系被保留:

    I1I2I1EcI2EcI_1 \subseteq I_2 \quad\Rightarrow\quad I_1 \oplus E_c \subseteq I_2 \oplus E_c

腐蚀

腐蚀是膨胀的对偶运算。它收缩或细化图像的前景区域,有效地腐蚀对象的边界。

图像 II 经过结构元素 EE 的腐蚀是所有像素位置 cc 的集合,其中平移到 cc 的结构元素 EE 完全包含在 II 中。

IE={cEcI}I \ominus E = \{ c \mid E_c \subseteq I\}

只有当结构元素在某个像素居中时完全位于输入图像的前景内,输出图像中的该像素才被设置为1。此操作可移除小对象、平滑对象边界并加宽对象之间的间隙。

在小图像 I 上使用结构元素 E_c 进行腐蚀的示例(原点 c 位于中心,用蓝点表示)。

Figure 11:在小图像 II 上使用结构元素 EcE_c 进行腐蚀的示例(原点 cc 位于中心,用蓝点表示)。

腐蚀具有与膨胀相似的性质:

  • 腐蚀不能表示为卷积。

  • 腐蚀是可结合的:

    (IE1)E2=(IE2)E1=I(E1E2)(I \ominus E_1 ) \ominus E_2 = (I \ominus E_2) \ominus E_1 = I \ominus (E_1 \oplus E_2)

    注意,两次连续腐蚀的结果等效于一次腐蚀,其结构元素是前两个结构元素的膨胀

  • 腐蚀是单调运算:

    I1I2I1EI2EI_1 \subseteq I_2 \quad\Rightarrow\quad I_1 \ominus E \subseteq I_2 \ominus E

对偶性

膨胀和腐蚀是对偶算子。 将背景视为主体,主体视为背景(即通过处理图像的补集),膨胀会转换成腐蚀,反之亦然:

IcE=(IE)cIcE=(IE)cI^\mathrm{c} \ominus E = (I \oplus E)^\mathrm{c} \\ I^\mathrm{c} \oplus E = (I \ominus E)^\mathrm{c}
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()

基本算子的组合

开运算

运算是另一个基本的形态学过程。它被定义为先进行腐蚀,然后进行膨胀,两次操作使用相同的结构元素。

开运算的主要效果是移除小的前景对象(通常被认为是噪声),并断开较大对象之间的细微连接或狭窄部分。它倾向于从内部平滑对象的轮廓。开运算的一个关键性质是它是幂等的,这意味着多次应用该操作不会产生超出第一次应用的效果。

IE=(IE)EI \circ E = (I \ominus E) \oplus E
在小图像 I 上使用结构元素 E_c 进行开运算的示例(原点 c 位于中心,用蓝点表示)。

Figure 12:在小图像 II 上使用结构元素 EcE_c 进行开运算的示例(原点 cc 位于中心,用蓝点表示)。

开运算是幂等运算,也就是说,应用两次相同的开运算与只应用一次的结果相同:

(IE)E=IE(I \circ E) \circ E = I \circ E

闭运算

运算是开运算的对偶。它包括先进行膨胀,然后进行腐蚀,同样使用相同的结构元素。

闭运算用于填充前景对象中的小孔,并通过填充它们之间的小间隙来连接附近的对象。它倾向于从外部平滑对象的轮廓。与开运算一样,闭运算也是幂等的。

IE=(IE)EI \bullet E = (I \oplus E) \ominus E
在小图像 I 上使用结构元素 E_c 进行闭运算的示例(原点 c 位于中心,用蓝点表示)。

Figure 13:在小图像 II 上使用结构元素 EcE_c 进行闭运算的示例(原点 cc 位于中心,用蓝点表示)。

与开运算类似,闭运算是幂等运算:

(IE)E=IE(I \bullet E) \bullet E = I \bullet E
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()
🤖