Python图像识别+KNN求解数独的实现

1.概述

数独作为一种流行的数字游戏,一直以来都备受青睐。Python作为一种高效的编程语言戏,也能够很好地应用于数独求解当中。本文将介绍如何通过Python实现图像识别与KNN算法来求解数独难题。

2.图像处理

2.1 Pillow库的运用

我们可以使用Pillow库来读取数独的图片数据,通过图片数据提取数独各个格子的数字信息。代码如下:

from PIL import Image

# 读取数独图片

img = Image.open('sudoku.png')

img.load()

img.show()

读取数独图片的代码很简单,接下来我们需要对图片进行处理,将其转化为数字矩阵。我们首先需要将图片进行二值化的处理,即将所有的图像颜色都转化为黑白两种。代码如下:

# 将图片转化为黑白二值化图片

img = img.convert('1')

# 显示二值化图片

img.show()

接下来我们需要确定图片中数独的边缘信息,以便对图片进行切割。我们可以使用边缘检测算法来获得图片的边缘信息。此处我们使用了Sobel算子进行边缘检测。代码如下:

from scipy import ndimage

# Sobel算子边缘检测

sobel_kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]

edge_img = ndimage.convolve(img, sobel_kernel, mode='constant', cval=0.0)

# 显示Sobel边缘检测图片

img_out = Image.fromarray(edge_img)

img_out.show()

接下来我们可以通过边缘检测的结果对图片进行切割。我们可以使用Pillow库提供的crop函数来对原图进行切割,并获取数独的各个小方格。代码如下:

# 进行切割

sudoku_list = []

