如何为OpenCV中的Watershed定义标记?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (188)

我打算使用区域最大值作为标记,minMaxLoc()会给我的价值,但我怎么能限制它是我感兴趣?我可以利用findContours()cvBlob blob 的结果来限制ROI并将最大值应用于每个blob?

提问于
用户回答回答于

问题中呈现的图像由多个太靠近且在某些情况下重叠的对象组成。这里的分水岭的用处是正确分离这些对象,而不是将它们分组为单个组件。所以每个物体至少需要一个标记,背景需要至少一个标记。作为一个例子,首先通过Otsu对输入图像进行二值化处理,然后执行形态学开口以去除小物体。这一步的结果如左图所示。现在考虑二值图像考虑应用距离变换,结果在右侧:

我们可以通过在较早的阈值之后标记不同的区域来获得每个对象的标记。现在,我们还可以考虑上面左侧图像的扩张版本的边框来组成我们的标记。完整的标记如右图所示:

我们这里的这个标记很有意义。每个colored water == one marker将开始填补该地区,分​​水岭改造将建设水坝,阻止不同的“颜色”合并。如果我们进行变换,我们会得到左边的图像。只考虑通过与原始图像合成,我们得到的结果是正确的:

import sys
import cv2
import numpy
from scipy.ndimage import label

def segment_on_dt(a, img):
    border = cv2.dilate(img, None, iterations=5)
    border = border - cv2.erode(border, None)

    dt = cv2.distanceTransform(img, 2, 3)
    dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(numpy.uint8)
    _, dt = cv2.threshold(dt, 180, 255, cv2.THRESH_BINARY)
    lbl, ncc = label(dt)
    lbl = lbl * (255/ncc)
    # Completing the markers now. 
    lbl[border == 255] = 255

    lbl = lbl.astype(numpy.int32)
    cv2.watershed(a, lbl)

    lbl[lbl == -1] = 0
    lbl = lbl.astype(numpy.uint8)
    return 255 - lbl


img = cv2.imread(sys.argv[1])

# Pre-processing.
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)    
_, img_bin = cv2.threshold(img_gray, 0, 255,
        cv2.THRESH_OTSU)
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN,
        numpy.ones((3, 3), dtype=int))

result = segment_on_dt(img, img_bin)
cv2.imwrite(sys.argv[2], result)

result[result != 255] = 0
result = cv2.dilate(result, None)
img[result == 255] = (0, 0, 255)
cv2.imwrite(sys.argv[3], img)
用户回答回答于

你可以使用分水岭计算此图像中的对象数量。这将是该代码的稍微高级版本。

1 - 首先我们加载我们的图像,将其转换为灰度,并用适当的值对其进行阈值。我采取了Otsu的二值化,所以它会找到最佳的阈值:

import cv2
import numpy as np

img = cv2.imread('sofwatershed.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

以下是我得到的结果:

2 - 现在我们必须创建标记。标记是与原始图像大小相同的图像,即32SC1。

现在在原始图像中会有一些区域可以确定,这部分区域属于前景。在标记图像中用255标记此区域。现在,你确定为背景的区域将标记为128.你不确定的区域将标记为0.接下来我们要做的是。

A - 前景地区: - 我们已经有了一个阈值图像,其中药丸是白色的。我们侵蚀他们一点,以便我们确信剩余的地区属于前景:

fg = cv2.erode(thresh,None,iterations = 2)

fg

B - 背景区域: - 这里我们扩大阈值图像,以减少背景区域。但我们确信剩下的黑色区域是100%的背景。我们将它设置为128:

bgt = cv2.dilate(thresh,None,iterations = 3)
ret,bg = cv2.threshold(bgt,1,128,1)

现在我们得到bg如下:

C - 现在我们添加fg和bg

marker = cv2.add(fg,bg)

以下是我们得到的结果:

然后我们将它转​​换成32SC1:

marker32 = np.int32(marker)

3 - 最后我们应用分水岭并将结果转换回uint8图像:

cv2.watershed(img,marker32)
m = cv2.convertScaleAbs(marker32)

m:

4 - 我们对其进行适当的阈值以获取蒙版并bitwise_and使用输入图像执行操作:

ret,thresh = cv2.threshold(m,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
res = cv2.bitwise_and(img,img,mask = thresh)

结果如下:

扫码关注云+社区

领取腾讯云代金券