使用OpenCV对距离图像进行分水岭变换
在Matlab中,我们可以对距离变换进行分水岭变换,以分开两个相互接触的物体:
上面的第一张图片是我们想要分开的接触物体的图像。第二张图片是它的距离变换结果。
所以,如果我们把黑白图像叫做 img
,在Matlab中我们可以这样做:
D = -bwdist(~img);
L = watershed(D);
现在我们来看看如何用OpenCV做同样的事情:OpenCV有一个基于标记的分水岭分割功能。要用OpenCV完成分开两个接触物体的任务,我们需要为两个物体和背景提供标记。
img = np.zeros((400, 400), np.uint8)
cv2.circle(img, (150, 150), 100, 255, -1)
cv2.circle(img, (250, 250), 100, 255, -1)
dist = cv2.distanceTransform(img, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE)
dist3 = np.zeros((dist.shape[0], dist.shape[1], 3), dtype = np.uint8)
dist3[:, :, 0] = dist
dist3[:, :, 1] = dist
dist3[:, :, 2] = dist
markers = np.zeros(img.shape, np.int32)
markers[150,150] = 1 # seed for circle one
markers[250, 250] = 2 # seed for circle two
markers[50,50] = 3 # seeds for background
cv2.watershed(dist3, markers)
在下面的图片中,你可以看到经过分水岭处理后的 markers
图像。原始的黑白 img
被红色叠加在上面。问题是,结果中的 markers
图像的物体边界和原始图像并不相同。我该如何确保物体边界保持一致呢?
1 个回答
3
你最好了解一下“分水岭”这个函数到底是怎么工作的。它会开始用种子进行“洪水填充”,并把它们的坐标和邻居的梯度放进一个优先队列里。
你知道,当你对图像应用距离变换时,圆的梯度会变成0或1,但背景的梯度总是0。
现在你有三个种子,洪水填充开始工作:背景(种子3)、邻居(种子1)、邻居(种子2),它们会轮流工作,直到种子1或种子2的梯度达到1;然后只有种子3可以继续工作。
当种子3碰到圆的边界时,它的梯度变成1,这样它们又可以轮流工作了。
所以如果你想确保物体的边界保持不变,最好在种子3碰到圆的边界时增加梯度。
就像这样:
dist = cv2.distanceTransform(img, cv2.cv.CV_DIST_L2, cv2.cv.CV_DIST_MASK_PRECISE)
dist[dist > 0] += 2.0
这里是一个结果
...这里面有一些问题(当队列中的所有梯度都是1时,哪个会先弹出,哪个会第二个弹出)