获取图像中的边缘/垂直线

问题描述 投票:0回答:2

原帖

我有一批相似的图像需要分析。

之后

  • 阈值
  • 孔填充使用
    skimage.morphology.reconstruction
    (用法示例这里
  • 图像关闭

我得到这样的图像:

我对形状的边缘感兴趣,特别是原始灰度图像中沿垂直线的斜率。我以为我可以使用 Canny 来获得确实给了我的轮廓:

我的问题:

  1. 我将如何分离大致垂直和水平的边缘?对我来说有非常不同的意义,因为我对相邻水平线之间发生的事情一点都不感兴趣(我宁愿从图像中剪掉那些部分),我对垂直线周围发生的事情感兴趣(白色周围在黑白图像中)。
  2. 我会假设我的线应该是直的(可能有斜坡,但噪音太高)。我将如何平滑轮廓线?
  3. 最后我想回到灰度图像,看看垂直线附近的像素统计。如何从 Canny 找到的边缘中提取线的位置信息?

我的代码:

im_th= cv2.inRange(img, 0, 500, cv2.THRESH_BINARY)
seed = np.copy(im_th)
seed[1:-1, 1:-1] = im_th.max()
mask = im_th
filled = reconstruction(seed, mask, method='erosion').astype(np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(4,4))
closed = cv2.morphologyEx(filled,cv2.MORPH_CLOSE, kernel=kernel)
edges = cv2.Canny(cv2.medianBlur(closed, 5), 50, 150)

编辑

根据 Christoph Rackwitz 和 mimocha 的建议,我尝试使用

findContours
HoughLinesP
。这两个结果看起来很有希望,但需要进一步的工作才能使用。

查找轮廓方法

代码:

contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour_img = cv2.drawContours(cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR), contours, -1, (255,0,0), 3)

结果图像(覆盖在闭合图像上):

轮廓很好找,我还有151条轮廓线。我想平滑结果并减少线条。

HoughLinesP 方法

代码:

threshold = 15
min_line_length = 30
max_line_gap = 200
line_image = np.copy(cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR)) * 0
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold, None, min_line_length, max_line_gap)
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),5)
lines_edges = cv2.addWeighted(cv2.cvtColor(closed, cv2.COLOR_GRAY2BGR), 0.2, line_image, 1, 0)

结果图像:

HoughLinesP 确实保证了直线,它能很好地找到线条,但我仍然有一些线条太“粗”(你可以在图像中看到一些粗线,它们实际上是平行线)。因此我需要一些方法来改进这个结果。

到目前为止的总结:

  1. 轮廓方法和霍夫变换方法都有增益。
  2. 霍夫变换可能有一个优势,因为它返回直线,并且还允许在垂直线中分离水平线,如here.
  3. 我仍然需要一种合并/聚类线的方法。
python opencv image-processing contour edge-detection
2个回答
1
投票

你在寻找像 Hough 变换 这样的东西吗?您应该能够直接在检测到的边缘上使用它。

  1. 水平线不影响结果。 (您可以按角度过滤线条)
  2. 线条稍微倾斜不会影响结果。
  3. 霍夫变换告诉您线在哪里以及它们的角度——它根据您的边缘像素将一条无限长的线拟合到您的图像上。因为你知道线的斜率和从线到原点的最短距离,你可以检查线相交的每个像素。

但是,如果您正在寻找每个垂直线段的特定(x,y)坐标开始和结束的位置,这可能无法回答您的问题。


我从 OpenCV 教程中复制了代码示例并在您的图像上对其进行了测试以找到两条垂直线:

>>> import cv2 as cv
>>> import numpy as np
>>> src = cv.imread("test.png")
>>> edge = cv.Canny(src, 50, 200, None, 3)
>>> cv.HoughLines(edge, 1, np.pi / 180, 150, None, 0, 0)
array([[[ 81.,   0.]],

       [[144.,   0.]]], dtype=float32)

0
投票

我会通过找到较宽的条并在那里切割来找到图像的两个相关部分。这是一些使用 DIPlib 的代码(免责声明:我是作者),但使用几乎任何其他包也很容易做到这一点。

import diplib as dip
import math


img = dip.ImageRead('5oC9n.png')

# Colapse the x axis by summation
y = dip.Sum(img, process=[True, False]).Squeeze()

# In our 1D result, find the transition points
y = dip.Abs(dip.Dx(y))
y = dip.Shrinkage(y, 5)  # ignore any local maxima below 5
maxima = dip.SubpixelMaxima(y)

# We want segments between 1st and 2nd, and between 3rd and 4th.
# We'll add a bit of margin too
assert(len(maxima) == 4)

# First segment
top = math.ceil(maxima[0].coordinates[0]) + 2
bottom = math.floor(maxima[1].coordinates[0]) - 2
img1 = img[:, top:bottom]

# Second segment
top = math.ceil(maxima[2].coordinates[0]) + 2
bottom = math.floor(maxima[3].coordinates[0]) - 2
img2 = img[:, top:bottom]

接下来,找到直线的斜率,可以通过在每条边上拟合一条直线来完成。 This other answer of mine does exactly that (the answer computes the distance between two parallel edges, but in doing it fits a straight line and finds its normal (i.e. the slope).

© www.soinside.com 2019 - 2024. All rights reserved.