分水岭算法和距离变换函数

分水岭算法原理

在地理学中,分水岭是一个山脊,该山脊通过不同的水系来区分排水区域。集水盆地是把水排入河流或水库的地理区域。分水岭变换把这些概念应用到灰度图像处理中,从而解决许多图像分割问题。
我们把灰度图像视为一个拓扑表面,表面中f(x,y)的值被解释为高度。例如,我们可以把下图中的简单图像形象化为下图中的三维表面。如果雨水降落到该表面上,那么雨水明显会流人集水盆地中。正好降落到分水岭脊线上的雨水会等概率地流到集水盆地中。分水岭变换将找到灰度图像中的集水盆地和脊线。在解决图像分割问题方面,关键概念是把起始图像变为另一幅图像,在变换后的图像中,集水盆地就是我们要识别的物体或区域。
在这里插入图片描述
其核心思路是每一张灰度图都可以表示为一个地形图,比如:
在这里插入图片描述在这里插入图片描述

如果我们从它的最小值开始淹没这个形状,我们将图像分割成两个不同的集合:集水区和分水岭。
在这里插入图片描述

算法流程

  1. 对图像进行二值化
  2. 降噪去除干扰
  3. 使用距离变换函数完成距离变换
  4. 获得边界区域
  5. 标记区域
  6. 使用分水岭算法进行分割
  7. 与原图结合显示

距离变换函数

距离变换函数(opencv中的cv2.distanceTransform函数):
用于计算图像中每一个非零点像素与其最近的零点像素之间的距离,输出的是保存每一个非零点与最近零点的距离信息。
比如构造一200x200大小的图片,其中在(50,60)和(120,120)处,分别有半径为15和12的圆,如下图所示:
在这里插入图片描述
对其进行距离变换,得到如下所示图像:
在这里插入图片描述
越亮代表距离最近的零点像素之间的距离越大,越暗代表距离越小,对其进行伪彩色增强更加的直观,得到如下图所示图像:
在这里插入图片描述
距离变换再加上合适的阈值。接下来我们要找到肯定不是目标的区域。膨胀可以将对象的边界延伸到背景中去。剩下的区域就是我们不知道该如何区分的了。这就是分水岭算法要做的。这些区域通常是前景与背景的交界处(或者两个前景的交界)。我们称之为边界。

区域标记

现在知道哪些是背景那些是目标了。那我们就可以创建标签(一个与原图像大小相同的数组),并标记其中的区域了。对我们已经确定分类的区域(无论是前景还是背景)使用不同的正整数标记,对我们不确定的区域使用 0 标记。我们可以使用函数 cv2.connectedComponents() 来做这件事。它会把将背景标记为 0,其他的对象使用从 1 开始的正整数标记。 但是,我们知道如果背景标记为 0,那分水岭算法就会把它当成未知区域了。所以我们想使用不同的整数标记它们。而对不确定的区域(函数 cv2.connectedComponents 输出的结果中使用 unknown 定义未知区域)标记为 0。
深蓝色区域为未知区域。肯定是目标的区域 使用不同的颜色标记。得到如下图所示的标记,图中的黄色部分,即肯定是目标区域的部分。
在这里插入图片描述

结果

再进行分水岭算法,即可得到结果:
在这里插入图片描述

分割示例

对如图所示图像进行分割:
在这里插入图片描述

对其进行二值化,以及腐蚀操作得到:
在这里插入图片描述在这里插入图片描述

用得到的图像进行距离变换,得到如下图所示:
在这里插入图片描述

再加上合适的阈值。接下来我们要找到肯定不是目标的区域。用假彩色显示标签,蓝色为目标区域,在与原图进行叠加得到最终的分割区域如图所示:
在这里插入图片描述在这里插入图片描述

再对其他图片进行测试得到:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

从以上的测试结果,可以看出分水岭分割算法,对于目标背景简单的物体图片进行分割,可以得到较好的效果,但是对于复杂的目标,比如lina图像,分割效果不好。

代码

分水岭分割

import numpy as np
import cv2
from matplotlib import pyplot as plt
 
src = cv2.imread('up.jpg')
img = src.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)


gray = cv2.GaussianBlur(gray, (5,5), 2)


ret, thresh = cv2.threshold(
    gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

# thresh=cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,11,2)

# 消除噪声
kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
 
# 膨胀
sure_bg = cv2.dilate(opening, kernel, iterations=3)
 
# 距离变换
dist_transform = cv2.distanceTransform(opening, 2, 5)
ret, sure_fg = cv2.threshold(dist_transform, 0.8*dist_transform.max(), 255, 0)
 
# 获得未知区域
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
 
# 标记
ret, markers1 = cv2.connectedComponents(sure_fg)
 
# 确保背景是1不是0
markers = markers1 + 1
 
# 未知区域标记为0
markers[unknown == 255] = 0
 
markers3 = cv2.watershed(img, markers)
img[markers3 == -1] = [0, 0, 255]
 


plt.subplot(131), plt.imshow(cv2.cvtColor(src, cv2.COLOR_BGR2RGB)),
plt.title('Original'), plt.axis('off')
plt.subplot(132), plt.imshow(np.abs(markers), cmap='jet'),
plt.title('Markers'), plt.axis('off')
plt.subplot(133), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)),
plt.title('Result'), plt.axis('off')
plt.show()

距离函数

from scipy.ndimage.morphology import distance_transform_edt
import numpy as np
import cv2

l = 200

x, y = np.indices((l, l))

center1 = (50, 60)

center2 = (120, 120)


radius1, radius2 = 15, 12

circle1 = (x - center1[0])**2 + (y - center1[1])**2 < radius1**2

circle2 = (x - center2[0])**2 + (y - center2[1])**2 < radius2**2


# 3 circles

img = circle1 + circle2
img = ~img
ig = img
img = np.uint8(img*255)
cv2.imshow("0", img)
cv2.imwrite("0.jpg", img)


kernel = np.ones((3, 3), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations=2)

sure_bg = cv2.dilate(opening, kernel, iterations=3)
# img = distance_transform_edt(img)
img = cv2.distanceTransform(img, 2, 5)

ret, sure_fg = cv2.threshold(img, 0.7*img.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)


# 标记
ret, markers1 = cv2.connectedComponents(sure_fg)
 
# 确保背景是1不是0
markers = markers1 + 1
 
# 未知区域标记为0
markers[unknown == 255] = 0
im_color1=cv2.applyColorMap(cv2.convertScaleAbs(markers,alpha=150),cv2.COLORMAP_JET)
cv2.imshow("4", im_color1)
cv2.imwrite("4.jpg", im_color1)

markers3 = cv2.watershed(ig, markers)
img[markers3 == -1] = [255]
cv2.imshow("5", img)

img_max = np.max(img)
img = np.uint8(img*255/img_max)
cv2.imwrite("3.jpg", img)

im_color=cv2.applyColorMap(cv2.convertScaleAbs(img,alpha=1),cv2.COLORMAP_JET)
cv2.imshow("1", im_color)
cv2.imwrite("1.jpg", im_color)
cv2.waitKey(0)

猜你喜欢

转载自blog.csdn.net/qq_40608730/article/details/122732041