币的分水岭分析——错误的输出
Watershed analysis of coins - wrong output
对于图像中圆形物体的计数我想使用分水岭算法。
为了了解它是如何工作的以及如何使用它来满足我的需要,我在 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)
运行 此代码将给出此结果(至少在我的电脑上)
检测硬币的结果对我的目的来说已经足够好了,但我对也检测到的图像边缘有点困惑。根据我在调试过程中看到的情况,分水岭增加了这条边,但我不清楚为什么会这样。
您可以使用 openCV 提供的教程通过添加背景标签来解决此问题。
https://docs.opencv.org/3.1.0/d3/db4/tutorial_py_watershed.html
他们添加了一个额外的步骤来插入确定的背景和确定的前景区域,以帮助分水岭算法正确分割硬币区域。
*********** 编辑**************
再次阅读您的代码。我发现你原来的代码没有问题。
使用变量边框设置背景标签。
执行 OpenCV 教程中的代码,您可能会得到相同的结果。问题在于您绘制结果的方式。由于这是一个显示问题,因此我们可以通过多种方式解决该问题。其中之一就是利用sure-background
的信息
这里是对函数的修改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
我为要绘制的红线添加了一个新条件。只有在不确定背景区域时才会绘制线条。
您的最终结果应该如下所示。
对于图像中圆形物体的计数我想使用分水岭算法。 为了了解它是如何工作的以及如何使用它来满足我的需要,我在 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)
运行 此代码将给出此结果(至少在我的电脑上)
检测硬币的结果对我的目的来说已经足够好了,但我对也检测到的图像边缘有点困惑。根据我在调试过程中看到的情况,分水岭增加了这条边,但我不清楚为什么会这样。
您可以使用 openCV 提供的教程通过添加背景标签来解决此问题。 https://docs.opencv.org/3.1.0/d3/db4/tutorial_py_watershed.html
他们添加了一个额外的步骤来插入确定的背景和确定的前景区域,以帮助分水岭算法正确分割硬币区域。
*********** 编辑**************
再次阅读您的代码。我发现你原来的代码没有问题。 使用变量边框设置背景标签。
执行 OpenCV 教程中的代码,您可能会得到相同的结果。问题在于您绘制结果的方式。由于这是一个显示问题,因此我们可以通过多种方式解决该问题。其中之一就是利用sure-background
的信息这里是对函数的修改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
我为要绘制的红线添加了一个新条件。只有在不确定背景区域时才会绘制线条。
您的最终结果应该如下所示。