数字图像处理(⼆)分⽔岭算法+python
红萝卜丝浓浓的爱1、算法原理说明
传统分⽔岭算法:
任何灰阶图像都可以视为地形表⾯。图像中每⼀像素的灰度值表⽰该点的海拔⾼度,每⼀个局部极⼩值及其影响区域称为集⽔盆地,⽽集⽔盆地的边界则形成分⽔岭。
我们⽤在局部最⼩值的地⽅,打⼀个⼩孔,然后把整个地形浸⼊到⽔中。那么当⽔没过⾼峰时,不同盆地的⽔,必然会产⽣融合。为了防⽌这种情况,我们在⽔没过⾼峰时,就在两盆地的边缘线处竖起⼀座⼤坝。然后继续此操作,知道最后填满⽔。所创建的⼤坝就会形成分⽔岭。
但是这种做法,由于图像中存在很多的噪声或局部不规则性,会产⽣很多⼩的集⽔盆地,会造成图像的过度分割,⽆法真正标注出图像有意义的部分。所以需要合并⼀些图像分割后的相似区域。
这⼏张图可以很好的解释它的过程:
基于标记的分⽔岭算法:
在传统的分⽔岭基础上,opencv实现了基于标记的分⽔岭算法cv::wathershed(Marker-controlled watershed)
它的原理是:对图像中部分像素做标记,表明它的所属区域是已知的。分⽔岭算法可以根据这个初始标签确定其他像素所属的区域。
图⽚来源:IMAGE SEGMENTATION AND MATHEMATICAL MORPHOLOGY
可以根据梯度(⼀般梯度图像在边缘处有较⾼的像素值,⽽在其它地⽅则有较低的像素值,理想情况下,分⼭岭恰好在边缘。),也可以根据距离变换互相结合。
2、效果图
3.结果分析
本次算法步骤:
1、加载原始图像
于小磊2、阈值分割,将图像分割为⿊⽩两个部分
3、对图像进⾏开运算,即先腐蚀再膨胀
如何查看主板型号4、对开运算的结果再进⾏膨胀,得到⼤部分是背景的区域
5、通过距离变换 Distance Transform 获取前景区域
6、背景区域sure_bg 和前景区域sure_fg相减,得到即有前景⼜有背景的重合区域
7、连通区域处理
8、最后使⽤分⽔岭算法
这次实验代码使⽤距离变换的效果在对⼀些⼏何体的分割效果还是可以的。
但是像对于⼈脸的lena图⽚,关于⼈脸处细节的分割很差;
效果更好⼀点:第⼆次考虑根基梯度。
4.源码
<1>
import cv2
import numpy as np
# 读⼊图像,并灰度化
img = cv2.imread('test8.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 阈值分割,将图像分为⿊⽩两部分,阈值0,255,第四个参数THRESH_OTSU,它对⼀幅双峰图像⾃动根据其直⽅图计算出合适的阈值
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) #thresh返回图⽚,ret返回True或Fal,代表有没有读到图⽚# cv2.imshow("thresh", thresh)
# 去除噪声,对图像进⾏形态学的开运算(先进⾏腐蚀操作,再进⾏膨胀操作),使⽤闭运算可以去除对象中的空洞。
kernel = np.ones((3, 3), np.uint8) #返回⼀个3*3 的全1数组
opening = phologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2) #2 顺序为腐蚀-腐蚀-膨胀-膨胀
# cv2.imshow("opening", opening)
# 背景的区域
sure_bg = cv2.dilate(opening, kernel, iterations=3)
#cv2.imshow("sure_bg", sure_bg)
# 距离变换,前景的区域
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5) # DIST_L2 可以为3或者5
ret, sure_fg = cv2.threshold(dist_transform, 0.1 * dist_transform.max(), 255, 0) #0.1时的效果好于其他
#cv2.imshow("sure_fg", sure_fg)
# sure_bg与sure_fg相减,得到既有前景⼜有背景的重合区域
sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg)
# 连通域处理
ret, markers = tedComponents(sure_fg,connectivity=8) #对连通区域进⾏标号,序号为 0 - N-1
markers = markers + 1 #OpenCV 分⽔岭算法对物体做的标注必须都⼤于1 ,背景为标号为0,因此对所有markers 加1 变成了 1 - N
#去掉属于背景区域的部分(即让其变为0,成为背景)
markers[unknow==255] = 0
# 分⽔岭算法
markers = cv2.watershed(img, markers) #分⽔岭算法后,所有轮廓的像素点被标注为 -1
print(markers)
img[markers == -1] = [255, 0, 0] # 将标注为-1 的像素点标成蓝⾊
cv2.imshow("result", img)
cv2.imwrite('result8.jpg', img)
cv2.waitKey(0)
<2>
import matplotlib.pyplot as plt
from scipy import ndimage as ndi
#from skimage import morphology,color,data,filter
from skimage import morphology,filters
import cv2
#读图像,并且灰度化
img =cv2.imread('test8.jpg')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
denoid = dian(image, morphology.disk(2)) #过滤噪声(中值滤波)
#将梯度值低于10的作为开始标记点
markers = adient(denoid, morphology.disk(5)) <10 # 返回图像的局部梯度(即局部最⼤值 - 局部最⼩值)markers = ndi.label(markers)[0]
gradient = adient(denoid, morphology.disk(2)) #计算梯度
#基于梯度的分⽔岭算法
labels =morphology.watershed(gradient, markers, mask=image)
#figsize值:设置⼦图的宽度和⾼度;2⾏2个的⼦图
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(6, 6))
axes = axes.ravel()
学生空间战马奔腾二胡独奏ax0, ax1, ax2, ax3 = axes
ax0.imshow(image, ay, interpolation='nearest') # interpolation⼀种插值运算,cmap表⽰绘图时的样式ax0.t_title("Original")
ax1.imshow(gradient, Spectral, interpolation='nearest')
ax1.t_title("Gradient")
ax2.imshow(markers, Spectral, interpolation='nearest')
ax2.t_title("Markers")
ax3.imshow(labels, Spectral, interpolation='nearest')
ax3.t_title("Segmented")
日语演讲稿for ax in axes:
ax.axis('off') # 关闭坐标轴
当兵的祝福语
fig.tight_layout() # tight_layout会⾃动调整⼦图参数,使之填充整个图像区域。
plt.show()