Python-opencv+KNN求解数独

最近一直在玩数独,突发奇想实现图像识别求解数独,输入到输出平均需要0.5s。

整体思路大概就是识别出图中数字生成list,然后求解。

输入输出demo

数独采用的是微软自带的Microsoft sudoku软件随便截取的图像,如下图所示:

经过程序求解后,得到的结果如下图所示:

程序具体流程

程序整体流程如下图所示:

读入图像后,根据求解轮廓信息找到数字所在位置,以及不包含数字的空白位置,提取数字信息通过KNN识别,识别出数字;无数字信息的在list中置0;生成未求解数独list,之后求解数独,将信息在原图中显示出来。

# -*-coding:utf-8-*-

import os

import cv2 as cv

import numpy as np

import time

####################################################

#寻找数字生成list

def find_dig_(img, train_set):

if img is None:

print("无效的图片!")

os._exit(0)

return

_, thre = cv.threshold(img, 230, 250, cv.THRESH_BINARY_INV)

_, contours, hierarchy = cv.findContours(thre, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)

sudoku_list = []

boxes = []

for i in range(len(hierarchy[0])):

if hierarchy[0][i][3] == 0: # 表示父轮廓为 0

boxes.append(hierarchy[0][i])

# 提取数字

nm = []

for j in range(len(boxes)): # 此处len(boxes)=81

if boxes[j][2] != -1:

x, y, w, h = cv.boundingRect(contours[boxes[j][2]])

nm.append([x, y, w, h])

# 在原图中框选各个数字

cropped = img[y:y + h, x:x + w]

im = img_pre(cropped)#预处理

AF = incise(im)#切割数字图像

result = identification(train_set, AF, 7)#knn识别

sudoku_list.insert(0, int(result))#生成list

else:

sudoku_list.insert(0, 0)

if len(sudoku_list) == 81:

sudoku_list= np.array(sudoku_list)

sudoku_list= sudoku_list.reshape((9, 9))

print("old_sudoku -> \n", sudoku_list)

return sudoku_list, contours, hierarchy

else:

print("无效的图片!")

os._exit(0)

######################################################

#KNN算法识别数字

def img_pre(cropped):

# 预处理数字图像

im = np.array(cropped) # 转化为二维数组

for i in range(im.shape[0]): # 转化为二值矩阵

for j in range(im.shape[1]):

# print(im[i, j])

if im[i, j] != 255:

im[i, j] = 1

else:

im[i, j] = 0

return im

# 提取图片特征

def feature(A):

midx = int(A.shape[1] / 2) + 1

midy = int(A.shape[0] / 2) + 1

A1 = A[0:midy, 0:midx].mean()

A2 = A[midy:A.shape[0], 0:midx].mean()

A3 = A[0:midy, midx:A.shape[1]].mean()

A4 = A[midy:A.shape[0], midx:A.shape[1]].mean()

A5 = A.mean()

AF = [A1, A2, A3, A4, A5]

return AF

# 切割图片并返回每个子图片特征

def incise(im):

# 竖直切割并返回切割的坐标

a = [];

b = []

if any(im[:, 0] == 1):

a.append(0)

for i in range(im.shape[1] - 1):

if all(im[:, i] == 0) and any(im[:, i + 1] == 1):

a.append(i + 1)

elif any(im[:, i] == 1) and all(im[:, i + 1] == 0):

b.append(i + 1)

if any(im[:, im.shape[1] - 1] == 1):

b.append(im.shape[1])

# 水平切割并返回分割图片特征

names = locals();

AF = []

for i in range(len(a)):

names['na%s' % i] = im[:, range(a[i], b[i])]

if any(names['na%s' % i][0, :] == 1):

c = 0

else:

for j in range(names['na%s' % i].shape[0]):

if j < names['na%s' % i].shape[0] - 1:

if all(names['na%s' % i][j, :] == 0) and any(names['na%s' % i][j + 1, :] == 1):

c = j

break

else:

c = j

if any(names['na%s' % i][names['na%s' % i].shape[0] - 1, :] == 1):

d = names['na%s' % i].shape[0] - 1

else:

for j in range(names['na%s' % i].shape[0]):

if j < names['na%s' % i].shape[0] - 1:

if any(names['na%s' % i][j, :] == 1) and all(names['na%s' % i][j + 1, :] == 0):

d = j + 1

break

else:

d = j

names['na%s' % i] = names['na%s' % i][range(c, d), :]

AF.append(feature(names['na%s' % i])) # 提取特征

for j in names['na%s' % i]:

pass

return AF

# 训练已知图片的特征

def training():

train_set = {}

for i in range(9):

value = []

for j in range(15):