for j in range(0, img_out.height, img_out.height // 9):

row = []

for i in range(0, img_out.width, img_out.width // 9):

box = (i, j, i + img_out.width // 9, j + img_out.height // 9)

tile = img.crop(box)

row.append(tile)

sudoku_list.append(row)

如此,我们就成功地获取了数独各个小方格,可以继续对其进行处理。

2.2 KNN算法的运用

我们可以使用KNN算法来进行数独的数字识别。KNN算法的基本思想是,对于待分类的样本,将其与所有已分好类的样本进行比较,选取K个最相似的已分好类的样本,对待分类样本进行归类。在数独中,我们可以将每个数字的图片视为一个样本,并对其进行归类。代码如下:

import os

import numpy as np

from sklearn.neighbors import KNeighborsClassifier

from sklearn.model_selection import train_test_split

# 将训练集中的数字图片读入并转换成矩阵

digits = []

labels = []

for digit in range(10):

for filename in os.listdir(f'data/{digit}'):

im = Image.open(f'data/{digit}/{filename}')

arr = np.array(im).reshape(-1)

digits.append(arr)

labels.append(digit)

# 划分训练集与测试集

train_digits, test_digits, train_labels, test_labels = train_test_split(digits, labels, test_size=0.3, random_state=2)

# 训练KNN分类器

knn = KNeighborsClassifier(n_neighbors=3, p=2)

knn.fit(train_digits, train_labels)

# 测试KNN分类器并输出准确率

accuracy = knn.score(test_digits, test_labels)

print(f'Accuracy: {accuracy}')

最终,我们可以得到KNN算法的准确率。我们可以将其用于数独的识别,如下:

# 处理数独,识别各个小方格的数字

sudoku_matrix = []

for row in sudoku_list:

row_res = []

for tile in row:

tile_arr = np.array(tile).reshape(-1)

digit = knn.predict([tile_arr])[0]

row_res.append(digit)

sudoku_matrix.append(row_res)

至此,图像处理部分已经完成了。我们获得了数独各个小方格的数字信息,接下来可以通过算法对其进行求解。

3. 求解数独

3.1 数独求解算法

数独求解算法有很多种,本文采用递归的求解方式。具体实现方法如下:

def solve_sudoku(matrix):

# 找到未填充的位置

row, col = find_unfilled(matrix)

# 如果所有位置都填满了,则返回True

if row == col == -1:

return True

# 尝试填入数字

for num in range(1, 10):

if is_valid(matrix, row, col, num):

matrix[row][col] = num

# 继续递归

if solve_sudoku(matrix):

return True

else:

# 如果出现无解,则回溯

matrix[row][col] = 0

# 如果所有数字都尝试过,都无法得到解,返回False

return False

我们首先尝试找到未填充的位置,然后依次填入数字。如果填入的数字满足数独规则,我们将这个数字填入,并继续递归填下一个位置。如果最终填满了所有的位置,则返回True,表示已经找到解;如果出现无解,则回溯到上一个格子,重新尝试其他数字。

3.2 算法求解

我们将之前得到的数独数字矩阵作为求解的初始值,并使用求解算法进行求解。代码如下:

# 使用算法求解数独

solve_sudoku(sudoku_matrix)

至此,我们已经成功地通过Python实现了数独的图像识别与求解。我们将上述代码整合成一个完整的程序:

from PIL import Image

import os

import numpy as np

from scipy import ndimage

from sklearn.neighbors import KNeighborsClassifier

from sklearn.model_selection import train_test_split

# Sobel算子边缘检测

def sobel_edge_detection(img):

sobel_kernel = [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]

edge_img = ndimage.convolve(img, sobel_kernel, mode='constant', cval=0.0)

return Image.fromarray(edge_img)

# 切割数独图片

def crop_sudoku(img):

# 创建数独列表

sudoku_list = []

for j in range(0, img.height, img.height // 9):

row = []

for i in range(0, img.width, img.width // 9):

box = (i, j, i + img.width // 9, j + img.height // 9)

tile = img.crop(box)

row.append(tile)

sudoku_list.append(row)

return sudoku_list

# 寻找未填充的位置

def find_unfilled(matrix):

for i in range(len(matrix)):

for j in range(len(matrix)):

if matrix[i][j] == 0:

return i, j

return -1, -1

# 判断数字在该位置是否有效

def is_valid(matrix, row, col, num):

# 判断行和列是否有重复数字

for i in range(9):

if matrix[row][i] == num or matrix[i][col] == num:

return False

# 判断9宫格中是否有重复数字

row_start = (row // 3) * 3

col_start = (col // 3) * 3

for i in range(row_start, row_start + 3):

for j in range(col_start, col_start + 3):

if matrix[i][j] == num:

return False

# 数字有效

return True

# 递归求解数独

def solve_sudoku(matrix):

# 找到未填充的位置

row, col = find_unfilled(matrix)

# 如果所有位置都填满了,则返回True

if row == col == -1:

return True

# 尝试填入数字

for num in range(1, 10):

if is_valid(matrix, row, col, num):

matrix[row][col] = num

# 继续递归

if solve_sudoku(matrix):

return True

else:

# 如果出现无解,则回溯

matrix[row][col] = 0

# 如果所有数字都尝试过,都无法得到解,返回False

return False

# 读取训练集中的数字图片,并将其转换成矩阵形式

digits = []

labels = []

for digit in range(10):

for filename in os.listdir(f'data/{digit}'):

im = Image.open(f'data/{digit}/{filename}')

arr = np.array(im).reshape(-1)

digits.append(arr)

labels.append(digit)

# 划分训练集和测试集

train_digits, test_digits, train_labels, test_labels = train_test_split(digits, labels, test_size=0.3, random_state=2)

# 训练KNN分类器

knn = KNeighborsClassifier(n_neighbors=3, p=2)

knn.fit(train_digits, train_labels)

# 读取数独图片,进行预处理

img = Image.open('sudoku.png')

img.load()

# 将图片转化为黑白二值化图片

img = img.convert('1')

# Sobel算子边缘检测

edge_img = sobel_edge_detection(np.array(img))

img_out = Image.fromarray(edge_img)

# 切割数独图片

sudoku_list = crop_sudoku(img_out)

# 处理数独,识别各个小方格的数字

sudoku_matrix = []

for row in sudoku_list:

row_res = []

for tile in row:

tile_arr = np.array(tile).reshape(-1)

digit = knn.predict([tile_arr])[0]

row_res.append(digit)

sudoku_matrix.append(row_res)

# 使用算法求解数独

solve_sudoku(sudoku_matrix)

# 打印求解结果

for row in sudoku_matrix:

print(row)

4. 总结

本文介绍了如何通过Python实现数独的图像识别与求解。我们使用了Pillow库对图片进行处理,使用KNN算法对数独各个小方格的数字进行识别,并使用递归的方式求解数独的答案。通过实现本文所述的方法,我们可以更快、更方便地玩数独。当然,本文使用的图像处理和算法也可以应用于其他数字识别的问题。

后端开发标签