一、实验目的
掌握二值图像、灰度图像进行形态学操作的原理和实现方法。
掌握灰度图像进行形态学操作的原理和实现方法。
掌握数字图像处理关于图像分割的概念、分类以及基本内容。
掌握常见基于边缘的图像分割算法和实现(包括一二阶微分算子法和非积分算子法)。
掌握常见的基于区域的图像分割算法和实现。
掌握使用形态学操作法提取图像边缘进行分割的方法和实现。
二、实验内容
- 读取一幅二值图像并显示,分别使用3*3大小的十字型、圆形和矩形结构元素,分别进行膨胀、腐蚀、开运算、闭运算,并显示形态学操作后的图像。分析不同类型结构元素对图像形态学操作的特点。
- 读取一幅灰度图像并显示,使用5*5大小的矩形结构元素,分别进行膨胀、腐蚀、开运算、闭运算,并显示形态学操作后的图像。分析不同类型结构元素对图像形态学操作的特点。
- 读取一幅灰度图像并进行显示,对canny算子实现对图像的分割算法,并通过调整sobel算子核的大小(33,55),对比canny算子进行图像分割的效果。
- 读取并显示一幅灰度图像,对其采用分水岭算法进行图像边缘提取。
- 案例操作题:腐蚀、膨胀运算除了能去除细小区域、填充空洞外,还能够提取目标物体的边缘轮廓。本案例活用图像形态学,采用腐蚀、膨胀运算和图像差分运算提取建筑物的边缘。
本案例采用的数据是一张建筑物的图片building.jpg,请写出问题分析思路,并编程实现。
三、完整实验程序(截图)、结果(截图)与分析
1. 代码
结果
分析不同类型结构元素对图像形态学操作的特点。
矩形结构元素:各向同性,膨胀/腐蚀均匀扩展或收缩边界。
十字结构元素:强调水平和垂直方向,对角边缘处理较弱。
圆形结构元素(实际为椭圆):与十字类似,但若结构不同,可能更平滑。
2. 代码
结果
分析不同类型结构元素对图像形态学操作的特点。
在图像形态学处理中,结构元素(Structuring Element) 的形状直接影响操作的局部作用方式,进而决定对图像特征的保留、去除或增强效果。
- 矩形结构元素(各向同性):膨胀/腐蚀均匀影响所有方向,适合处理方形特征。
- 十字结构元素(各向异性):主要影响水平和垂直方向,对角方向变化较小,适合处理十字形结构。
- 圆形结构元素(各向同性):均匀作用于所有方向,能平滑边缘并保持圆形特征。
开运算消除小物体时,圆形结构元素更有效;闭运算填充孔洞时,矩形结构元素更均匀。
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()