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

Python-opencv+KNN求解数独

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

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

输入输出demo

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

8dd4610dcb5d1a983eb200b0e7c5da15.png

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

b5ac39dc9b943f0cc4c485165d32c7ea.png

程序具体流程

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

1c6dafd0ffedf746170b7ed70fb9d0ed.png

读入图像后,根据求解轮廓信息找到数字所在位置,以及不包含数字的空白位置,提取数字信息通过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幅图放入文件夹中,如下图所示。

b3dd92efbdb83d0777d48e27f41c363d.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/552153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上位机与1200组态步骤_西门子1200的HSC的应用实例!

高速计数器寻址CPU 将每个高速计数器的测量值&#xff0c;存储在输入过程映像区内&#xff0c;数据类型为 32 位双整型有符号数&#xff0c;用户可以在设备组态中修改这些存储地址&#xff0c;在程序中可直接访问这些地址&#xff0c;但由于过程映像区受扫描周期影响&#xff0…

cmd代码表白_手把手教你把Python代码转成exe

【文末有福利】很多刚入门的盆友都有疑惑怎么把Python代码转成可执行的exe&#xff1f;&#xff0c;这里行哥统一解决一下这个问题1.在cmd里安装pyintsaller包2.在cmd里输入打包成exe的命令3.问题总结4. pyintsaller --help5. end1.在cmd里安装pyintsaller包第一步安装将Python…

searchview 点击后被覆盖_03实战Jacoco统计代码覆盖

本篇内容主要介绍&#xff1a;在gradle项目中添加jacoco来了解工程的代码覆盖情况。自动化的统计代码覆盖情况&#xff0c;有助于将焦点问题可视化&#xff0c;从而决定什么时候采取什么动作进行改进。Jacoco可以方面的添加在测试执结束&#xff0c;进行代码覆盖率的统计。如果…

电脑功耗软件_台式电脑配置详解!

近期有许多做设计的朋友问我&#xff0c;怎么配一台工作用的电脑&#xff0c;今天就给大家介绍一下电脑的各个组成部分。电脑核心配置&#xff0c;一般指CPU、主板、显卡三大件&#xff0c;一般笔记本电脑不需要考虑主板&#xff0c;只需考虑CPU和显卡两大件。1.CPU的选择现在的…

spring定时器突然不执行了_非标自动化设备之PLC定时器的时间和程序扫描周期

扫描周期是PLC本身执行指令时的周转时间。PLC执行指令按梯形图是从左向右&#xff0c;从上到下执行。每执行一遍就顺便扫描一遍所有元件状态并按指令执行相应动作。这个是需要时间吧&#xff0c;把这个时间叫扫描周期。并如此无限循环下去。当然扫描周期越短&#xff0c;PLC性能…

python入门小游戏之跳一跳_从零基础开始,用python手把手教你玩跳一跳小游戏,直接打出高分...

这对于很多人来说&#xff0c;可能是已经过时的游戏&#xff0c;但是对于python入门来说&#xff0c;却是一个非常值得学习的项目。TONOW收集了很多有关python入门的项目案例&#xff0c;包含了相应的教程和源码&#xff0c;哪怕你从未接触编程&#xff0c;也通过这些教程&…

如何反映两条曲线的拟合精度_用水平仪如何检测导轨的直线度?

1、水平仪的原理及用途&#xff1a; 气泡型水平仪的水准管是由玻璃制成&#xff0c;水准管内壁是一个具有一定曲率半径的曲面&#xff0c;管内装有液体&#xff0c;当水平仪发生倾斜时&#xff0c;水准管中气泡就向水平仪升高的一端移动&#xff0c;从而确定水平面的…

嵌套查询和连接查询的效率_Elasticsearch 7.x Nested 嵌套类型查询 | ES 干货

一、什么是 ES Nested 嵌套Elasticsearch 有很多数据类型&#xff0c;大致如下&#xff1a;基本数据类型&#xff1a; string 类型。ES 7.x 中&#xff0c;string 类型会升级为&#xff1a;text 和 keyword。keyword 可以排序&#xff1b;text 默认分词&#xff0c;不可以排序。…

c++程序设计_基于proteus的51单片机开发实例30-模块化程序设计

1. 基于proteus的51单片机开发实例30-模块化程序设计1.1. 实验目的模块化程序设计不知不觉我们的51单片机开发实例已经进行到第三十篇了&#xff0c;是时候进行一个总结和反思了&#xff0c;总结什么&#xff1f;反思什么呢&#xff1f;我们先从程序结构开始吧。总结在前面的29…

反序列化的时候出现eof exception_FastJson序列化时候出现了$ref?还不赶紧学习下...

