计算机视觉-图像视觉实验-实验3

如果是玫瑰,它总会开花的。——歌德

实验3:肺部图像分割

一、实验目的

用OpenCV编写一个基于分水岭算法的图像分割程序能对肺部医学图像进行分割,辅助医生进行病情诊断,强化和巩固学生对图像分割知识的掌握和灵活应用。

二、实验要求

1、用OpenCV编写一个基于分水岭算法的图像分割程序,能对获取的肺部医学图像进行分割;

2、撰写实验报告,内容包括实验原理、实验过程、关键代码注释、实验结果解释以及实验分析。实验分析部分需要指出实验结果的优劣原因,并提出进一步提高实验性能的方法或手段。

3、使用Python版的OpenCV编写代码。

注意:每位同学需要自备实验素材,撰写报告时保证版面整洁,可适当调整实验报告格式和实验结果图

三、 实验原理

本实验使用了OpenCV库提供的图像处理和分割方法,包括灰度转换、阈值处理、形态学操作、距离转换、连通组件标记和分水岭算法等,以实现图像分割的目的。下面对每个模块分别进行实验原理说明:

1、阈值处理:

原理:阈值处理是图像分割的基本方法之一,将图像的像素值分为前景和背景两部分。通过选择一个合适的阈值,所有高于这个阈值的像素点被赋予一个值(例如255,白色),而低于这个阈值的像素点被赋予另一个值(例如0,黑色)。Otsu的方法可以自动地选择一个阈值,它通过最大化前景和背景像素值的类间方差来确定最佳阈值。

2、形态学操作:

原理:形态学操作包括膨胀腐蚀,主要用于图像的预处理,比如消除噪声分离接触在一起的对象等。膨胀操作会增加图像中对象的边界区域,而腐蚀会减小对象边界。开运算是先腐蚀后膨胀的过程,用于去除小对象或分离对象。形态学操作通常使用一个结构元素(也称为核)对图像进行卷积。

3、距离转换:

原理:距离转换计算图像中每个非零像素到最近零像素的距离。在二值图像中,这可用于确定对象之间的边界。常见的距离度量包括欧氏距离、曼哈顿距离和棋盘距离。结果是一个图像,其中每个像素的值表示到最近背景像素的距离。

4、连通组件标记:

原理:连通组件标记算法用于识别并标记二值图像中的各个连通区域,即将图像中相互连接的前景像素点归为相同的组件。图像中每一个连通区域被赋予一个唯一的标签。连通组件标记是很多图像分析任务的基础,如计数对象、对象跟踪等。

5、分水岭算法:

