OpenCV分水岭+ otsu with distanceTransform

时间:2018-01-22 13:23:51

标签: python opencv watershed

我正在尝试使用OpenCV分水岭算法(https://docs.opencv.org/3.1.0/d3/db4/tutorial_py_watershed.html)但只做了一点改动。文档中有以下代码行:

ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)

如果可能,我需要用Otsu的阈值方法替换这种阈值。我使用的大部分代码来自OpenCV分水岭文档:

img = np.load('file.npy')
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel, iterations = 2)
sure_background = cv2.dilate(opening, kernel, iterations = 50)
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, sure_foreground = cv2.threshold(dist_transform, 0, dist_transform.max(), cv2.THRESH_BINARY + cv2.THRESH_OTSU)

最后一行导致错误:

  

/tmp/build/80754af9/opencv_1512687413662/work/modules/imgproc/src/thresh.cpp:1402:错误:(-215)src.type()==(((0)&((1< ;< 3) - 1))+(((1)-1)<<<<<<<<<<<< 3))函数阈值

我知道在分水岭文档中,在这一步中没有Otsu的阈值,但是我必须设置一个脚本来执行非常多的图像,对我来说重要的是它不需要为每个图像明确设置的阈值。

有没有办法在这个对象上运行这个方法(cv2.threshold)(cv2.distanceTransform的结果)和Otsu的阈值?

修改

老实说,我没有在其他库中寻找类似的解决方案,但现在我在scikit-image库(http://scikit-image.org/docs/dev/auto_examples/segmentation/plot_watershed.html)中找到了分水岭算法。它的使用似乎有点复杂,可能符合我的需求。但我之后询问可能对我(或其他人)有用,所以如果有人有解决方案,那么请分享一下:)

编辑2

当我通过添加以下行来标准化值时:

dist_transform = cv2.normalize(dist_transform, None, 255,0, cv2.NORM_MINMAX, cv2.CV_8UC1)

它有效,但不幸的是,还有另一个问题。当我像这样运行cv2.watershed方法时:

ret, labels = cv2.connectedComponents(sure_foreground)
labels = labels + 1
labels[unknown == 255] = 0
labels = cv2.watershed(img, labels) # this line is wrong

我将此作为错误消息:

  

错误:(-215)src.type()==(((0)&amp;((1 <&lt; 3)-1))+(((3)-1)&lt;&lt; 3 ))   &安培;&安培; dst.type()==(((4)&amp;((1 <&lt; 3)-1))+(((1)-1)&lt;&lt;&lt;&lt;&lt;&lt; 3))   分水岭

原因是我使用的图像是1通道阵列,分水岭方法需要3通道阵列(我添加了加载上面的图像)。有办法解决这个问题吗?数据类型是正确的(图像是uint8,标签 - int32)。

2 个答案:

答案 0 :(得分:1)

您的问题已在documentation

中指定
  

目前,Otsu的方法仅适用于8位图像。

来自距离变换的那个:

  

dst - 输出具有计算距离的图像。它是一个32位浮点单通道图像,大小与src相同。

这意味着距离变换的输出与使用Otsu方法的阈值变换不相容...

可能的解决方案

您可以截断浮点值并将矩阵转换为uint8以便能够使用它。

np.uint8(dist_transform)

这可能会丢失一些数据,你必须小心负值和通过255的那些... numpy只会给你一个这样的值:

a = np.array([3.4, 2.7, 8.9, -1.2, 267])
np.uint8(a)
# result => array([  3,   2,   8, 255,  11], dtype=uint8)

如你所见,得到-1.2 =&gt; 255和267 =&gt; 11和其他数字被截断...你可能想要考虑舍入数字或者可能将数值标准化......一切都取决于你的目标是什么以及你的距离变换看起来如何。

我希望这会对你有所帮助,并且记得要注意src和dst类型,它通常是OpenCV最常见的错误。

答案 1 :(得分:1)

正如@ api55所说,OTSU仅适用于CV_8UC1。您可以将dist图像标准化为[0,255]:

dist2 = cv2.normalize(dist, None, 255,0, cv2.NORM_MINMAX, cv2.CV_8UC1)
ret, sure_foreground = cv2.threshold(dist2, 0, int(dist.max()), cv2.THRESH_BINARY + cv2.THRESH_OTSU)

注意:

如果np.uint8(x)包含大于x或小于零的元素(以避免下溢/溢出),则不应简单地使用255

>>> np.uint8(300)
44
>>> np.uint8(-10)
246

链接可能对显示距离图有用:

How to use `cv2.imshow` correctly for the float image returned by `cv2.distanceTransform`?

更新:

这是distmap比较(第三个是你的方法):

enter image description here