实验5 基于Python的数字图像形态学操作和图像分割

在这里插入图片描述

一、实验目的

 掌握二值图像、灰度图像进行形态学操作的原理和实现方法。
 掌握灰度图像进行形态学操作的原理和实现方法。
 掌握数字图像处理关于图像分割的概念、分类以及基本内容。
 掌握常见基于边缘的图像分割算法和实现(包括一二阶微分算子法和非积分算子法)。
 掌握常见的基于区域的图像分割算法和实现。
 掌握使用形态学操作法提取图像边缘进行分割的方法和实现。

二、实验内容

  1. 读取一幅二值图像并显示,分别使用3*3大小的十字型、圆形和矩形结构元素,分别进行膨胀、腐蚀、开运算、闭运算,并显示形态学操作后的图像。分析不同类型结构元素对图像形态学操作的特点。
  2. 读取一幅灰度图像并显示,使用5*5大小的矩形结构元素,分别进行膨胀、腐蚀、开运算、闭运算,并显示形态学操作后的图像。分析不同类型结构元素对图像形态学操作的特点。
  3. 读取一幅灰度图像并进行显示,对canny算子实现对图像的分割算法,并通过调整sobel算子核的大小(33,55),对比canny算子进行图像分割的效果。
  4. 读取并显示一幅灰度图像,对其采用分水岭算法进行图像边缘提取。
  5. 案例操作题:腐蚀、膨胀运算除了能去除细小区域、填充空洞外,还能够提取目标物体的边缘轮廓。本案例活用图像形态学,采用腐蚀、膨胀运算和图像差分运算提取建筑物的边缘。
    本案例采用的数据是一张建筑物的图片building.jpg,请写出问题分析思路,并编程实现。

三、完整实验程序(截图)、结果(截图)与分析

1. 代码

在这里插入图片描述
在这里插入图片描述

结果
在这里插入图片描述

分析不同类型结构元素对图像形态学操作的特点。
矩形结构元素:各向同性,膨胀/腐蚀均匀扩展或收缩边界。
十字结构元素:强调水平和垂直方向,对角边缘处理较弱。
圆形结构元素(实际为椭圆):与十字类似,但若结构不同,可能更平滑。
在这里插入图片描述

2. 代码

在这里插入图片描述
在这里插入图片描述

结果
在这里插入图片描述

分析不同类型结构元素对图像形态学操作的特点。
在图像形态学处理中,结构元素(Structuring Element) 的形状直接影响操作的局部作用方式,进而决定对图像特征的保留、去除或增强效果。

  1. 矩形结构元素(各向同性):膨胀/腐蚀均匀影响所有方向,适合处理方形特征。
  2. 十字结构元素(各向异性):主要影响水平和垂直方向,对角方向变化较小,适合处理十字形结构。
  3. 圆形结构元素(各向同性):均匀作用于所有方向,能平滑边缘并保持圆形特征。

开运算消除小物体时,圆形结构元素更有效;闭运算填充孔洞时,矩形结构元素更均匀。

3. 代码

在这里插入图片描述

结果
在这里插入图片描述

对比canny算子进行图像分割的效果。
Canny算子的Sobel核大小直接影响边缘检测的 细节保留 与 抗噪性:
3×3核:高精度、低抗噪,适合细节丰富的干净图像。
5×5核:低精度、高抗噪,适合噪声较多的复杂场景。
实际应用中需根据图像特点权衡选择,必要时可结合形态学操作提升分割效果。

4. 代码

在这里插入图片描述

结果
在这里插入图片描述

分水岭算法能有效处理物体接触或重叠的情况,提取连续的闭合边缘。对噪声敏感,需依赖预处理减少过分割。

5. 代码

在这里插入图片描述

结果
在这里插入图片描述

腐蚀差分:提取外边缘。
膨胀差分:提取内边缘。
结构元素影响:矩形结构元素产生均匀边缘,调整大小可控制边缘厚度。

四、出现的问题及心得

我在形态学操作中因结构元素选择不当导致边缘提取不完整,需反复调整核形状与大小以平衡细节保留与抗噪性;Canny算子参数(Sobel核尺寸、双阈值)的调试耗时较长,发现3×3核虽细节丰富但噪声敏感,5×5核抗噪性强却牺牲了细小边缘;分水岭算法因预处理不足,二值化阈值偏差引发过分割,后通过优化距离变换阈值与标记生成解决。深刻体会到形态学与边缘检测算法对参数的高度敏感性,需结合图像特性动态调整,同时预处理的质量直接决定后续分割效果。通过实践,巩固了OpenCV函数的使用逻辑,并认识到理论理解与实验验证相结合的重要性,尤其在复杂场景下需灵活组合多种算法形态学+分水岭以提升鲁棒性。

完整代码

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

# 读取二值图像
img = cv2.imread("D:/tuxiang/IMG_20250423_181624.png", 0)
_, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)

# 创建结构元素
kernels = [
    ('Cross', cv2.MORPH_CROSS, (3, 3)),
    ('Ellipse', cv2.MORPH_ELLIPSE, (3, 3)),
    ('Rect', cv2.MORPH_RECT, (3, 3))
]

plt.figure(figsize=(15, 10))
plt.subplot(4, 4, 1)
plt.imshow(binary, cmap='gray')
plt.title('Original')

