如何在 OpenCV 中定义分水岭的标记? [英] How to define the markers for Watershed in OpenCV?

查看:17
本文介绍了如何在 OpenCV 中定义分水岭的标记?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 OpenCV 为 Android 写作.我正在使用标记控制的分水岭分割类似于下面的图像,而无需用户手动标记图像.我打算使用区域最大值作为标记.

I'm writing for Android with OpenCV. I'm segmenting an image similar to below using marker-controlled watershed, without the user manually marking the image. I'm planning to use the regional maxima as markers.

minMaxLoc() 会给我这个值,但我怎样才能将它限制为我感兴趣的 blob?我可以利用 findContours() 或 cvBlob blobs 的结果来限制 ROI 并对每个 blob 应用最大值吗?

minMaxLoc() would give me the value, but how can I restrict it to the blobs which is what I'm interested in? Can I utilize the results from findContours() or cvBlob blobs to restrict the ROI and apply maxima to each blob?

推荐答案

首先:函数 minMaxLoc 只能找到给定输入的全局最小值和全局最大值,因此对于确定区域最小值和/或区域最大值.但是您的想法是正确的,基于区域最小值/最大值提取标记以执行基于标记的分水岭变换是完全可以的.让我试着澄清一下什么是分水岭变换,以及你应该如何正确使用 OpenCV 中的实现.

First of all: the function minMaxLoc finds only the global minimum and global maximum for a given input, so it is mostly useless for determining regional minima and/or regional maxima. But your idea is right, extracting markers based on regional minima/maxima for performing a Watershed Transform based on markers is totally fine. Let me try to clarify what is the Watershed Transform and how you should correctly use the implementation present in OpenCV.

一些涉及分水岭的论文以类似的方式描述了它(如果您不确定,我可能会错过一些细节:问).考虑您知道的某个区域的表面,它包含山谷和山峰(以及其他与我们无关的细节).假设在这个表面之下你只有水,有颜色的水.现在,在你表面的每个山谷打洞,然后水开始填满整个区域.在某些时候,不同颜色的水会相遇,当这种情况发生时,你会建造一座水坝,使它们不会相互接触.最后你会得到一个水坝的集合,这是分隔所有不同颜色的水的分水岭.

Some decent amount of papers that deal with watershed describe it similarly to what follows (I might miss some detail, if you are unsure: ask). Consider the surface of some region you know, it contains valleys and peaks (among other details that are irrelevant for us here). Suppose below this surface all you have is water, colored water. Now, make holes in each valley of your surface and then the water starts to fill all the area. At some point, differently colored waters will meet, and when this happen, you construct a dam such that they don't touch each other. In the end you have a collection of dams, which is the watershed separating all the different colored water.

现在,如果您在该表面上打了太多孔,最终会产生太多区域:过度分割.如果你做的太少,你会得到一个细分不足.因此,几乎所有建议使用分水岭的论文实际上都提出了避免该论文处理的应用程序出现这些问题的技术.

Now, if you make too many holes in that surface, you end up with too many regions: over-segmentation. If you make too few you get an under-segmentation. So, virtually any paper that suggests using watershed actually presents techniques to avoid these problems for the application the paper is dealing with.

我写了所有这些(这对于任何知道分水岭转换的人来说可能都太天真了),因为它直接反映了您应该如何使用分水岭实现(当前接受的答案是以完全错误的方式做的).现在让我们开始使用 Python 绑定的 OpenCV 示例.

I wrote all this (which is possibly too naïve for anyone that knows what the Watershed Transform is) because it reflects directly on how you should use watershed implementations (which the current accepted answer is doing in a completely wrong manner). Let us start on the OpenCV example now, using the Python bindings.

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

The image presented in the question is composed of many objects that are mostly too close and in some instances overlapping. The usefulness of watershed here is to separate correctly these objects, not to group them into a single component. So you need at least one marker for each object and good markers for the background. As an example, first binarize the input image by Otsu and perform a morphological opening for removing small objects. The result of this step is shown below in the left image. Now with the binary image consider applying the distance transform to it, result at right.

利用距离变换结果,我们可以考虑一些阈值,以便我们只考虑距离背景最远的区域(下图左).这样做,我们可以通过在较早的阈值之后标记不同的区域来获得每个对象的标记.现在,我们还可以考虑上面左图的放大版本的边框来组成我们的标记.完整的标记如下右图所示(有些标记太暗而无法看到,但左图中的每个白色区域都显示在右图中).

With the distance transform result, we can consider some threshold such that we consider only the regions most distant to the background (left image below). Doing this, we can obtain a marker for each object by labeling the different regions after the earlier threshold. Now, we can also consider the border of a dilated version of the left image above to compose our marker. The complete marker is shown below at right (some markers are too dark to be seen, but each white region in the left image is represented at the right image).

我们这里的这个标记很有意义.每个 colored water == one marker 将开始填充该区域,分水岭变换将建造水坝以阻止不同颜色"的融合.如果我们进行变换,我们会得到左边的图像.通过将它们与原始图像组合来仅考虑水坝,我们得到了正确的结果.

This marker we have here makes a lot of sense. Each colored water == one marker will start to fill the region, and the watershed transformation will construct dams to impede that the different "colors" merge. If we do the transform, we get the image at left. Considering only the dams by composing them with the original image, we get the result at right.

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 + 1))
    # 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)

这篇关于如何在 OpenCV 中定义分水岭的标记?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