原理:分水岭算法是一种数学形态学的图像分割技术,被用来将图像分割为不同区域,这些区域则代表单独的对象。其基本概念来源于地理学的分水岭——山脉分水岭可以将水分流到不同的方向。在图像分割中,该算法通常是在图像的梯度图上应用,梯度图显示了像素亮度变化的强烈程度。分水岭算法找到梯度图中的局部最小值,并将图像中的像素点划分到这些最小值的区域中,类似于山谷收集流下来的水。

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('1.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转换为灰度图
ret,imgthresh = cv2.threshold(gray,0,255,
cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) #Otsu阈值处理,转化为二值图
kernel = np.ones((3,3),np.uint8) #定义形态变换卷积核
imgopen = cv2.morphologyEx(imgthresh,cv2.MORPH_OPEN, kernel,iterations=2)
#形态变换:开运算
imgbg = cv2.dilate(imgopen,kernel,iterations=3) #膨胀操作,确定背景
imgdist = cv2.distanceTransform(imgopen,cv2.DIST_L2,0) #距离转换,用去确定前景
ret,imgfg = cv2.threshold(imgdist, 0.7*imgdist.max(),255,2) #对距离转换结果进行阈值处理
imgfg = np.uint8(imgfg) #转换为整数,获得前景
ret,markers = cv2.connectedComponents(imgfg) #标记阈值处理结果
unknown = cv2.subtract(imgbg,imgfg) #确定位置未知区域
markers = markers + 1 #加1使背景不为0
markers[unknown == 255] = 0 #将未知区域设置为0
imgwater = cv2.watershed(img,markers) #执行分水岭算法分割图像
plt.imshow(imgwater) #以灰度图像格式显示匹配结果
plt.title('watershed')
plt.axis('off')
plt.show()
img[imgwater == -1] = [0,255,0] #将原图中被标记点设置为绿色

cv2.imshow('watershed',img) #显示分割结果
cv2.waitKey(0)

四、实验结果

参考代码实验结果:经过处理的图像会在原有图像的基础上,以绿色线条清晰地突出显示出图像中不同对象的边界。

原图

原图

效果图

效果图

实验优缺点:

优点:

  1. 使用了Otsu阈值处理:自动根据图像的灰度分布选择最佳阈值,适用于图像的二值化,有效地去除了背景噪声。
  2. 形态学操作:通过开运算和膨胀操作改善了图像的结构,有助于更清晰地分离前景和背景。
  3. 利用距离变换和分水岭算法实现图像分割:先通过距离变换确定对象的前景区域,再通过分水岭算法准确地分割出对象,适用于对象和背景紧密相连的情况。

缺点

  1. 错误处理不足:没有检查cv2.imread是否成功读取图像,如果图像路径不正确,后续的所有操作都会失败。
  2. 资源释放:在使用cv2.imshow后,应调用cv2.destroyAllWindows()来确保窗口正确关闭,释放资源。

改进建议

  1. 增加错误处理:在操作图像前,检查图像是否成功加载,例如通过if img is None: print('Image not found');exit()
  2. 资源释放:在cv2.waitKey(0)后添加cv2.destroyAllWindows()以确保所有OpenCV窗口都被正确关闭。
  3. 代码清晰性:将代码段按功能分块,每个块前用注释简要说明该部分代码的目的,提高代码的可读性。

改进代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import cv2
import numpy as np
import matplotlib.pyplot as plt

def image_segmentation(image_path, threshold=0.7):
"""
对图像进行分割
Args:
image_path (str): 输入图像的路径
threshold (float): 阈值百分比,默认为0.7
Returns:
np.ndarray: 分割后的图像
"""
try:
# 读取图像
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值处理
ret, imgthresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 形态变换
kernel = np.ones((3, 3), np.uint8)
imgopen = cv2.morphologyEx(imgthresh, cv2.MORPH_OPEN, kernel, iterations=2)
# 背景和前景处理
imgbg = cv2.dilate(imgopen, kernel, iterations=3)
imgdist = cv2.distanceTransform(imgopen, cv2.DIST_L2, 0)
ret, imgfg = cv2.threshold(imgdist, threshold * imgdist.max(), 255, 2)
imgfg = np.uint8(imgfg)
# 标记处理
ret, markers = cv2.connectedComponents(imgfg)
unknown = cv2.subtract(imgbg, imgfg)
markers = markers + 1
markers[unknown == 255] = 0
# 分水岭算法分割图像
imgwater = cv2.watershed(img, markers)
# 标记点设置为绿色
img[imgwater == -1] = [0, 255, 0]
return img
except Exception as e:
print(f"Error: {e}")
return None
# 图像分割示例
image_path = '1.jpg'
segmented_image = image_segmentation(image_path)
if segmented_image is not None:
cv2.imshow('Segmented Image', segmented_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

改进代码实验效果:

改进后

其他方法:

基于边缘检测的方法是图像处理中的一种基本技术,广泛用于图像分割、特征提取等领域。以下是代码实现。

基于边缘检测的代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import cv2
import numpy as np
from matplotlib import pyplot as plt

def sobel_edge_detection(image_path):
# 读取图像
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

# 使用Sobel算子进行边缘检测
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5) # 对x方向进行操作
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5) # 对y方向进行操作

# 显示原图
plt.subplot(1, 3, 1), plt.imshow(img, cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])

# 显示x方向边缘
plt.subplot(1, 3, 2), plt.imshow(sobelx, cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])

# 显示y方向边缘
plt.subplot(1, 3, 3), plt.imshow(sobely, cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])

plt.show()

# 调用函数
sobel_edge_detection('<your-image-file-path>')

实验效果:

实验效果图

五、实验分析

对于参考代码:

实验结果显示:参考代码能够很好的将图像边界清晰地突出,并用不同颜色显示出图像中不同对象的边界。使用了Otsu阈值处理,自动根据图像的灰度分布选择最佳阈值,适用于图像的二值化,有效地去除了背景噪声。通过开运算和膨胀操作改善了图像的结构,有助于更清晰地分离前景和背景。利用距离变换和分水岭算法实现图像分割,先通过距离变换确定对象的前景区域,再通过分水岭算法准确地分割出对象,适用于对象和背景紧密相连的情况。

对于改进代码

实验结果显示:改进代码会在操作图像前,检查图像是否成功加载;在cv2.waitKey(0)后添加cv2.destroyAllWindows()以确保所有OpenCV窗口都被正确关闭。

基于边缘检测的方法:

实验结果显示:通过使用使用Sobel算子进行边缘检测,通过对图像X、Y分别进行边缘检测操作,该方法简单、快捷,能够很好的将图像的边缘区域很好的提取出来。一些小部分的边缘也能很好的体现出来,这是基于分水岭代码所没有的。


计算机视觉-图像视觉实验-实验3
https://yelelalearn.github.io/2024/04/25/4-25blog/
作者
Yelearn
发布于
2024年4月25日
更新于
2024年4月25日
许可协议