将线条的黑白图像矢量化为没有厚度的矢量(即坐标)

Vectorising black and white image of lines into vectors of no thickness (i.e coordinates)

我希望能够拍摄图像,例如:

然后return每行开始和结束的坐标(例如示例图像中有 3 行)。我知道有一些方法,例如霍夫线变换,这确实给出了坐标,但是由于输入图像具有厚度,它是所有边缘的 returns 坐标。一个例子如下所示:

是否有不同的方法(或调整霍夫线变换)来获取不考虑线条粗细的坐标?

您可能想使用 skeletonize 来获得线条的 骨架

为了获得行的 tips/corners,这是我遵循的程序:

  1. 获取骨架图片
  2. 通过 3x3 大小的矩阵过滤骨架图像找到提示。
  3. 通过霍夫变换将线条拟合到骨架图像
  4. 根据线的交点求线角

这是我的代码。让我们从导入所需的库开始。

import cv2
import matplotlib.pyplot as plt
import numpy as np
from skimage.morphology import skeletonize
from itertools import combinations

我开始获取骨架图像。我将使用此图像来检测所有点。请注意,skeletonize 的默认方法会在骨架图像中创建分叉。但是 lee 方法更适合您的问题。请注意您的其他图片。


img_bgr = cv2.imread('lines.png',1)
img = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2GRAY)

_,img = cv2.threshold(img,10,1,cv2.THRESH_BINARY_INV)

skeleton = skeletonize(img,method='lee')

# display purposes
skeleton_bgr = cv2.cvtColor(skeleton.astype(np.uint8),cv2.COLOR_GRAY2BGR)

接下来是获取线条的提示。

img_conv = cv2.filter2D(skeleton.astype(np.uint8),-1,np.ones((3,3))) #
img_conv = img_conv*skeleton
img_tips = img_conv == 2
tips = np.array(np.nonzero(img_tips)).T

仅供参考,检测到的尖端距离骨架有几个像素,我可能遗漏了一些东西,我把这部分留给你。

正如你在问题中所建议的,在下一步中,我正在为骨架图像拟合线条。

lines = cv2.HoughLines(skeleton.astype(np.uint8), 1, np.pi / 180, 150, None, 0, 0)

lines_points = []

# # Draw the lines
for i in range(0, len(lines)):
    rho = lines[i][0][0]
    theta = lines[i][0][1]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
    pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
    # cv2.line(skeleton_bgr, pt1, pt2, (0,0,255), 3, cv2.LINE_AA)
    lines_points.append([pt1,pt2])

然后,我通过交点得到线的角。

def line_intersection(line1, line2):
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])

    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    if div == 0:
       raise Exception('lines do not intersect')

    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y

line_combinations = combinations(lines_points, 2)

fig,ax = plt.subplots(1)
ax.imshow(skeleton, cmap = 'gray')
ax.scatter(tips[0,:],tips[1,:],s=4,color='r')

for line_combination in list(line_combinations):
    line1 = line_combination[0]
    line2 = line_combination[1]

    intersection = line_intersection(line1,line2)
    
    # clip the intersection points to the image size    
    if intersection[0]<img.shape[1] and intersection[1]<img.shape[0]:
        ax.scatter(intersection[0],intersection[1],s=4,color='b')

这是原图上的结果。

这是包含绘图函数的完整代码。

import cv2
import matplotlib.pyplot as plt
import numpy as np
from skimage.morphology import skeletonize
from itertools import combinations

def line_intersection(line1, line2):
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1])

    def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

    div = det(xdiff, ydiff)
    if div == 0:
       raise Exception('lines do not intersect')

    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y


img_bgr = cv2.imread('lines.png',1)
img = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2GRAY)

_,img = cv2.threshold(img,10,1,cv2.THRESH_BINARY_INV)

skeleton = skeletonize(img,method='lee')
skeleton_bgr = cv2.cvtColor(skeleton.astype(np.uint8),cv2.COLOR_GRAY2BGR)


# fig,ax = plt.subplots(1)
# ax.imshow(skeleton, cmap = 'gray')


#get tips
img_conv = cv2.filter2D(skeleton.astype(np.uint8),-1,np.ones((3,3))) #
img_conv = img_conv*skeleton
img_tips = img_conv == 2
tips = np.array(np.nonzero(img_tips)).T



# fig,ax = plt.subplots(1)
# ax.imshow(skeleton, cmap = 'gray')
# ax.scatter(tips[0,:],tips[1,:],s=4,color='r')


lines = cv2.HoughLines(skeleton.astype(np.uint8), 1, np.pi / 180, 150, None, 0, 0)

lines_points = []

# # Draw the lines
for i in range(0, len(lines)):
    rho = lines[i][0][0]
    theta = lines[i][0][1]
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a * rho
    y0 = b * rho
    pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
    pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
    cv2.line(skeleton_bgr, pt1, pt2, (0,0,255), 3, cv2.LINE_AA)
    lines_points.append([pt1,pt2])


line_combinations = combinations(lines_points, 2)


fig,ax = plt.subplots(1)
ax.imshow(img, cmap = 'gray')
ax.scatter(tips[0,:],tips[1,:],s=4,color='r')

for line_combination in list(line_combinations):
    line1 = line_combination[0]
    line2 = line_combination[1]

    intersection = line_intersection(line1,line2)
    
    # clip the intersection points to the image size    
    if intersection[0]<img.shape[1] and intersection[1]<img.shape[0]:
        ax.scatter(intersection[0],intersection[1],s=4,color='b')

# fig,ax = plt.subplots(1)
# ax.imshow(skeleton_bgr,'gray')

plt.show()