• 【OpenCV实现平滑图像形态学变化】


    概要

    形态学变化是一组简单的图像操作,主要用于处理二值图像,即只包含黑和白两种颜色的图像。这些操作通常需要两个输入,原始图像和一个内核(kernel),内核的形状和大小决定了操作的性质。

    文章将首先介绍腐蚀和膨胀这两个基本的形态学算子。腐蚀操作通过内核在图像上滑动,将像素值置为内核覆盖区域内的最小值,用于消除图像中的小物体或者噪点。相反,膨胀操作将像素值置为内核覆盖区域内的最大值,常用于连接图像中的物体或者填充小的空洞。

    随后,文章将介绍其他常见的形态学算子,如开运算和闭运算。开运算是先进行腐蚀操作,再进行膨胀操作,常用于去除噪声和分离物体。闭运算则是先进行膨胀操作,再进行腐蚀操作,常用于填充小洞和连接物体

    目标

    不同的形态学运算例如腐蚀、膨胀、开运算、闭运算。
    不同的函数列如cv.erode()、cv.dilate()、cv.morphologyEx()等等。

    腐蚀

    是一种基本的形态学操作,其原理类似于自然界中的水土流失现象。在腐蚀操作中,一个内核(kernel)在图像上滑动,如果内核覆盖下的所有像素都为1(即白色,表示前景物体),那么中心像素点就会被赋值为1,否则被腐蚀掉(赋值为0)。

    这种操作导致了图像中前景物体的边界被侵蚀,保持前景物体为白色的同时,减小了其厚度或尺寸。换句话说,图像中的白色区域会逐渐减小。腐蚀操作在去除小白点噪声(例如图像中的小杂点)和分离连接在一起的对象等方面非常有效。通过选择合适的内核大小,可以调整腐蚀的程度,使其更加适应不同场景下的图像处理需求。

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    # 使用腐蚀操作,iterations参数表示腐蚀操作的次数
    erosion = cv.erode(img, kernel, iterations=1)
    
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示腐蚀后的图像
    cv.imshow('Eroded Image', erosion)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    腐蚀结果:
    在这里插入图片描述

    膨胀

    膨胀是一种形态学操作,与腐蚀相反。它的基本思想是通过内核的滑动,只要内核下的像素中有一个为1,中心像素就会被赋值为1(这类似于逻辑或运算)。膨胀操作会扩大白色物体(或前景物体)的区域或大小。在去除噪声时,通常会先进行腐蚀操作,然后再进行膨胀操作。这是因为腐蚀能够去除小的白色噪声,但同时也可能腐蚀掉我们需要保留的物体。膨胀操作的目的就是扩大物体,使其恢复到原始大小和形状。

    在膨胀操作中,噪声已经在腐蚀阶段被去除,因此在膨胀时不会再次引入噪声,但物体的大小和体积会得到恢复。此外,膨胀操作还对有破损或间断连接的物体部分进行恢复,使其更加完整。通过膨胀操作,可以使图像中的白色区域逐渐增大,从而更好地突出物体的特征。

    在这里插入图片描述

    开运算

    开运算只是先腐蚀后膨胀的另一个名称。正如我们上面所解释的,它对于消除噪音很有用。这里我们函数cv.morphologyEx()。

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    # 使用开运算,先腐蚀后膨胀,可以去除噪声并保持物体的整体形状
    opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
    
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示开运算后的图像
    cv.imshow('Opened Image', opening)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    闭运算
    闭运算和开运算相反,先膨胀后腐蚀。它对于关闭前景对象内的小孔或对象上的小黑点非常有用。

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    # 使用闭运算,先膨胀后腐蚀,可以填充前景物体内部的小孔,平滑物体的边界
    closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
    
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示闭运算后的图像
    cv.imshow('Closed Image', closing)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    形态梯度
    这是图像的膨胀和腐蚀之间做了一次差
    结果将看起来像只留下对象的轮廓。

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    # 使用形态梯度,通过膨胀和腐蚀的差别,突出物体的边缘
    gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
    
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示形态梯度后的图像
    cv.imshow('Gradient Image', gradient)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    顶帽
    它是输入图像和开运算图像之间的差。下面的示例是针对 9x9 内核完成的。

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)
    
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示形态梯度后的图像
    cv.imshow('Gradient Image', tophat)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    底帽
    它是闭运算图像和输入图像的差

    import cv2 as cv
    import numpy as np
    
    # 读取图像
    img = cv.imread('img.png', 0)
    
    # 创建一个 5x5 的内核(矩阵)
    kernel = np.ones((5, 5), np.uint8)
    
    blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)
    # 显示原始图像
    cv.imshow('Original Image', img)
    
    # 显示形态梯度后的图像
    cv.imshow('Gradient Image', blackhat)
    
    # 等待用户按下任意键后关闭窗口
    cv.waitKey(0)
    cv.destroyAllWindows()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在这里插入图片描述

    结构元素(内核)

    在形态学变换中,我们经常需要定义一个内核来指导图像处理。在前面的例子中,我们手动创建了一个矩形内核,但在实际应用中,可能需要不同形状和大小的内核。为了方便地获取这些内核,OpenCV提供了一个函数cv.getStructuringElement()。

    使用该函数,您只需要传递内核的形状和大小,就可以获得所需的内核。

    cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
    
    • 1

    如果需要一个椭圆形内核,可以使用以下代码:

    cv.getStructuringElement(cv.MORPH_ELLIPSE, (5, 5))
    
    
    • 1
    • 2

    类似地,如果需要一个十字形内核,可以使用

    cv.getStructuringElement(cv.MORPH_CROSS, (5, 5))
    
    • 1

    小结

    形态学变换是一种基于图像形状的简单而有效的处理方法,通常应用于二值图像(只包含黑白两种颜色)。在形态学变换中,我们使用内核(也称为结构化元素)来定义操作的性质和形状。

    腐蚀是一种形态学变换,它侵蚀了前景物体(白色区域)的边界,通常用于去除小白点噪声或分离连接的对象。使用cv.erode()函数,我们可以将内核滑动在图像上,将内核覆盖下的像素点都为1时,中心像素点就会被赋值为1,其他时候都为0,从而缩小白色区域。

    膨胀则与腐蚀相反,它会扩大白色物体的区域,常用于恢复连接对象的大小和形状。使用cv.dilate()函数,内核覆盖下的像素点只需有一个为1,中心像素点就会被赋值为1,从而扩大白色区域。

    开运算是先腐蚀后膨胀的组合操作,它可以去除噪声并保持物体的整体形状。闭运算则是先膨胀后腐蚀的组合操作,它可以填充前景物体内部的小孔,平滑物体的边界。这两种操作分别使用cv.morphologyEx()函数中的cv.MORPH_OPEN和cv.MORPH_CLOSE参数实现。

    为了方便地定义不同形状和大小的内核,OpenCV提供了cv.getStructuringElement()函数。通过传递内核的形状和大小参数,可以获得所需的内核。矩形、椭圆和十字形内核是常见的选择,可以根据具体任务的要求灵活选择合适的内核形状。

  • 相关阅读:
    OCI 发布了容器运行时和镜像规范!
    c# 类的介绍及延伸
    设计模式(4)-行为型模式
    MAX9295配置说明
    JMeter笔记9 | JMeter参数化
    Qt开发-QT Quick
    Sentry 是一个开源的错误监控和日志聚合平台-- 通过docker-compose 安装Sentry
    1067 Sort with Swap(0, i)
    原生js vue react通用的递归函数
    基于Java的勤工助学管理系统设计与实现(源码+lw+部署文档+讲解等)
  • 原文地址:https://blog.csdn.net/weixin_47869094/article/details/134083927