霍夫变换绝对是正确的选择。事实上,网格检测是介绍该技术时最流行的示例之一(请参阅here and here).
我建议采取以下步骤:
- 下采样
- blur
- 应用 Canny(您应该从使用的角度很好地猜测网格线的最小/最大可能长度)
- 膨胀边缘图像(canny 发现网格中分隔符的两个边界作为不同的边缘,膨胀将使这些再次合并)
- 侵蚀(现在我们的边框太粗了,霍夫会发现太多的线条)
- 应用霍夫线
- 合并相似的线
在最后一步,您有多种可能的方法可以选择,这很大程度上取决于您之后想要对结果做什么。例如,您可以使用找到的图像创建一个新的边缘图像,并再次应用侵蚀和霍夫,您可以使用基于傅立叶的东西,或者您可以只是简单地通过一些任意阈值(仅举几个)过滤线条。我实现了最后一个(因为从概念上讲这是最容易做的),这就是我所做的(尽管我完全不确定这是否是最好的方法):
- 为 rho 和 theta 值定义任意阈值
- 检查一条边在另一条边的这些阈值中出现的次数
- 从最相似的一行开始,我开始删除与其相似的行(这样我们将保留在某种意义上是相似组中“中间”行的行)
- 其余行是最终候选行
看代码,玩得开心:
import cv2
import numpy as np
filter = False
file_path = ''
img = cv2.imread(file_path)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,90,150,apertureSize = 3)
kernel = np.ones((3,3),np.uint8)
edges = cv2.dilate(edges,kernel,iterations = 1)
kernel = np.ones((5,5),np.uint8)
edges = cv2.erode(edges,kernel,iterations = 1)
cv2.imwrite('canny.jpg',edges)
lines = cv2.HoughLines(edges,1,np.pi/180,150)
if not lines.any():
print('No lines were found')
exit()
if filter:
rho_threshold = 15
theta_threshold = 0.1
# how many lines are similar to a given one
similar_lines = {i : [] for i in range(len(lines))}
for i in range(len(lines)):
for j in range(len(lines)):
if i == j:
continue
rho_i,theta_i = lines[i][0]
rho_j,theta_j = lines[j][0]
if abs(rho_i - rho_j) < rho_threshold and abs(theta_i - theta_j) < theta_threshold:
similar_lines[i].append(j)
# ordering the INDECES of the lines by how many are similar to them
indices = [i for i in range(len(lines))]
indices.sort(key=lambda x : len(similar_lines[x]))
# line flags is the base for the filtering
line_flags = len(lines)*[True]
for i in range(len(lines) - 1):
if not line_flags[indices[i]]: # if we already disregarded the ith element in the ordered list then we don't care (we will not delete anything based on it and we will never reconsider using this line again)
continue
for j in range(i + 1, len(lines)): # we are only considering those elements that had less similar line
if not line_flags[indices[j]]: # and only if we have not disregarded them already
continue
rho_i,theta_i = lines[indices[i]][0]
rho_j,theta_j = lines[indices[j]][0]
if abs(rho_i - rho_j) < rho_threshold and abs(theta_i - theta_j) < theta_threshold:
line_flags[indices[j]] = False # if it is similar and have not been disregarded yet then drop it now
print('number of Hough lines:', len(lines))
filtered_lines = []
if filter:
for i in range(len(lines)): # filtering
if line_flags[i]:
filtered_lines.append(lines[i])
print('Number of filtered lines:', len(filtered_lines))
else:
filtered_lines = lines
for line in filtered_lines:
rho,theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv2.imwrite('hough.jpg',img)