# 基于 OpenCV 及 Python 的数独问题识别与求解
本人平时比较喜欢玩数独,但比较难的数独经常一做就是半个多小时。这学期选了一门”计算机视觉”课,课程最后要做一个项目,正好我就想能不能写一个程序,用手机拍个照或者截个图,放进程序里跑一下,自动就把题目识别然后解出来了,and it was so.
先来看一个 [opencv](https://so.csdn.net/so/search?from=pc_blog_highlight&q=opencv) 里自带的数独图片:
![](https://www.writebug.com/myres/static/uploads/2021/12/8/f8db65d4360d2bd6f40be1a6f1adda7a.writebug)
程序的最终目的就是将这种图像转化为一个矩阵或者说二维数组,来要告诉计算机在 9x9 的方格中哪个位置有数字、数字是什么,就像下面的形式:
```
[[0 0 0 6 0 4 7 0 0]
[7 0 6 0 0 0 0 0 9]
[0 0 0 0 0 5 0 8 0]
[0 7 0 0 2 0 0 9 3]
[8 0 0 0 0 0 0 0 5]
[4 3 0 0 1 0 0 7 0]
[0 5 0 2 0 0 0 0 0]
[3 0 0 0 0 0 2 0 8]
[0 0 2 3 0 1 0 0 0]]
```
在这个二维数组里,用 0 表示要填写的数字,用 1-9 表示识别出来的题目数字。
了解主要任务之后,下面就是具体的步骤了:
1. 图像预处理
2. 识别边框
3. 图像校正
4. 提取数字
5. 识别数字
6. 计算答案
这里使用的语言及版本如下,关于开发环境的配置可以参考本人[另一篇文章](http://blog.csdn.net/howlclat/article/details/78819760):
- Python 版本:**3.6.3** (Anaconda 3)
- OpenCV 版本:**3.3.1**
第一篇文章主要讲解图像预处理部分,并给出使用 matplotlib 显示 OpenCV 图像的方法。
## 图像预处理
首先是使用 cv2.imread() 函数读取原图片,然后使用 cv2.cvtColor() 灰度化,因为颜色信息不重要。之后是使用滤波算法进行降噪处理,因为滤波处理对于识别效果有很大影响。常用的有均值滤波、高斯滤波、中值滤波、双边滤波等,具体使用方法可以参考[这里](https://docs.opencv.org/master/d4/d13/tutorial_py_filtering.html)。本文使用高斯滤波及中值滤波,但是滤波参数不能适用于所有情况的图像, **必须根据图像尺寸和清晰度做调整** 。
```python
img_original = cv2.imread('./images/p1.png')
img_gray = cv2.cvtColor(img_original, cv2.COLOR_BGR2GRAY)
img_Blur = cv2.medianBlur(img_gray, 3)
img_Blur = cv2.GaussianBlur(img_Blur, (3, 3), 0)
```
可以看到,[Python](https://so.csdn.net/so/search?from=pc_blog_highlight&q=Python) 版本的 [OpenCV](https://so.csdn.net/so/search?from=pc_blog_highlight&q=OpenCV) 与 C++ 版本有个很大的区别,就是处理后的图像一般作为函数的返回值(之一),而 C++ 需要将原图像和处理后图像的地址作为参数传入,这是 [Python](https://so.csdn.net/so/search?from=pc_blog_highlight&q=Python) 中函数可以返回多个值的特点决定的。
此图片在预处理阶段要解决的主要问题是亮度不均衡,图像左下角比较暗,如果直接进行二值化效果较差,对滤波后图像取 100 作为阈值处理得到中间的图像,左下部分图像缺失;若进行自适应阈值操作,可以得到较好的效果,但左下部分线条亦有缺失:
![](https://www.writebug.com/myres/static/uploads/2021/12/8/eae84079a6df4e18f694608c706bb317.writebug)
参考 [stackflow](https://stackoverflow.com/questions/10196198/how-to-remove-convexity-defects-in-a-sudoku-square) 上一位大神的方法,以圆形作为结构化元素进行闭操作,将灰度图像除以闭操作后图像,再进行归一化。
```python
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
close = cv2.morphologyEx(img_Blur, cv2.MORPH_CLOSE, kernel)
div = np.float32(img_Blur) / close
img_brightness_adjust = np.uint8(cv2.normalize(div, div, 0, 255, cv2.NORM_MINMAX))
```
第一句使用 `cv2.getStructuringElement()` 获取结构元素,可以使用椭圆、矩形、十字形三种,这里使用椭圆。第二句使用 `cv2.morphologyEx()` 对原图使用刚才的结构元素进行闭操作。将灰度图像除以闭操作后图像,但要先转换成 float32 类型才能除,`/` 运算符相当于调用 numpy 中的 divide 函数,即点除,又即每个像素点分别相除,且只保留整数部分。最后将除的结果归一化到 [0, 255] 区间内,并转换回 无符号 8 位整数(uint8) 类型。
结果如下图,效果拔群:
![](https://www.writebug.com/myres/static/uploads/2021/12/8/f32ce9c8313d770b1b02e568b2ed34b3.writebug)
下一步是二值化,[OpenCV](https://so.csdn.net/so/search?from=pc_blog_highlight&q=OpenCV) 中二值化有简单阈值、自适应阈值、Otsu’s 二值化等方法,可以参考[这里](https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html)。简单阈值是一种全局性的阈值,整个图像都和这个阈值比较。而自适应阈值可以看成一种局部性的阈值,通过规定一个区域大小,比较这个点与区域大小里面像素点的平均值(或者其他特征)的大小关系确定这个像素点是属于黑或者白。这里使用自适应阈值方法,代码如下:
```python
img_thresh = cv2.adaptiveThreshold(img_brightness_adjust, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 7)
```
使用 `cv2.adaptiveThreshold()` 函数, 其中 cv2.ADAPTIVE_THRESH_GAUSSIAN_C 代表在小区域内阈值选取为加权和,以高斯分布确定权重。cv2.THRESH_BINARY_INV 代表二值化的结果取反,为下一步形态学处理做准备。
![](https://www.writebug.com/myres/static/uploads/2021/12/8/32aa189c6dd50d82411f4342eeb035fb.writebug)
## 使用 matplotlib 显示 OpenCV 图像
Matplotlib 是 [Python](https://so.csdn.net/so/search?from=pc_blog_highlight&q=Python) 中最常用的可视化工具之一,可以非常方便地创建海量类型地 2D 图表和一些基本的 3D 图表。也可以方便地显示图像。
众所周知,OpenCV 中彩色图像的像素是以 Bule Green Red 为顺序的,也就是 BGR,而普通的图像则是采用 RGB 顺序,因此要在其他地方(如 Qt、matplotlib 中)使用 OpenCV 的彩色图像,就必须转换格式。
我将使用 matplotlib 显示 OpenCV 图像的功能写成了一个函数:
```python
def plotImg(img, title=""):
if img.ndim == 3:
b, g, r = cv2.split(img)
img = cv2.merge([r, g, b])
plt.imshow(img), plt.axis("off")
else:
plt.imshow(img, cmap='gray'), plt.axis("off")
plt.title(title)
plt.show()
```
这里先判断输入图像是不是 3 维,即彩色图,是的话进行转换,不是的话作为灰度图显示。转换代码中分割再重新组合的方法,还可以使用 `img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)` 。注意如果灰度图不加 `cmap='gray'` 这个参数,会显示为“热度图”。
当然不要忘了开头加上:
```python
import cv2
from matplotlib import pyplot as plt12
```
matplotlib 还可以像 MATLAB 里一样方便的显示多个图像,例如这里并列显示两幅图像:
```python
def plotImgs(img1, img2):
if img1.ndim == 3:
b, g, r = cv2.split(img1)
img1 = cv2.merge([r, g, b])
plt.subplot(121), plt.imshow(img1), plt.axis("off")
else:
plt.subplot(121), plt.imshow(img1, cmap='gray'), plt.axis("off")
if img2.ndim == 3:
b, g, r = cv2.split(img2)
img2 = cv2.merge([r, g, b])
plt.subplot(122), plt.imshow(img2), plt.axis("off")
else:
plt.subplot(122), plt.imshow(img2, cmap='gray'), plt.axis("off")
plt.show()
```
其中 `plt.subplot(121)` 就代表一行两列图像中的第一个。
编写计算机视觉程序时可以使用上述函数将中间过程图像显示出来,方便观察与调试。例如:
![](https://www.writebug.com/myres/static/uploads/2021/12/8/db63c3728b7175bebdb1816a1ce21ada.wri
没有合适的资源?快使用搜索试试~ 我知道了~
温馨提示
首先是使用 cv2.imread() 函数读取原图片,然后使用 cv2.cvtColor() 灰度化。之后是使用滤波算法进行降噪处理,因为滤波处理对于识别效果有很大影响。数字提取的主要步骤是:将含有数字的图像,分为 9*9 即 81 个大小相同的方格,遍历这个 81 个位置,判断每个方格中是否有数字,记录数字所在位置(0~80),保存在 indexes_numbers 中,数字储存在数组 sudoku 中。
资源推荐
资源详情
资源评论
收起资源包目录
100011308-基于 OpenCV 及 Python 的数独问题识别与求解.zip (42个子文件)
opsd
extractNumber.py 4KB
samples.npy 703KB
label_mnist.npy 148KB
LICENSE 1KB
sudoku_main.py 4KB
label.npy 2KB
.idea
dictionaries
Cui.xml 184B
sudoku_opencv_py.iml 453B
other.xml 186B
vcs.xml 180B
workspace.xml 17KB
misc.xml 293B
inspectionProfiles
Project_Default.xml 410B
modules.xml 284B
sudoku_solver.py 3KB
knn_ocr.py 1KB
correction.py 10KB
plotCVImg.py 961B
trainDataProcess.py 2KB
images
h1.png 214KB
h6.png 296KB
c3.png 245KB
h2.png 274KB
p1.png 27KB
pe.png 30KB
p2.png 23KB
c4.png 423KB
c2.png 1.39MB
a2.png 28KB
a1.png 28KB
c5.png 431KB
h3.png 746KB
p3.png 68KB
h5.png 292KB
digits.png 704KB
samples_mnist.npy 57.78MB
__pycache__
sudoku_solver.cpython-36.pyc 2KB
plotCVImg.cpython-36.pyc 946B
correction.cpython-36.pyc 6KB
knn_ocr.cpython-36.pyc 2KB
extractNumber.cpython-36.pyc 3KB
README.md 24KB
共 42 条
- 1
资源评论
神仙别闹
- 粉丝: 2674
- 资源: 7640
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 基于matlab实现用有限元法计算电磁场的Matlab工具 .rar
- 基于matlab实现有限元算法 计算电磁场问题 边界条件包括第一类边界和第二类边界.rar
- 基于matlab实现用于计算不同车重下的电动汽车动力性和经济性.rar
- 基于matlab实现遗传算法求解多车场车辆路径问题 有多组算例可以用.rar
- 浏览器.apk
- 基于matlab实现是一个matlab中的power system 中搭建的一个模型
- 基于JSP毕业设计-教学管理系统(源代码+论文).zip
- 基于JSP毕业设计-家政管理系统-毕业设计.zip
- 基于Python实现淘宝商品评论采集(含逆向)源代码
- 基于matlab实现多目标进化算法NSGAⅡ&Matlab讲解.rar
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功