我有一批相似的图像需要分析。
之后
skimage.morphology.reconstruction
(用法示例这里)我得到这样的图像:
我对形状的边缘感兴趣,特别是原始灰度图像中沿垂直线的斜率。我以为我可以使用 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条轮廓线。我想平滑结果并减少线条。
代码:
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 确实保证了直线,它能很好地找到线条,但我仍然有一些线条太“粗”(你可以在图像中看到一些粗线,它们实际上是平行线)。因此我需要一些方法来改进这个结果。
你在寻找像 Hough 变换 这样的东西吗?您应该能够直接在检测到的边缘上使用它。
但是,如果您正在寻找每个垂直线段的特定(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)
我会通过找到较宽的条并在那里切割来找到图像的两个相关部分。这是一些使用 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).