ima = cv.imread('E:/test_image/knn_test/{}/{}.png'.format(i + 1, j + 1), 0)

im = img_pre(ima)

AF = incise(im)

value.append(AF[0])

train_set[i + 1] = value

return train_set

# 计算两向量的距离

def distance(v1, v2):

vector1 = np.array(v1)

vector2 = np.array(v2)

Vector = (vector1 - vector2) ** 2

distance = Vector.sum() ** 0.5

return distance

# 用最近邻算法识别单个数字

def knn(train_set, V, k):

key_sort = [11] * k

value_sort = [11] * k

for key in range(1, 10):

for value in train_set[key]:

d = distance(V, value)

for i in range(k):

if d < value_sort[i]:

for j in range(k - 2, i - 1, -1):

key_sort[j + 1] = key_sort[j]

value_sort[j + 1] = value_sort[j]

key_sort[i] = key

value_sort[i] = d

break

max_key_count = -1

key_set = set(key_sort)

for key in key_set:

if max_key_count < key_sort.count(key):

max_key_count = key_sort.count(key)

max_key = key

return max_key

# 生成数字

def identification(train_set, AF, k):

result = ''

for i in AF:

key = knn(train_set, i, k)

result = result + str(key)

return result

######################################################

######################################################

#求解数独

def get_next(m, x, y):

# 获得下一个空白格在数独中的坐标。

:param m 数独矩阵

:param x 空白格行数

:param y 空白格列数

