计算机视觉处理(OpenCV基础教学(五):形态学变换(腐蚀与膨胀))

OpenCV基础教学(五):形态学变换(腐蚀与膨胀)

本文将深入讲解形态学变换的基本原理,重点介绍腐蚀和膨胀两种核心操作,并通过Python+OpenCV代码实战演示它们在图像处理中的应用。


一、形态学变换概述

什么是形态学变换?

形态学变换是一种基于形状的图像处理技术,主要应用于二值图像。它通过移动一个称为"核"或"结构化元素"的小窗口,对图像进行局部操作,从而提取图像中有用的形状信息。

形态学变换的组成部分
  1. 输入图像:必须是二值图像(0和255)
  2. 核(结构化元素):一个小的二进制矩阵,定义了操作的邻域形状
  3. 操作算法:定义了如何将核应用于图像的规则
基本操作:腐蚀与膨胀
  • 腐蚀(Erosion):使物体边界收缩,去除小物体
  • 膨胀(Dilation):使物体边界扩张,填补空洞

二、腐蚀操作(Erosion)详解

腐蚀操作的数学原理

腐蚀操作的数学定义是集合论中的平移交集操作。对于图像A和核B,腐蚀定义为:

复制代码
A ⊖ B = {z | (B)_z ⊆ A}

其中(B)_z表示核B平移到位置z。

简单理解 :只有当核完全覆盖在物体内部时,中心点才保留为物体点。

核(结构化元素)的类型

OpenCV提供了三种基本的核类型:

python 复制代码
import cv2
import numpy as np

# 1. 矩形核(MORPH_RECT)
kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
print("矩形核(5×5):")
print(kernel_rect)

# 2. 椭圆形核(MORPH_ELLIPSE)
kernel_ellipse = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
print("\n椭圆形核(5×5):")
print(kernel_ellipse)

# 3. 十字形核(MORPH_CROSS)
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))
print("\n十字形核(5×5):")
print(kernel_cross)

核的形状对比

  • 矩形核:最简单的形状,适用于大多数场景
  • 椭圆形核:更平滑,适合处理圆形或椭圆形物体
  • 十字形核:各向异性,适合特定方向的操作
腐蚀操作的实际效果

腐蚀操作的主要作用:

  1. 去除小物体:孤立的小点或细线
  2. 分离物体:断开相连的物体
  3. 平滑边界:使边界更加整齐
  4. 缩小物体:整体尺寸减小

三、膨胀操作(Dilation)详解

膨胀操作的数学原理

膨胀操作的数学定义也是基于集合论:

复制代码
A ⊕ B = {z | (B)_z ∩ A ≠ ∅}

其中(B)_z表示核B平移到位置z。

简单理解 :只要核与物体有重叠,中心点就变为物体点。

核的大小与形状影响

核的大小直接影响膨胀的程度:

  • 小核:精细的膨胀,保持细节
  • 大核:强烈的膨胀,可能连接分离的物体
python 复制代码
# 不同大小的核对比
kernel_small = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
kernel_medium = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
kernel_large = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15))
膨胀操作的实际效果

膨胀操作的主要作用:

  1. 填补空洞:填充物体内部的孔洞
  2. 连接物体:将相邻但分离的物体连接起来
  3. 扩大物体:整体尺寸增大
  4. 平滑边界:填充凹陷部分

四、代码实战:腐蚀与膨胀

基础实现代码
python 复制代码
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 1. 读取并预处理图像
image_np = cv2.imread('./test.png')
image_gray = cv2.cvtColor(image_np, cv2.COLOR_BGR2GRAY)

# 2. 二值化图像
ret, image_thresh = cv2.threshold(image_gray, 127, 255, cv2.THRESH_BINARY)

# 3. 腐蚀操作
kernel_cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (9, 9))
image_erode = cv2.erode(image_thresh, kernel_cross)

# 4. 膨胀操作
kernel_rect = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
image_dilate = cv2.dilate(image_thresh, kernel_rect)
不同核类型的对比实验
python 复制代码
# 对比不同核类型的腐蚀效果
def compare_kernels_erosion(image, size=5):
    """对比不同核类型的腐蚀效果"""
    kernels = [
        ('MORPH_RECT', cv2.MORPH_RECT),
        ('MORPH_ELLIPSE', cv2.MORPH_ELLIPSE),
        ('MORPH_CROSS', cv2.MORPH_CROSS)
    ]
    
    plt.figure(figsize=(15, 5))
    
    plt.subplot(1, 4, 1)
    plt.imshow(image, cmap='gray')
    plt.title('Original Binary')
    plt.axis('off')
    
    for idx, (name, kernel_type) in enumerate(kernels, 2):
        kernel = cv2.getStructuringElement(kernel_type, (size, size))
        result = cv2.erode(image, kernel)
        
        plt.subplot(1, 4, idx)
        plt.imshow(result, cmap='gray')
        plt.title(f'Erosion: {name}')
        plt.axis('off')
    
    plt.tight_layout()
    plt.show()

