将线条的黑白图像矢量化为没有厚度的矢量(即坐标)
Vectorising black and white image of lines into vectors of no thickness (i.e coordinates)
我希望能够拍摄图像,例如:
然后return每行开始和结束的坐标(例如示例图像中有 3 行)。我知道有一些方法,例如霍夫线变换,这确实给出了坐标,但是由于输入图像具有厚度,它是所有边缘的 returns 坐标。一个例子如下所示:
是否有不同的方法(或调整霍夫线变换)来获取不考虑线条粗细的坐标?
您可能想使用 skeletonize 来获得线条的 骨架 。
为了获得行的 tips/corners,这是我遵循的程序:
- 获取骨架图片
- 通过 3x3 大小的矩阵过滤骨架图像找到提示。
- 通过霍夫变换将线条拟合到骨架图像
- 根据线的交点求线角
这是我的代码。让我们从导入所需的库开始。
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()
我希望能够拍摄图像,例如:
然后return每行开始和结束的坐标(例如示例图像中有 3 行)。我知道有一些方法,例如霍夫线变换,这确实给出了坐标,但是由于输入图像具有厚度,它是所有边缘的 returns 坐标。一个例子如下所示:
是否有不同的方法(或调整霍夫线变换)来获取不考虑线条粗细的坐标?
您可能想使用 skeletonize 来获得线条的 骨架 。
为了获得行的 tips/corners,这是我遵循的程序:
- 获取骨架图片
- 通过 3x3 大小的矩阵过滤骨架图像找到提示。
- 通过霍夫变换将线条拟合到骨架图像
- 根据线的交点求线角
这是我的代码。让我们从导入所需的库开始。
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()