OpenCV 形态学转换

图像形态学是根据图像的形状进行的操作,它可以从图像中提取出用于表示图像形状最基本的信息,使得计算机能够更好的理解和识别图像信息。

形态学操作一般针对的是二值图像进行操作。这里简单说下二值图、灰度图、彩色图的区别:

  1. 二值图:图像中的每个像素值为 0 或者 255,即:每个像素点要不是 0 、要不是 255
  2. 彩色图:由 R、G、B 三原色组成的图像。即:有 3 个通道,每个通道像素值的取值范围为 0~255
  3. 灰度图:灰度图只有 1 个通道,每个像素的取值范围为 0 ~ 255

下面是是一段生成三种图像的示例代码:

import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':

    # 固定随机数种子
    np.random.seed(0)

    # 生成图像尺寸
    size = (16, 16)

    # 1. 二值图
    image1 = np.random.choice([0, 255], size=size)

    # 2. 灰度图
    image2 = np.random.randint(0, 256, size=size)

    # 3. 彩色图
    size += (3,)  # 通道数
    image3 = np.random.randint(0, 256, size=size)

    # imshow 函数默认显示 RGB(A) 彩色图,通过指定 cmap='gray' 显示灰度图
    for index, (image, title) in enumerate([(image1, '二值图'), (image2, '灰度图'), (image3, '彩色图')]):
        plt.subplot(131 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')

    plt.show()

程序输出结果:

灰度图和二值图看起来都是黑白图,不同的是灰度图的每个像素点是有明亮变化的。

1. 腐蚀和膨胀

腐蚀: 卷积核对应的原图像的像素值中全部是 1,则中心元素的像素值就是 1,否则为 0。这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。

膨胀: 卷积核对应的原图像的像素值中只要有一个是 1,则中心元素的像素值就是 1,否则为 0。腐蚀去掉白噪声的同时,也会使前景对象变小。膨胀使前景对象变大,它也可以用来连接两个分开的物体。

import numpy as np
import matplotlib.pyplot as plt
import cv2


def test():

    np.random.seed(0)

    images = []

    # 读取图像
    orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
    images.append((orign_image, "原图"))

    # 添加噪声
    h, w = orign_image.shape
    noise_num = 200000
    hp = np.random.randint(0, h, size=(noise_num,))
    wp = np.random.randint(0, w, size=(noise_num,))
    noise_image = np.copy(orign_image)
    for n1, n2 in zip(hp, wp):
        noise_image[n1][n2] = 255
    images.append((noise_image, "噪声"))

    # 腐蚀操作
    # kernel size 越大腐蚀程度越强
    kernel = np.ones((5, 5), np.int8)
    # iterations 表示重复多少次腐蚀或者膨胀
    erode_image = cv2.erode(noise_image, kernel, iterations=2)
    images.append((erode_image, "腐蚀"))

    # 膨胀操作
    dilate_image = cv2.dilate(erode_image, kernel, iterations=2)
    images.append((dilate_image, "膨胀"))

    # 图像显示
    for index, (image, title) in enumerate(images):
        plt.subplot(141 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')
    plt.show()


if __name__ == '__main__':
    test()

程序输出结果:

2. 开闭运算

开运算:先腐蚀再膨胀,它可以被用来去除噪声。例如上面的例子一样,先腐蚀之后去除了噪声,但是对象会变小,再进行膨胀,增大对象,从而去除噪声。

闭运算:先膨胀再腐蚀。它可以被用来填充前景物体中的小洞,或者前景物体上的小黑点。下面的例子演示下这个效果。

import numpy as np
import matplotlib.pyplot as plt
import cv2


def test():

    np.random.seed(0)
    images = []

    # 读取图像
    orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
    images.append((orign_image, "原图"))

    # 添加噪声
    h, w = orign_image.shape
    hp, wp = range(0, h), range(0, w)
    noise_image = np.copy(orign_image)
    for x in hp:
        for y in wp:
            if noise_image[x][y] == 255:
                if np.random.randint(1, 11) <= 2:
                    noise_image[x][y] = 0
    images.append((noise_image, "噪声"))

    # 闭运算
    kernel = np.ones((5, 5), np.int8)
    # op=cv2.MORPH_OPEN 参数指定开运算
    # op=cv2.MORPH_CLOSE 参数指定闭运算
    open_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_CLOSE, kernel=kernel, iterations=2)
    images.append((open_image, "闭运算"))

    # 图像显示
    for index, (image, title) in enumerate(images):
        plt.subplot(131 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')
    plt.show()


if __name__ == '__main__':
    test()

程序执行结果:

从这里可以看到,开运算可以去除背景中的噪声,闭运算则可以去除前景中的噪声。

3. 形态学梯度

形态学梯度:图像膨胀与腐蚀的差,结果看上去就像前景物体的轮廓。梯度用于描述目标边界灰度剧烈变化的区域,突出高亮区域的边界。

import cv2
import numpy as np
import matplotlib.pyplot as plt


def test():

    images = []
    image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
    images.append((image, '原图'))
    kernel = np.ones(shape=(5, 5))
    image = cv2.morphologyEx(image, op=cv2.MORPH_GRADIENT, kernel=kernel, iterations=1)
    images.append((image, '梯度'))

    # 图像显示
    for index, (image, title) in enumerate(images):
        plt.subplot(121 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')
    plt.show()


if __name__ == '__main__':
    test()

程序输出结果:

4. 顶帽和黑帽

顶帽:原图与开运算之后得到的差,用于显示开运算忽略的图像信息。

黑帽:原图与闭运算之后得到的差,用于显示闭运算忽略的图像信息。

4.1 顶帽示例

import numpy as np
import matplotlib.pyplot as plt
import cv2


def test():

    np.random.seed(0)
    images = []

    # 读取图像
    orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)

    # 添加噪声
    h, w = orign_image.shape
    noise_num = 150000
    hp = np.random.randint(0, h, size=(noise_num,))
    wp = np.random.randint(0, w, size=(noise_num,))
    noise_image = np.copy(orign_image)
    for n1, n2 in zip(hp, wp):
        noise_image[n1][n2] = 255
    images.append((noise_image, "原图"))

    # 开运算
    kernel = np.ones((5, 5), np.int8)
    open_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_OPEN, kernel=kernel, iterations=2)
    images.append((open_image, "开运算"))

    # 顶帽
    tophat_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_TOPHAT, kernel=kernel, iterations=2)
    images.append((tophat_image, "顶帽"))

    # 图像显示
    for index, (image, title) in enumerate(images):
        plt.subplot(131 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')
    plt.show()


if __name__ == '__main__':
    test()

程序执行结果:

4.2 黑帽示例

import numpy as np
import matplotlib.pyplot as plt
import cv2


def test():

    np.random.seed(0)
    images = []

    # 读取图像
    orign_image = cv2.imread('demo.png', cv2.IMREAD_GRAYSCALE)
    # 添加噪声
    h, w = orign_image.shape
    hp, wp = range(0, h), range(0, w)
    noise_image = np.copy(orign_image)
    for x in hp:
        for y in wp:
            if noise_image[x][y] == 255:
                if np.random.randint(1, 11) <= 2:
                    noise_image[x][y] = 0
    images.append((noise_image, "原图"))

    # 闭运算
    kernel = np.ones((5, 5), np.int8)
    close_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_CLOSE, kernel=kernel, iterations=2)
    images.append((close_image, "闭运算"))

    # 黑帽
    black_image = cv2.morphologyEx(noise_image, op=cv2.MORPH_BLACKHAT, kernel=kernel, iterations=2)
    images.append((black_image, "黑帽"))

    # 图像显示
    for index, (image, title) in enumerate(images):
        plt.subplot(131 + index)
        plt.imshow(image, cmap='gray')
        plt.title(title)
        plt.axis('off')
    plt.show()


if __name__ == '__main__':
    test()

程序执行结果:

未经允许不得转载:一亩三分地 » OpenCV 形态学转换
评论 (0)

6 + 4 =