有很多方法可以做到这一点,我将只举一个例子。但是,我在python 中最快,所以我的代码示例将使用该语言。不过,翻译起来应该不难(在您为他人完成后,请随时使用您的 C++ 解决方案编辑您的帖子)。
对于预处理,我强烈建议dilate()ing 你的边缘图像。这将使线条更粗,这将有助于更好地拟合霍夫线。霍夫线函数在抽象中所做的基本上是制作一个穿过大量角度和距离的线网格,如果这些线越过 Canny 的任何白色像素,那么它会为该线给出它通过的每个点的分数.但是,来自 Canny 的线条不会完全笔直,因此您会得到一些不同的线条得分。使这些 Canny 线条更粗将意味着每条 真正 接近拟合良好的线条将有更好的机会获得更高的得分。
如果您要使用HoughLinesP,那么您的输出将是行segments,您所拥有的只是在线上的两个点。
由于线条大多是垂直和水平的,您可以轻松地根据它们的位置分割线条。如果一条线的两个 y 坐标彼此靠近,则该线大部分是水平的。如果两个 x 坐标彼此靠近,则该线大部分是垂直的。因此,您可以通过这种方式将线条分割成垂直线和水平线。
def segment_lines(lines, delta):
h_lines = []
v_lines = []
for line in lines:
for x1, y1, x2, y2 in line:
if abs(x2-x1) < delta: # x-values are near; line is vertical
v_lines.append(line)
elif abs(y2-y1) < delta: # y-values are near; line is horizontal
h_lines.append(line)
return h_lines, v_lines
然后,您可以从它们的端点using determinants 获取两条线段的交点。
def find_intersection(line1, line2):
# extract points
x1, y1, x2, y2 = line1[0]
x3, y3, x4, y4 = line2[0]
# compute determinant
Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/ \
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/ \
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
return Px, Py
所以现在如果你遍历你的所有线,你会有来自所有水平线和垂直线的交点,但是你有 很多 线,所以你会有很多交点盒子的同一个角落。
但是,这些都在一个向量中,因此您不仅需要平均每个角的点,还需要将它们实际组合在一起。您可以使用 k-means 聚类来实现这一点,它在 OpenCV 中实现为 kmeans()。
def cluster_points(points, nclusters):
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
_, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
return centers
最后,我们可以使用circle() 简单地将这些中心(确保我们先四舍五入——因为到目前为止一切都是浮点数)绘制到图像上,以确保我们做对了。
我们拥有它;四个点,在盒子的角落。
这是我在 python 中的完整代码,包括生成上图的代码:
import cv2
import numpy as np
def find_intersection(line1, line2):
# extract points
x1, y1, x2, y2 = line1[0]
x3, y3, x4, y4 = line2[0]
# compute determinant
Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/ \
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/ \
((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
return Px, Py
def segment_lines(lines, delta):
h_lines = []
v_lines = []
for line in lines:
for x1, y1, x2, y2 in line:
if abs(x2-x1) < delta: # x-values are near; line is vertical
v_lines.append(line)
elif abs(y2-y1) < delta: # y-values are near; line is horizontal
h_lines.append(line)
return h_lines, v_lines
def cluster_points(points, nclusters):
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
_, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
return centers
img = cv2.imread('image.png')
# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
dilated = cv2.dilate(edges, np.ones((3,3), dtype=np.uint8))
cv2.imshow("Dilated", dilated)
cv2.waitKey(0)
cv2.imwrite('dilated.png', dilated)
# run the Hough transform
lines = cv2.HoughLinesP(dilated, rho=1, theta=np.pi/180, threshold=100, maxLineGap=20, minLineLength=50)
# segment the lines
delta = 10
h_lines, v_lines = segment_lines(lines, delta)
# draw the segmented lines
houghimg = img.copy()
for line in h_lines:
for x1, y1, x2, y2 in line:
color = [0,0,255] # color hoz lines red
cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)
for line in v_lines:
for x1, y1, x2, y2 in line:
color = [255,0,0] # color vert lines blue
cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)
cv2.imshow("Segmented Hough Lines", houghimg)
cv2.waitKey(0)
cv2.imwrite('hough.png', houghimg)
# find the line intersection points
Px = []
Py = []
for h_line in h_lines:
for v_line in v_lines:
px, py = find_intersection(h_line, v_line)
Px.append(px)
Py.append(py)
# draw the intersection points
intersectsimg = img.copy()
for cx, cy in zip(Px, Py):
cx = np.round(cx).astype(int)
cy = np.round(cy).astype(int)
color = np.random.randint(0,255,3).tolist() # random colors
cv2.circle(intersectsimg, (cx, cy), radius=2, color=color, thickness=-1) # -1: filled circle
cv2.imshow("Intersections", intersectsimg)
cv2.waitKey(0)
cv2.imwrite('intersections.png', intersectsimg)
# use clustering to find the centers of the data clusters
P = np.float32(np.column_stack((Px, Py)))
nclusters = 4
centers = cluster_points(P, nclusters)
print(centers)
# draw the center of the clusters
for cx, cy in centers:
cx = np.round(cx).astype(int)
cy = np.round(cy).astype(int)
cv2.circle(img, (cx, cy), radius=4, color=[0,0,255], thickness=-1) # -1: filled circle
cv2.imshow("Center of intersection clusters", img)
cv2.waitKey(0)
cv2.imwrite('corners.png', img)
最后,只有一个问题……为什么不将 OpenCV 中实现的Harris corner detector 用作cornerHarris()?因为它用非常少的代码就能很好地工作。我对灰度图像进行了阈值处理,然后进行了一点模糊以去除虚假的角落,然后,嗯...
这是使用以下代码生成的:
import cv2
import numpy as np
img = cv2.imread('image.png')
# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
r, gray = cv2.threshold(gray, 120, 255, type=cv2.THRESH_BINARY)
gray = cv2.GaussianBlur(gray, (3,3), 3)
# run harris
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)
# dilate the corner points for marking
dst = cv2.dilate(dst,None)
dst = cv2.dilate(dst,None)
# threshold
img[dst>0.01*dst.max()]=[0,0,255]
cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.imwrite('harris.png', img)
我认为通过一些小的调整,Harris 角点检测器可能比外推霍夫线交点更准确。