# 对比不同核类型的膨胀效果
def compare_kernels_dilation(image, size=5):
    """对比不同核类型的膨胀效果"""
    kernels = [
        ('MORPH_RECT', cv2.MORPH_RECT),
        ('MORPH_ELLIPSE', cv2.MORPH_ELLIPSE),
        ('MORPH_CROSS', cv2.MORPH_CROSS)
    ]
    
    plt.figure(figsize=(15, 5))
    
    plt.subplot(1, 4, 1)
    plt.imshow(image, cmap='gray')
    plt.title('Original Binary')
    plt.axis('off')
    
    for idx, (name, kernel_type) in enumerate(kernels, 2):
        kernel = cv2.getStructuringElement(kernel_type, (size, size))
        result = cv2.dilate(image, kernel)
        
        plt.subplot(1, 4, idx)
        plt.imshow(result, cmap='gray')
        plt.title(f'Dilation: {name}')
        plt.axis('off')
    
    plt.tight_layout()
    plt.show()

# 运行对比实验
compare_kernels_erosion(image_thresh, 9)
compare_kernels_dilation(image_thresh, 7)
核大小对效果的影响
python 复制代码
# 分析核大小对腐蚀效果的影响
def erosion_size_analysis(image, sizes=[3, 5, 9, 15]):
    """分析核大小对腐蚀效果的影响"""
    plt.figure(figsize=(15, 10))
    
    plt.subplot(2, 3, 1)
    plt.imshow(image, cmap='gray')
    plt.title('Original Binary')
    plt.axis('off')
    
    for idx, size in enumerate(sizes, 2):
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size))
        result = cv2.erode(image, kernel)
        
        plt.subplot(2, 3, idx)
        plt.imshow(result, cmap='gray')
        plt.title(f'Erosion: {size}×{size}')
        plt.axis('off')
    
    plt.tight_layout()
    plt.show()

# 分析核大小对膨胀效果的影响
def dilation_size_analysis(image, sizes=[3, 5, 9, 15]):
    """分析核大小对膨胀效果的影响"""
    plt.figure(figsize=(15, 10))
    
    plt.subplot(2, 3, 1)
    plt.imshow(image, cmap='gray')
    plt.title('Original Binary')
    plt.axis('off')
    
    for idx, size in enumerate(sizes, 2):
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (size, size))
        result = cv2.dilate(image, kernel)
        
        plt.subplot(2, 3, idx)
        plt.imshow(result, cmap='gray')
        plt.title(f'Dilation: {size}×{size}')
        plt.axis('off')
    
    plt.tight_layout()
    plt.show()

# 运行分析
erosion_size_analysis(image_thresh)
dilation_size_analysis(image_thresh)
腐蚀与膨胀的联合应用
python 复制代码
# 腐蚀后膨胀(开运算)
def opening_operation(image, kernel_size=5):
    """开运算:先腐蚀后膨胀"""
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
    
    # 先腐蚀
    eroded = cv2.erode(image, kernel)
    # 再膨胀
    opened = cv2.dilate(eroded, kernel)
    
    return opened

# 膨胀后腐蚀(闭运算)
def closing_operation(image, kernel_size=5):
    """闭运算:先膨胀后腐蚀"""
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size))
    
    # 先膨胀
    dilated = cv2.dilate(image, kernel)
    # 再腐蚀
    closed = cv2.erode(dilated, kernel)
    
    return closed

# 应用示例
image_opened = opening_operation(image_thresh, 5)
image_closed = closing_operation(image_thresh, 5)

# 可视化对比
plt.figure(figsize=(15, 5))

plt.subplot(1, 4, 1)
plt.imshow(image_thresh, cmap='gray')
plt.title('Original Binary')
plt.axis('off')

plt.subplot(1, 4, 2)
plt.imshow(image_erode, cmap='gray')
plt.title('Erosion Only')
plt.axis('off')

plt.subplot(1, 4, 3)
plt.imshow(image_opened, cmap='gray')
plt.title('Opening (Erode→Dilate)')
plt.axis('off')

plt.subplot(1, 4, 4)
plt.imshow(image_closed, cmap='gray')
plt.title('Closing (Dilate→Erode)')
plt.axis('off')

plt.tight_layout()
plt.show()

五、高级形态学操作

开运算(Opening)

定义 :先腐蚀后膨胀
公式:Opening(A) = Dilation(Erosion(A))

作用

  1. 去除小物体(噪声)
  2. 平滑物体边界
  3. 断开细的连接
  4. 保留原始大小大致不变

代码实现

python 复制代码
# 使用OpenCV内置函数
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
opened = cv2.morphologyEx(image_thresh, cv2.MORPH_OPEN, kernel)
闭运算(Closing)

定义 :先膨胀后腐蚀
公式:Closing(A) = Erosion(Dilation(A))

作用

  1. 填补物体内部孔洞
  2. 连接邻近物体
  3. 平滑边界
  4. 保留原始大小大致不变

代码实现

python 复制代码
# 使用OpenCV内置函数
closed = cv2.morphologyEx(image_thresh, cv2.MORPH_CLOSE, kernel)
形态学梯度

