【问题标题】:Watershed analysis of coins - wrong output硬币的分水岭分析 - 错误的输出
【发布时间】:2018-12-05 10:36:55
【问题描述】:

为了计算图像中的圆形物体,我想使用分水岭算法。 为了了解它是如何工作的以及如何使用它来满足我的需求,我在 python 中搜索了一些工作示例 (https://docs.opencv.org/3.1.0/d3/db4/tutorial_py_watershed.html ; http://scikit-image.org/docs/dev/auto_examples/segmentation/plot_label.html)

我终于找到了一个可行的解决方案,它或多或少地为我自己的目的开箱即用 (How to define the markers for Watershed in OpenCV?)

使用此代码,我得到了很好的结果,无论是使用示例文件还是使用我自己的图像。 在分水岭分析之后,我确实得到了一个奇怪的行为。出于某种原因,分水岭步骤还在图像周围添加了边框。因此,在检测到的对象旁边,图像的整个边缘也会被检测到并着色。

我的猜测是我应该更改代码中的参数以阻止这种情况发生,但到目前为止我无法找到我应该做什么。

这是代码:

import cv2
import numpy as np
from scipy.ndimage import label
def segment_on_dt(a, img):
    border = cv2.dilate(img, None, iterations=3)
    border = border - cv2.erode(border, None)
    dt = cv2.distanceTransform(img, cv2.DIST_L2, 3)
    dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(np.uint8)
    _, dt = cv2.threshold(dt, 200, 255, cv2.THRESH_BINARY)
    lbl, ncc = label(dt)
    # Completing the markers now. 
    lbl[border == 255] = 255 
    lbl = lbl.astype(np.int32)
    cv2.watershed(a, lbl)
    lbl[lbl == -1] = 0
    lbl = lbl.astype(np.uint8)
    return 255 - lbl

# Load image file
img = cv2.imread('coins.jpg')
# Pre-processing.
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_gray = cv2.GaussianBlur(img_gray,(5,5),0)  
width, height = img_gray.shape
_, img_bin = cv2.threshold(img_gray, 0,  255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
img_bin = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN,np.ones((5, 5),     dtype=int))
result = segment_on_dt(img, img_bin)
result[result != 255] = 0
result = cv2.dilate(result, None)
img[result == 255] = (0, 0, 255)
cv2.imwrite('Img_output.png',img)

运行这段代码会给出这个结果(至少在我的电脑上)

检测硬币的结果对于我的目的来说已经足够了,但我对也检测到的图像边缘有点困惑。从我在调试过程中看到的情况来看,分水岭增加了这一优势,但我不清楚为什么会发生这种情况。

【问题讨论】:

    标签: python opencv watershed


    【解决方案1】:

    您可以通过使用 openCV 提供的教程添加背景标签来解决此问题。 https://docs.opencv.org/3.1.0/d3/db4/tutorial_py_watershed.html

    他们添加了一个额外的步骤来插入确定的背景和确定的前景区域,以帮助分水岭算法正确分割硬币区域。

    ****************编辑**************

    再次阅读您的代码后。我发现你的原始代码没有问题。 背景标签是使用变量边框设置的。

    执行 OpenCV 教程中的代码可能会得到相同的结果。问题在于您绘制结果的方式。由于这是一个显示问题,我们有很多方法可以解决这个问题。其中之一是使用确定背景的信息

    这里是对函数segment_on_dt

    的修改
    def segment_on_dt(a, img):
        sure_background = cv2.dilate(img, None, iterations=3)
        border = sure_background - cv2.erode(sure_background, None)
    
    
        dt = cv2.distanceTransform(img, cv2.DIST_L2, 3)
        dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(np.uint8)
        _, dt = cv2.threshold(dt, 200, 255, cv2.THRESH_BINARY)
        lbl, ncc = label(dt)
    
    
        # Completing the markers now. 
        lbl[border == 255] = 255 
    
    
        lbl = lbl.astype(np.int32)
        cv2.watershed(a, lbl)
        lbl[lbl == -1] = 0
        # Only draw red line if its not in sure background
        lbl[sure_background == 0] = 255
    
        lbl = lbl.astype(np.uint8)
        cv2.imshow('lbl_2',lbl)
    
        return 255 - lbl
    

    我为要绘制的红线添加了一个新条件。仅当线条不在确定的背景区域时才会绘制。

    您的最终结果应如下所示。

    【讨论】:

    • 我说的是否正确,即使有了这条额外的线,您仍然可以选择背景,因此您最终仍然会在图像周围绘制边框?因为这意味着我的脚本实际上会给出正确的结果。
    • 你指的是哪一行?
    • 对不起,我的意思是标记背景的额外部分。这将创建一个额外的对象(标记为背景),该对象还将在我发布的代码(或您提到的链接中的代码)中获得大纲?所以我得到的结果符合代码的预期。
    • 很抱歉,我最初对您的问题的回答是错误且不准确的。我已经修复了我的答案以正确解决您的问题。请再看看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-11
    • 1970-01-01
    • 2013-10-16
    • 2018-10-26
    相关资源
    最近更新 更多