「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战」
形态变换简介
形态变换( Morphological transformations )通常是在二值图像上执行、基于图像形状的操作。其具体的操作由核结构元素决定,它决定了操作的性质。膨胀和腐蚀是形态变换领域的两个基本算子。此外,开运算和闭运算是两个重要的运算,它们可以通过上述两个运算(膨胀和腐蚀)获得。最后,还有其他三个常用的变换操作,是基于上述操作的变体或结合。
膨胀运算与腐蚀运算
二值图像的膨胀运算的主要作用是逐渐扩大前景对象的边界区域。这意味着前景对象的区域会变大,而这些区域内的孔会缩小:
dilation = cv2.dilate(image, kernel, iterations=1)
腐蚀操作对二值图像的主要作用是逐渐侵蚀掉前景对象的边界区域。这意味着前景对象的区域会变小,而这些区域内的空洞会变大:
erosion = cv2.erode(image, kernel, iterations=1)
接下来,使用膨胀运算与腐蚀运算:
image_names = ['test1.png', 'test2.png', 'test3.png']
path = 'morpho_test_imgs'
kernel_size_3_3 = (3, 3)
kernel_size_5_5 = (5, 5)
def load_all_test_images():
test_morph_images = []
for index_image, name_image in enumerate(image_names):
image_path = os.path.join(path, name_image)
test_morph_images.append(cv2.imread(image_path))
return test_morph_images
def show_with_matplotlib(color_img, title, pos):
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(3, 3, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
def erode(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
erosion = cv2.erode(image, kernel, iterations=1)
return erosion
def dilate(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
dilation = cv2.dilate(image, kernel, iterations=1)
return dilation
test_images = load_all_test_images()
for index_image,image in enumerate(test_images):
show_with_matplotlib(image, 'test img_{}'.format(index_image + 1), index_image * 3 + 1)
img_1 = erode(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_1, 'erode_{}'.format(index_image + 1), index_image * 3 + 2)
img_2 = dilate(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_2, 'dilate_{}'.format(index_image + 1), index_image * 3 + 3)
开运算与闭运算
开运算先执行腐蚀,然后使用相同的结构元素(或核)进行膨胀。通过这种方式,可以应用腐蚀来消除一小组不需要的像素(例如,椒盐噪声)。
腐蚀会不分青红皂白地影响图像的所有区域。通过在腐蚀后执行扩张操作,可以减少腐蚀过度的一些影响:
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
闭预算同样也可以从腐蚀和膨胀操作中推导出来,该操作先执行膨胀,然后执行腐蚀。膨胀操作通常用于填充图像中的小孔。然而,膨胀操作也会使一小群噪声像素变大。通过在膨胀后对图像应用腐蚀操作,将减少膨胀带来的这种影响:
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
接下来,实际使用开运算与闭运算:
# build_kernel() 和 show_with_matplotlib() 函数与4.1中相同
def closing(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
clos = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
return clos
def opening(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
ope = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
return ope
for index_image,image in enumerate(test_images):
show_with_matplotlib(image, 'test img_{}'.format(index_image + 1), index_image * 3 + 1)
img_1 = closing(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_1, 'closing_{}'.format(index_image + 1), index_image * 3 + 2)
img_2 = opening(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_2, 'opening_{}'.format(index_image + 1), index_image * 3 + 3)
plt.show()
形态梯度运算
形态梯度运算定义为输入图像的膨胀和腐蚀之间的差异:
morph_gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
形态梯度运算的用法:
# build_kernel() 函数与4.1中相同
def show_with_matplotlib(color_img, title, pos):
img_RGB = color_img[:, :, ::-1]
ax = plt.subplot(2, 3, pos)
plt.imshow(img_RGB)
plt.title(title, fontsize=8)
plt.axis('off')
def morphological_gradient(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
morph_gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
return morph_gradient
for index_image,image in enumerate(test_images):
print(index_image)
show_with_matplotlib(image, 'test img_{}'.format(index_image + 1), index_image + 1)
img = morphological_gradient(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img, 'gradient_{}'.format(index_image + 1), index_image + 4)
顶帽运算与低帽(黑帽)运算
顶帽运算被定义为输入图像和图像开运算之间的差:
top_hat = cv2.morphologyEx(image, cv2.MORPH_TOPHAT, kernel)
黑帽操作被定义为输入图像和输入图像闭运算的差:
black_hat = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)
顶帽运算与低帽运算的用法:
# build_kernel() 和 show_with_matplotlib() 函数与4.1中相同
def black_hat(image, kernel_type, kernel_size):
kernel = build_kernel(kernel_type, kernel_size)
black = cv2.morphologyEx(image, cv2.MORPH_BLACKHAT, kernel)
return black
def opening_and_closing(image, kernel_type, kernel_size):
opening_img = opening(image, kernel_type, kernel_size)
closing_img = closing(opening_img, kernel_type, kernel_size)
return closing_img
for index_image,image in enumerate(test_images):
show_with_matplotlib(image, 'test img_{}'.format(index_image + 1), index_image * 3 + 1)
img_1 = top_hat(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_1, 'top_hat_{}'.format(index_image + 1), index_image * 3 + 2)
img_2 = black_hat(image, cv2.MORPH_RECT, (3,3))
show_with_matplotlib(img_2, 'black_hat_{}'.format(index_image + 1), index_image * 3 + 3)
plt.show()
结构元素
OpenCV 提供了 cv2.getStructuringElement() 函数用于构造结构元素。此函数输出所需的核( uint8 类型的 NumPy 数组),该函数接收两个参数——核的形状和大小。 OpenCV 中提供了以下三种核形状:
| 核形状 | 说明 |
|---|---|
| cv2.MORPH_RECT | 矩形核 |
| cv2.MORPH_ELLIPSE | 椭圆核 |
| cv2.MORPH_CROSS | 十字形核 |
应用形态变换
可以使用不同的核大小和形状、形态变换和图像。测试不同的核形状和大小。例如,下图是使用核大小 (3, 3) 的矩形核 (cv2.MORPH_RECT) 时的输出:
使用核大小 (5, 5) 的矩形核 (cv2.MORPH_RECT) 时的输出:
使用核大小 (3, 3) 的十字形核 (cv2.MORPH_CROSS) 时的输出:
使用核大小 (5, 5) 的十字形核 (cv2.MORPH_CROSS) 时的输出:
在预处理图像时,形态学操作是一种非常有用的技术,可以使用形态学操作消除一些干扰图像正确处理的噪声,或者处理图像结构中的缺陷。