最近在使用fastjson时候遇到一个问题&#xff0c;后台的数据转化为json字符串后发送到前端时候出现了$ref字样的东西&#xff0c;后来明白了这是引用&#xff0c;下面详细介绍下。一、一个简单的例子先来上一段代码&#xff0c;运行后打印的结果可能出乎意料&#xff0c;代码如…

阿里云 linux mysql数据库_Linux Mysql数据库安全配置

Linux Mysql数据库安全配置目录&#xff1a;1.修改mysql管理员账号root的密码(2种方法)2.修改mysql管理员账号root3.mysql管理员root账号密码遗忘解决办法(2种方法)4.创建数据库用户(3种方法)5.mysql数据库权限管理本地权限网络权限撤销权限删除用户一、修改mysql管理员账号roo…

快速锁屏电脑快捷键_一学就会的8个电脑快捷键一定要掌握!堪称“高效”神器...

日常工作中&#xff0c;电脑是我们常用的设备&#xff0c;为了提高工作效率常常需要牢记一些电脑快捷键&#xff0c;那么有哪些电脑快捷键值得我们牢记呢&#xff1f;下面分享8个实用的电脑快捷键&#xff0c;关键时刻省时又省力哟&#xff01;#1 WindowsL 快速锁屏一般用在暂时…

left join 重复数据_Python数据分析整理小节

一、数据读取1、读写数据库数据读取函数:pandas.read_sql_table(table_name, con, schemaNone, index_colNone, coerce_floatTrue, columnsNone)pandas.read_sql_query(sql, con, index_colNone, coerce_floatTrue)pandas.read_sql(sql, con, index_colNone, coerce_floatTrue,…

win10文件显示后缀名_Win10一开机,内存占用竟高达60%?你可以尝试这样做

说到win10一开机&#xff0c;内存占用竟高达60%&#xff0c;你是怎么处理的呢&#xff1f;深受其害的朋友就此大展身手了&#xff0c;瞅瞅&#xff01;A&#xff1a;我16G内存&#xff0c;也是开机占用了70%多。百度找了很多方法都是不相关的答案&#xff0c;后来发现了关闭快速…

LSTM(长短期记忆网络)的设计灵感和数学表达式

1、设计灵感 LSTM&#xff08;长短期记忆网络&#xff09;的设计灵感来源于传统的人工神经网络在处理序列数据时存在的问题&#xff0c;特别是梯度消失和梯度爆炸的问题。 在传统的RNN&#xff08;循环神经网络&#xff09;中&#xff0c;信息在网络中的传递是通过隐状态向量进…

个人博客代码_Jekyll + Github Pages 搭建个人免费博客

今天亲手通过 Jekyll 搭建了一套免费博客&#xff0c;搭建步骤其实超级简单。你不需要购买域名&#xff0c;也不需要购买服务器&#xff0c;就可以轻松拥有你自己的博客。Jekyll 的核心是一个文本转换引擎。它的方便之处在于支持多种文本标记语言&#xff1a;Markdown&#xff…

js计算排名_今天,我们讲一下,快速排名与黑帽SEO

做个有心人(第7篇)在Web3.0时代&#xff0c;想要获得流量&#xff0c;就必须使用广告手段&#xff0c;用什么广告手段&#xff0c;需要切合自身情况来做&#xff0c;比如说&#xff1a;SEO是免费的&#xff0c;而SEM就是付费的。而SEO快速排名是什么鬼?真的快吗?快速排名究竟…

黑马h5学习代码_如何零基础制作酷炫实用的H5页面

H5页面已经成为了当下移动端主要的宣传方式,一个好的H5页面有极高的营销价值,无论是企业还是个人都非常需要。制作一个炫酷的H5页面一定要会写代码吗,下面千锋网络营销小编就给大家分享如何零基础制作炫酷实用的H5页面。支持H5的Web APP迅猛发展很重要的一点就是APP中的内容产生…

bin文件如何编辑_如何为高通固件创建rawprogram0和patch0文件

这是一个分步教程&#xff0c;显示如何为Qualcomm固件创建rawprogram0.xml和patch0.xml文件。要求下载并安装Python https://www.python.org/downloads/release/python-2710/下载高通GPTtool [ 登录/注册免费下载]下载Notepad https://notepad-plus-plus.org/downloads/来自…

iframe 页面富文本框数据怎么保存_文字太多PPT怎么做都丑?估计是没注意这些细节!...

秋叶 PPT 双 11 大促返场最后 1 天全场精品课享年度超值价千万别错过啦&#xff01;作者&#xff1a;洁洁编辑&#xff1a;躺糖大家好&#xff0c;我是洁洁&#xff01;作为每天倾听你们的困惑的小编之一&#xff0c;我 get 到了一个你们平常做 PPT 会碰到的最头疼的问题&#…