for idx, (name, shape, size) in enumerate(kernels):
    kernel = cv2.getStructuringElement(shape, size)

    # 膨胀
    dilation = cv2.dilate(binary, kernel)
    plt.subplot(4, 4, idx * 4 + 2)
    plt.imshow(dilation, cmap='gray')
    plt.title(f'{name} Dilation')

    # 腐蚀
    erosion = cv2.erode(binary, kernel)
    plt.subplot(4, 4, idx * 4 + 3)
    plt.imshow(erosion, cmap='gray')
    plt.title(f'{name} Erosion')

    # 开运算
    opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    plt.subplot(4, 4, idx * 4 + 4)
    plt.imshow(opening, cmap='gray')
    plt.title(f'{name} Opening')

    # 闭运算
    closing = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
    plt.subplot(4, 4, idx * 4 + 5)
    plt.imshow(closing, cmap='gray')
    plt.title(f'{name} Closing')

plt.tight_layout()
plt.show()
"""
2.
"""

# 读取灰度图像
image = cv2.imread("D:/tuxiang/cat.jpg", cv2.IMREAD_GRAYSCALE)
if image is None:
    raise ValueError("Image not found or path is incorrect")

# 创建5x5结构元素
kernels = [
    (cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))),   # 矩形
    (cv2.getStructuringElement(cv2.MORPH_CROSS, (5, 5))), # 十字形
    (cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))) # 圆形(椭圆近似)
     ]
kernel_names = ['Rectangle', 'Cross', 'Circle']

# 形态学操作
morph_ops = [
    ('Erosion', cv2.erode),
    ('Dilation', cv2.dilate),
    ('Opening', lambda img, kernel: cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)),
    ('Closing', lambda img, kernel: cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel))
]

# 显示设置
plt.figure(figsize=(20, 16))
plt.subplot(4, 4, 1)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')

# 应用形态学操作并显示结果
for row, (kernel, name) in enumerate(zip(kernels, kernel_names), start=1):
    for col, (op_name, op_func) in enumerate(morph_ops, start=1):
        result = op_func(image, kernel)
        plt.subplot(4, 4, (row)*4 + col)
        plt.imshow(result, cmap='gray')
        plt.title(f'{name} {op_name}')
        plt.axis('off')

plt.tight_layout()
plt.show()
"""
3.
"""
img = cv2.imread("D:/tuxiang/nan.jpg", 0)

# Canny边缘检测(不同Sobel核大小)
edges_3x3 = cv2.Canny(img, 100, 200, apertureSize=3)
edges_5x5 = cv2.Canny(img, 100, 200, apertureSize=5)

# 显示结果
plt.figure(figsize=(10, 4))
plt.subplot(131), plt.imshow(img, cmap='gray'), plt.title('Original')
plt.subplot(132), plt.imshow(edges_3x3, cmap='gray'), plt.title('Canny 3x3')
plt.subplot(133), plt.imshow(edges_5x5, cmap='gray'), plt.title('Canny 5x5')
plt.show()
"""
4.
"""
# 1. 读取并显示灰度图像
img = cv2.imread("D:/tuxiang/IMG_20250423_182617.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.figure(figsize=(10, 5))
plt.subplot(121), plt.imshow(gray, cmap='gray'), plt.title('Original Gray Image')

# 2. 预处理:二值化 + 形态学去噪
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)  # 开运算去噪

# 3. 确定背景区域(膨胀操作)
sure_bg = cv2.dilate(opening, kernel, iterations=3)

# 4. 确定前景区域(距离变换 + 阈值)
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)
sure_fg = sure_fg.astype(np.uint8)  # 转换为整数类型

# 5. 计算未知区域(背景 - 前景)
unknown = cv2.subtract(sure_bg, sure_fg)

# 6. 标记连通区域
_, markers = cv2.connectedComponents(sure_fg)
markers += 1  # 将背景标记为1
markers[unknown == 255] = 0  # 未知区域标记为0

# 7. 应用分水岭算法
markers = cv2.watershed(img, markers)

# 8. 提取边缘(分水岭边界标记为-1)
img[markers == -1] = [255, 0, 0]  # 用红色标记边缘

# 显示结果
plt.subplot(122)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Watershed Edges (Red)')
plt.axis('off')
plt.show()

"""
5.
"""
# 读取图像并二值化
building = cv2.imread("D:/tuxiang/building.jpg", 0)
_, binary = cv2.threshold(building, 127, 255, cv2.THRESH_BINARY)

# 结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))

# 腐蚀和膨胀
eroded = cv2.erode(binary, kernel)
dilated = cv2.dilate(binary, kernel)

# 边缘提取
edge_external = binary - eroded
edge_internal = dilated - binary
edges = cv2.bitwise_or(edge_external, edge_internal)

# 显示结果
plt.figure(figsize=(15, 5))
plt.subplot(141), plt.imshow(binary, cmap='gray'), plt.title('Original')
plt.subplot(142), plt.imshow(edge_external, cmap='gray'), plt.title('External Edge')
plt.subplot(143), plt.imshow(edge_internal, cmap='gray'), plt.title('Internal Edge')
plt.subplot(144), plt.imshow(edges, cmap='gray'), plt.title('Combined Edges')
plt.show()



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qianqianaao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值