分水岭算法将图像看作地理学中的地形表面,图像中的高灰度值区域被看作山峰,低灰度值区域被看作山谷。进而实现图像的分割。
假如我们向“山谷”中注水,水位则会逐渐升高,然后不同山谷的水就会汇集在一起,如果我们阻止来自不同山谷的水汇集,我们需在水流可能交汇处建立堤坝,我们需要把图像分成两个不同的集合:集水盆地和分水岭线。我们建立的堤坝即是分水岭线,也即是对原图像的分割。
但是由于图像中的噪声或任何其他不规则性,这种方法会造成过度分割的结果。所以OpenCV
实现了一种基于标记的分水岭算法,您可以指定哪些是所有要合并的山谷点,哪些不是。这是一种交互式图像分割。我们所做的是为我们知道的对象给出不同的标签。用一种颜色(或强度)标记我们确定是前景或对象的区域,用另一种颜色标记我们确定是背景或非对象的区域,最后用0标记我们不确定的区域。这就是我们的标记。然后应用分水岭算法。然后我们的标记将使用我们给出的标签进行更新,并且对象的边界将具有-1的值。
opencv
实现分水岭算法的步骤:Otsu
的二值化。cv2.distanceTransform()
对图像进行处理,并对其结果进行阈值分割,得到”确定前景F“。UN=O-B-F
。cv.connectedComponents()
实现原始图像O的标注工作。cv.connectedComponents()
的标注结果进行修正。cv.watershed()
完成对图像的分割。下面我们以下图相接触的硬币图像为例,分割获取硬币的范围。
1.获取硬币的近似估计
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('coins.png')
assert img is not None, "file could not be read, check with os.path.exists()"
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
2.图像去噪
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv.morphologyEx(thresh,cv.MORPH_OPEN,kernel, iterations = 2)
3.确定背景区域
# sure background area
sure_bg = cv.dilate(opening,kernel,iterations=3)
4.确定前景区域
# Finding sure foreground area
dist_transform = cv.distanceTransform(opening,cv.DIST_L2,5)
ret, sure_fg = cv.threshold(dist_transform,0.7*dist_transform.max(),255,0)
5.确定未知区域
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv.subtract(sure_bg,sure_fg)
6.实现图像标注以及修复处理
# Marker labelling
ret, markers = cv.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
7.应用分水岭算法实现分割
markers = cv.watershed(img,markers)
# The boundary region will be marked with -1.
img[markers == -1] = [255,0,0]
注意:以上方法中通过距离函数确定未知区域是针对物体相互接触的情况,对于物体独立,没有相互接触的情况,可以在膨胀操作后,进行腐蚀操作,然后前者减去后者,即得到未知区域:
sure_bg = cv2.dilate(opening, kernel, iterations=3) # sure background area
sure_fg = cv2.erode(opening, kernel, iterations=3) # sure foreground area
unknown = cv2.subtract(sure_bg, sure_fg) # unknown area