我正在尝试使用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)。
答案 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比较(第三个是你的方法):