"""

for next_y in range(y + 1, 9): # 下一个空白格和当前格在一行的情况

if m[x][next_y] == 0:

return x, next_y

for next_x in range(x + 1, 9): # 下一个空白格和当前格不在一行的情况

for next_y in range(0, 9):

if m[next_x][next_y] == 0:

return next_x, next_y

return -1, -1 # 若不存在下一个空白格,则返回 -1,-1

def value(m, x, y):

# 返回符合"每个横排和竖排以及九宫格内无相同数字"这个条件的有效值。

i, j = x // 3, y // 3

grid = [m[i * 3 + r][j * 3 + c] for r in range(3) for c in range(3)]

v = set([x for x in range(1, 10)]) - set(grid) - set(m[x]) - \

set(list(zip(*m))[y])

return list(v)

def start_pos(m):

# 返回第一个空白格的位置坐标

for x in range(9):

for y in range(9):

if m[x][y] == 0:

return x, y

return False, False # 若数独已完成,则返回 False, False

def try_sudoku(m, x, y):

# 试着填写数独

for v in value(m, x, y):

m[x][y] = v

next_x, next_y = get_next(m, x, y)

if next_y == -1: # 如果无下一个空白格

return True

else:

end = try_sudoku(m, next_x, next_y) # 递归

if end:

return True

m[x][y] = 0 # 在递归的过程中,如果数独没有解开,

# 则回溯到上一个空白格

def sudoku_so(m):

x, y = start_pos(m)

try_sudoku(m, x, y)

print("new_sudoku -> \n", m)

return m

###################################################

# 将结果绘制到原图

def draw_answer(img, contours, hierarchy, new_sudoku_list ):

new_sudoku_list = new_sudoku_list .flatten().tolist()

for i in range(len(contours)):

cnt = contours[i]

if hierarchy[0, i, -1] == 0:

num = new_soduku_list.pop(-1)

if hierarchy[0, i, 2] == -1:

x, y, w, h = cv.boundingRect(cnt)

cv.putText(img, "%d" % num, (x + 19, y + 56), cv.FONT_HERSHEY_SIMPLEX, 1.8, (0, 0, 255), 2) # 填写数字

cv.imwrite("E:/answer.png", img)

if __name__ == '__main__':

t1 = time.time()

train_set = training()

img = cv.imread('E:/test_image/python_test_img/Sudoku.png')

img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

sudoku_list, contours, hierarchy = find_dig_(img_gray, train_set)

new_sudoku_list = sudoku_so(sudoku_list)

draw_answer(img, contours, hierarchy, new_sudoku_list )

print("time :",time.time()-t1)

PS:

使用KNN算法需要创建训练集,数独中共涉及9个数字,“1,2,3,4,5,6,7,8,9”各15幅图放入文件夹中,如下图所示。

到此这篇关于Python图像识别+KNN求解数独的实现的文章就介绍到这了,更多相关Python KNN求解数独内容请搜索WEB开发者以前的文章或继续浏览下面的相关文章希望大家以后多多支持WEB开发者!

python图像数独_Python图像识别+KNN求解数独的实现相关推荐

  1. python图像清晰度_python 图像判断,清晰度(明暗),彩色与黑白实例

    1,判断图像清晰度,明暗, 原理,Laplacian算法.偏暗的图片,二阶导数小,区域变化小:偏亮的图片,二阶导数大,区域变化快. import cv2 def getImageVar(imgPath ...

  2. python图像数独_Python判断有效的数独算法示例

    本文实例讲述了Python判断有效的数独算法.分享给大家供大家参考,具体如下: 一.题目 判断一个 9x9 的数独是否有效.只需要根据以下规则,验证已经填入的数字是否有效即可. 1. 数字 1-9 在 ...

  3. python图像检测_Python+Opencv识别两张相似图片

    在网上看到python做图像识别的相关文章后,真心感觉python的功能实在太强大,因此将这些文章总结一下,建立一下自己的知识体系. 当然了,图像识别这个话题作为计算机科学的一个分支,不可能就在本文简 ...

  4. python图像边缘检测_python opencv实现图像边缘检测

    本文利用python opencv进行图像的边缘检测,一般要经过如下几个步骤: 1.去噪 如cv2.GaussianBlur()等函数: 2.计算图像梯度 图像梯度表达的是各个像素点之间,像素值大小的 ...

  5. python图像锐化_Python图像处理介绍--图像模糊与锐化

    欢迎关注 "小白玩转Python",发现更多 "有趣" 引言 在之前的文章中,我们讨论了边缘检测内核.在本文我们将讨论如何在图像上应用模糊与锐化内核,将这些内核 ...

  6. python最小公倍数 菜鸟_Python实现的求解最小公倍数算法示例

    本文实例讲述了Python实现的求解最小公倍数算法.分享给大家供大家参考,具体如下: 简单分析了一下,前面介绍的最大公约数的求解方法跟最小公倍数求解方法类似,只需要改一个简单的条件,然后做一点简单的其 ...

  7. python图像归一化_python 归一化_Python也能成为毕加索?我用Python给小姐姐画了幅油画...

    点击上方"菜鸟学Python",选"星标"公众号 重磅干货,第一时间到达 小编的舍友最近交了一个女朋友,是念艺术系的,擅长画画!长的好看又漂亮,舍友经常在我耳边 ...

  8. python图像几何变换_python 图像工具opencv3实例(对象识别和增强现实)1-图像几何转换...

    安装OpenCV-Python安装 本教程基于python3,ubuntu 16,在windows环境可能可以运行. Linux#!pythonpip3 install opencv-python W ...

  9. python求最小公倍数_Python实现的求解最小公倍数算法示例

    这篇文章主要介绍了Python实现的求解最小公倍数算法,涉及Python数值运算.判断等相关操作技巧,需要的朋友可以参考下 本文实例讲述了Python实现的求解最小公倍数算法.分享给大家供大家参考,具 ...

最新文章

  1. python的pip文件目录_python基础—pip指定安装目录
  2. PAT_B_1006 换个格式输出整数
  3. hdu 1722 Cake 数学题
  4. .net ajax批量删除,asp.net 全部选中与取消操作,选中后的删除(ajax)实现无刷新效果...
  5. GPU云服务器+tensorboard
  6. Http协议之报文·方法·状态码
  7. android冻结命令,在Android上使用冻结tensorflow图中的变量
  8. 央视美女记者王冰冰身上,我看到了一个职场人特别需要的能力
  9. 浅谈SQL Server中的快照
  10. 基于 cm-11 源码编译模拟器
  11. ATO(韩商爱拓)1Gbit SPI NAND Flash领先全球
  12. F.dropout()与nn.dropout()
  13. SQL取整与时间差值返回
  14. excel中COUNTIFS函数用法
  15. 软件外包那些坑(一)
  16. 从零玩转HTML5+CSS3项目实战-跟着李南江学编程
  17. JDK1.8之Lambda
  18. 《C++新经典Linux C++通信架构实战》第1章 课程介绍
  19. 计算机软件选修课选什么好,大学选修课有哪些 选什么好
  20. 腾讯T14级SQL首席专家开源分布式数据库架构实践手册

热门文章

  1. pluto实现分析(22)
  2. mysql聚簇索引 和主键的区别_[MySQL] innoDB引擎的主键与聚簇索引
  3. python实现路由功能_python 实现重启路由器
  4. map flatmap mappartition flatMapToPair四种用法区别
  5. [转载] Java8-Stream API 详解
  6. Python | Lambda函数与示例
  7. python闭包怎么理解_Python 闭包的理解
  8. php mysql 权重_PHP对MySql的常用操作
  9. 大一python编程题_请教python编程问题(作业就剩这几道题了)
  10. python图形界面库哪个好_8个必备的Python GUI库