为了账号安全,请及时绑定邮箱和手机立即绑定

如何在OpenCV中定义分水岭的标记?

如何在OpenCV中定义分水岭的标记?

慕尼黑5688855 2019-10-23 16:59:34
我正在使用OpenCV为Android编写。我正在使用标记控制的分水岭来分割类似于下面的图像,而无需用户手动标记图像。我打算使用区域最大值作为标记。minMaxLoc()会给我带来价值,但是如何将其限制在我感兴趣的斑点上?我可以利用来自findContours()或cvBlob Blob 的结果来限制ROI并将最大值应用于每个Blob吗?
查看完整描述

3 回答

?
精慕HU

TA贡献1845条经验 获得超8个赞

首先:该函数minMaxLoc仅查找给定输入的全局最小值和全局最大值,因此对于确定区域最小值和/或区域最大值而言,它几乎没有用。但是您的想法是正确的,完全基于区域最小值/最大值提取标记以执行基于标记的分水岭变换。让我尝试阐明什么是分水岭变换,以及如何正确使用OpenCV中存在的实现。

一些处理分水岭的论文对其描述与随后的描述类似(如果不确定,我可能会漏掉一些细节:询问)。考虑一下您所知道的某个区域的表面,其中包含山谷和山峰(在这里,与我们无关的其他细节)。假设在此表面之下仅是水,有色水。现在,在表面的每个谷上打孔,然后水开始充满整个区域。在某个时候,会遇到不同颜色的水,当这种情况发生时,您要建造一个大坝,使它们彼此不接触。最后,您将获得水坝的集合,这是分隔所有不同颜色的水的分水岭。

现在,如果您在该表面上钻孔太多,则最终会出现太多区域:过度分割。如果您做得太少,您将获得细分市场。因此,几乎所有建议使用分水岭的论文实际上都提出了避免这些问题的技术,以解决论文正在处理的应用程序。

我写了所有这些内容(对于任何了解分水岭变换的人来说都太幼稚了),因为它直接反映了您应该如何使用分水岭实现(当前公认的答案是完全错误的方式)。现在让我们使用Python绑定从OpenCV示例开始。

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

//img1.sycdn.imooc.com//5db01699000144de02560192.jpg//img1.sycdn.imooc.com//5db0169a0001973d02560192.jpg根据距离变换结果,我们可以考虑一些阈值,以便仅考虑距背景最远的区域(下图为左图)。这样做,通过在较早的阈值之后标记不同的区域,我们可以获得每个对象的标记。现在,我们还可以考虑将左侧图片的放大版本的边框组成标记。完整的标记显示在右侧的下方(某些标记太暗而看不见,但左侧图像中的每个白色区域均在右侧图像中表示)。

//img1.sycdn.imooc.com//5db016a600015f2102560192.jpg//img1.sycdn.imooc.com//5db016a7000164ab02560192.jpg

我们在这里使用的这个标记很有意义。每个colored water == one marker将开始填充该区域,并且分水岭转换将建造水坝,以阻止不同的“颜色”合并。如果进行变换,则图像在左侧。通过将水坝与原始图像进行组合而仅考虑水坝,我们得到的结果是正确的。

//img1.sycdn.imooc.com//5db016b00001632602560192.jpg//img1.sycdn.imooc.com//5db016b100011fb802560192.jpg

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)


查看完整回答
反对 回复 2019-10-23
  • 3 回答
  • 0 关注
  • 1360 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信