定义 :膨胀结果减去腐蚀结果
公式:Gradient(A) = Dilation(A) - Erosion(A)

作用:提取物体边界

代码实现

python 复制代码
# 形态学梯度
gradient = cv2.morphologyEx(image_thresh, cv2.MORPH_GRADIENT, kernel)

# 可视化所有高级操作
plt.figure(figsize=(15, 10))

operations = [
    ('Original', image_thresh),
    ('Erosion', cv2.erode(image_thresh, kernel)),
    ('Dilation', cv2.dilate(image_thresh, kernel)),
    ('Opening', cv2.morphologyEx(image_thresh, cv2.MORPH_OPEN, kernel)),
    ('Closing', cv2.morphologyEx(image_thresh, cv2.MORPH_CLOSE, kernel)),
    ('Gradient', cv2.morphologyEx(image_thresh, cv2.MORPH_GRADIENT, kernel))
]

for idx, (name, img) in enumerate(operations, 1):
    plt.subplot(2, 3, idx)
    plt.imshow(img, cmap='gray')
    plt.title(name)
    plt.axis('off')

plt.tight_layout()
plt.show()

六、总结与应用建议

如何选择合适的形态学操作?
问题 推荐操作 参数建议
去除小噪声点 开运算 小核(3×3或5×5)
填补内部孔洞 闭运算 小到中型核
分离相连物体 腐蚀 根据连接处大小选择核
连接分离物体 膨胀 根据间隙大小选择核
提取物体边界 形态学梯度 小核(3×3)
平滑物体边界 先开运算后闭运算 小到中型核
实际应用场景分析
  1. 医学图像处理
python 复制代码
def medical_image_processing(image_path):
    """医学图像处理:提取细胞边界"""
    image = cv2.imread(image_path)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # 二值化
    _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    
    # 去除小噪声
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
    cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    
    # 提取边界
    gradient = cv2.morphologyEx(cleaned, cv2.MORPH_GRADIENT, kernel)
    
    return binary, cleaned, gradient
  1. 车牌字符分割
python 复制代码
def license_plate_segmentation(plate_image):
    """车牌字符分割"""
    # 转换为灰度并二值化
    gray = cv2.cvtColor(plate_image, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
    
    # 膨胀连接字符内部的断点
    kernel_horizontal = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 1))
    dilated = cv2.dilate(binary, kernel_horizontal)
    
    # 腐蚀分离字符
    kernel_vertical = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 5))
    eroded = cv2.erode(dilated, kernel_vertical)
    
    return binary, dilated, eroded
  1. 工业缺陷检测
python 复制代码
def defect_detection(product_image):
    """工业产品缺陷检测"""
    gray = cv2.cvtColor(product_image, cv2.COLOR_BGR2GRAY)
    
    # 使用自适应阈值处理光照不均
    binary = cv2.adaptiveThreshold(gray, 255, 
                                   cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                   cv2.THRESH_BINARY_INV, 11, 2)
    
    # 去除小噪声
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    
    # 膨胀连接缺陷区域
    dilated = cv2.dilate(opened, kernel, iterations=2)
    
    return binary, opened, dilated

📌 下期预告

下一期我们将学习图像的颜色识别


扩展思考

  1. 如何设计自适应核来适应不同形状的物体?
  2. 形态学操作在彩色图像上如何应用?
  3. 如何结合深度学习和形态学操作?

欢迎在评论区分享你的实践经验和遇到的问题!


如果觉得本文有帮助,欢迎点赞、收藏、关注!
你的支持是我持续创作的最大动力!

相关推荐
晚霞的不甘11 小时前
CANN 编译器深度解析:TBE 自定义算子开发实战
人工智能·架构·开源·音视频
愚公搬代码11 小时前
【愚公系列】《AI短视频创作一本通》016-AI短视频的生成(AI短视频运镜方法)
人工智能·音视频
哈__11 小时前
CANN内存管理与资源优化
人工智能·pytorch
极新11 小时前
智启新篇,智创未来,“2026智造新IP:AI驱动品牌增长新周期”峰会暨北京电子商务协会第五届第三次会员代表大会成功举办
人工智能·网络协议·tcp/ip
island131411 小时前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构任务的 Stream 调度机制
开发语言·人工智能·深度学习·神经网络
艾莉丝努力练剑11 小时前
深度学习视觉任务:如何基于ops-cv定制图像预处理流程
人工智能·深度学习
禁默11 小时前
大模型推理的“氮气加速系统”:全景解读 Ascend Transformer Boost (ATB)
人工智能·深度学习·transformer·cann
User_芊芊君子11 小时前
CANN大模型加速核心ops-transformer全面解析:Transformer架构算子的高性能实现与优化
人工智能·深度学习·transformer
格林威11 小时前
Baumer相机玻璃制品裂纹自动检测:提高透明材质检测精度的 6 个关键步骤,附 OpenCV+Halcon 实战代码!
人工智能·opencv·视觉检测·材质·工业相机·sdk开发·堡盟相机
点云SLAM11 小时前
Concentrate 英文单词学习
人工智能·英文单词学习·雅思备考·concentrate·集中·浓缩 / 集中物