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

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

问题描述

我正在使用 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 blob 的结果来限制 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).

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

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 中定义 Watershed 的标记?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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