OpenCV形态变换详解

935 阅读4分钟

「这是我参与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) 时的输出:

十字形核

在预处理图像时,形态学操作是一种非常有用的技术,可以使用形态学操作消除一些干扰图像正确处理的噪声,或者处理图像结构中的缺陷。

相关链接

OpenCV基本图像运算