最近在学分水岭算法的opencv函数watershed()时,对函数执行完后image和marker的变化一无所知、懵懵懂懂。
于是便结合网上资料和自己现身说法,给大家分享一下【watershed函数的执行效果】。
OpenCV: Image Segmentation with Watershed Algorithm
首先说明一点:
传入watershed函数的marker参数可以有三种成分:背景、不确定、前景(也即object)。
比如背景像素填充1,而object1填充2,不确定像素填充0。
下文的“种子数值”代表Object成分的编号。
因此,object1的种子数值是2。
watershed函数一旦执行,就会改变marker,但是image不变。
改变A:从基点开始向外填充种子数值,直到边界。
例如下面的第2个硬币对应的种子数值是2,原marker中只有硬币最中间才是填充了2
函数执行后,整个硬币所处区域全被填充2。
改变B:向外填充时,若即将与另一个山谷的积水相涉就停止扩展,最外围便是边界线,marker中的边界线区域填充-1。
还是以第二个硬币为例,第二个硬币的边界像素点填充-1。
也就是说,最后得到的marker想比原marker是更加完善的;原marker的object像素点只需要至少填充一个种子数值,而新marker是将object的所有像素全部填充种子数值,更加精确和点对点。
测试用到的图片
测试用到的代码
- import cv2 as cv
- import numpy as np
- from matplotlib import pyplot as plt
-
- def watershed_demo():
- # remove noise if any
- print(src.shape)
- blurred = cv.pyrMeanShiftFiltering(src, 10, 100)
- # gray\binary image
- gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)
- ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
- cv.imshow("binary-image", binary)
-
- # morphology operation
- kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
- mb = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel, iterations=2)
- sure_bg = cv.dilate(mb, kernel, iterations=3)
- cv.imshow("mor-opt", sure_bg)
-
- # distance transform
- dist = cv.distanceTransform(mb, cv.DIST_L2, 3)
- dist_output = cv.normalize(dist, 0, 1.0, cv.NORM_MINMAX)
- cv.imshow("distance-t", dist_output*50)
-
- ret, surface = cv.threshold(dist, dist.max()*0.6, 255, cv.THRESH_BINARY)
-
- surface_fg = np.uint8(surface)
- cv.imshow("surface-bin", surface_fg)
- unknown = cv.subtract(sure_bg, surface_fg)
- ret, markers = cv.connectedComponents(surface_fg)
-
- # watershed transform
- markers = markers + 1
- markers[unknown == 255] = 0
- print(type(markers))
- print(np.sum(markers==2))
- markers = cv.watershed(src, markers=markers)
- print(np.unique(markers))
- print(np.sum(markers==2))
- src[markers==-1] = [0, 0, 255]
- cv.imshow("result", src)
- markers = markers + 1
- plt.imshow(markers)
- plt.show()
-
-
- src = cv.imread("D:\BaiduNetdiskDownload\pycv-learning\pythonProject\images\coins.jpg")
- cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
- cv.imshow("input image", src)
- watershed_demo()
- cv.waitKey(0)
- cv.destroyAllWindows()
cv.imshow("surface-bin", surface_fg)的输出如下。
这十个白圆块的区域,对应marker填充种子数值的范围。
cv.imshow("result", src)的输出如下。
硬币的边界上绘制了一个红圈,正是利用了上文第二部分的改变B。
plt.imshow(markers)的输出如下。
我们可以看到新得到的marker真正做到对各个硬币进行像素级别的标注,不同硬币object对应不同填充色。