使用OpenCV和Python查找数独网格

nik*_*328 6 python opencv image-processing computer-vision

我正在尝试使用OpenCV检测数独谜题中的网格,但我遇到了最后一步的麻烦(我猜).

我在做的是:

  • 降低图像的范围
  • 模糊它
  • 应用高通滤波器(双边)
  • 使用自适应阈值对图像进行阈值处理
  • 一些扩张和糜烂

这一切都给了我以下图像:

原始图像下采样和模糊.

在此输入图像描述

从现在开始,我需要检测网格,并且我找到了一些如何做到这一点的方法,但是没有一个方法让我有足够的信心.

第一个是使用Hough变换找到线,但我发现了很多虚假线.

霍夫变换.

另一个是使用连接组件,这给了我最好的结果.我试图实现RANSAC,以此来获得正确的重心,但我并没有很好的效果,也需要一段时间才能得到答案("一段时间"小于2秒,但后来我想用它实时视频).

在此输入图像描述

在此输入图像描述

知道如何做到这一点?我的意思是,我怎么能丢弃错误的质心并开始解决数独?

use*_*898 10

霍夫变换绝对是必经之路.实际上,网格检测是引入此tehcnique时最常见的例子之一(参见 此处此处).

我建议采取以下步骤:

  • 下采样
  • 模糊
  • 应用Canny(您应该从使用的角度很好地猜测网格线的最小/最大可能长度)
  • 扩大边缘图像(canny在网格中找到分隔符的边界作为不同的边,扩张将使这些合并再次合并)
  • 侵蚀(现在我们的边界太厚了,霍夫会找到太多的线条)
  • 申请HoughLines
  • 合并相似的行

在最后一步,您有许多可能的方法,这在很大程度上取决于您之后想要对结果做什么.例如,您可以使用找到的图像创建新的边缘图像并再次应用侵蚀和霍夫,您可以使用基于傅里叶的东西,或者您可以简单地按照某些任意阈值过滤线条(仅举几例).我实现了最后一个(从概念上说这是最简单的一个),这就是我所做的(虽然我不确定这是否是最好的方法):

  • 为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)
Run Code Online (Sandbox Code Playgroud)

结果