1

我正在尝试使用 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 的阈值,但我必须设置一个要在很多图像上执行的脚本,对我来说重要的是它不需要设置阈值明确的每张图片。

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


编辑

老实说,我没有在其他库中寻找类似的解决方案,但现在我在 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) & ((1 << 3) - 1)) + (((3)-1) << 3)) && dst.type() == (((4) & ((1 << 3) - 1)) + (((1)-1) << 3)) 在函数分水岭

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

4

2 回答 2

1

您的问题在文档中指定:

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

还有一个距离变换:

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

这意味着距离变换的输出与 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 => 255 和 267 => 11 并且其他数字被截断...您可能需要考虑对数字进行四舍五入或标准化值...。一切都取决于您的目标是什么以及如何你的距离变换看起来像。

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

于 2018-01-22T14:01:13.477 回答
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

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

cv2.distanceTransform 返回的浮动图像如何正确使用 cv2.imshow?


更新:

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

在此处输入图像描述

于 2018-01-22T14:08:05.570 回答