【问题标题】:Find sudoku grid using OpenCV and Python使用 OpenCV 和 Python 查找数独网格
【发布时间】:2018-08-03 21:17:42
【问题描述】:

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

我正在做的是:

  • 缩小图像
  • 模糊它
  • 应用高通滤波器(双边)
  • 使用自适应阈值对图像进行阈值处理
  • 一些膨胀和腐蚀

所有这些都给了我以下图像:

从现在开始,我需要检测网格,我找到了一些方法来做到这一点,但它们都没有给我足够强大的信心。

第一个是使用霍夫变换找到线,但我发现了很多虚假的线。

另一个是使用连接组件,这给了我最好的结果。我试图实现 RANSAC 作为获得正确质心的一种方式,但我没有得到很好的结果,也需要一段时间才能得到答案(“一段时间”不到 2 秒,但后来我想用它实时视频)。

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

【问题讨论】:

  • 您输入的图像是否总是倾斜的?数独的正面照片是合理的预期要求
  • 我知道,但我正在努力让它足够灵活,以便实时跳转到视频检测。您认为这不是一个好主意或起点吗?
  • 你知道网格并且你有足够多的点来适应它。无需完美识别所有路口。
  • 我会首先从正面图像开始(或者尽可能正面而不倾斜),然后在您满意后尝试倾斜图像并提高算法的鲁棒性。我认为 HoughTransform 可以给你很好的结果(IMO,它看起来很有希望你得到它)。您也可以尝试它的概率版本。 可能工作的其他想法是对图像进行 OCR,从而获得数字的“质心”(以及网格上正方形的中心)。将 Hough 与这些质心结合起来,您肯定可以更精确地过滤掉它。
  • 我不知道概率版本。我从 OpenCV 和图像处理开始,所以这似乎是一个不错的方法。谢谢!

标签: python opencv image-processing computer-vision


【解决方案1】:

霍夫变换绝对是要走的路。事实上,网格检测是介绍此技术时最流行的示例之一(参见herehere)。

我建议以下步骤:

  • 下采样
  • 模糊
  • 应用 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)

【讨论】:

  • 哇,这正是我想做的。我会尝试自己实现它,如果我卡住了,我会看你的代码。我想知道这种方法是否也适用于其他谜题,但我必须等待。谢谢!
  • 这就是精神!我在代码中添加了一些 cmets,以便更容易理解您是否必须返回它,但不要犹豫,询问您是否卡住了
猜你喜欢
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
  • 2020-01-26
  • 2012-10-28
  • 1970-01-01
  • 2015-07-31
  • 1970-01-01
  • 2017-11-11
相关资源
最近更新 更多