基于分水岭算法的图像分割
一、理论
任何灰度图像都可以看作是地形表面,其中高强度的表示峰和丘陵,而低强度的表示山谷。用不同颜色的水(标签)填充每个孤立的山谷(局部最小值)。水位上升取决于附近的峰值(梯度),来自不同山谷的水将开始融合,为避免这种情况,需要在水合并前建立障碍,继续填补水和建立障碍的工作,直到所有的山峰都在水下。
这种方法会因图像中的噪声或任何其他不规则形而给出过度调整结果。因此,OpenCV
提供了基于标记的分水岭算法,可以指定哪些谷点要合并,哪些不合并。这是一种交互式图像分割。
二、利用分水岭算法分割硬币
首先找到硬币的近似估计值,为此。可以使用Otsu
的二值化
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt
img = cv.imread('E:/Computer/Desktop/opencv/Image_watershed_coins.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
plt.imshow(thresh, cmap='gray')
现在需要去除图像中的任何小白噪声,为此,可以使用图像开运算,移除对象中的小孔,可以使用图像闭运算。所以,限制我们知道了靠近物体中心的区域是前景,而远离物体的区域是背景。
所以我们需要提取我们确定他们是硬币的区域,侵蚀消除了边界像素。
kernel = np.ones((3, 3), np.uint8)
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2)
plt.imshow(opening, cmap='gray')
sure_bg = cv.dilate(opening, kernel, iterations=3)
dist_transform = cv.distanceTransform(opening, cv.DIST_L2, 5)
ret, sure_fg = cv.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
unknow = cv.subtract(sure_bg, sure_fg)
plt.subplot(211)
plt.imshow(unknow)
plt.subplot(212)
plt.imshow(sure_fg)
边界通过sure_bg
区域中减去sure_fg
区域获得。在阈值图像中,得到了一些我们确定硬币的硬币区域,现在他们已经分离。
现在确定哪个是硬币区域,哪个是背景。所以创建标记并标记其中的区域。我们确切的知道任何区域都标有不同的正整数,不确定的区域保留为0,对此,使用cv.connectedComponents()
。它用0标记图像背景,用1开始的整数标记其他对象。
但是,如果背景标记为0,分水岭会将其视为未知区域,所以要用不同的整数来标记他。相反,将标记有未知定义的位置区域标记为0。
markers = cv.watershed(img, markers)
img[markers == -1] = [255, 0, 0]
plt.subplot(121)
plt.imshow(markers)
plt.subplot(122)
plt.imshow(img)