本文将展示如何通过图像处理和深度学习来自动解算数独谜题:
图中的红色数字均由算法生成。接下来我们将介绍如何创建该算法,并说明为何深度学习和图像处理对于对象检测和图像分类同样十分有用。
图像处理与深度学习
我们重点介绍两种技术:
图像处理
按像素级别变换或者修改图像。比如,过滤、模糊、去模糊和边缘检测等;
深度学习
通过学习样本图像自动识别图像特点。近几年,深度学习已经彻底改变了图像处理领域。
我们来探讨下这两种技术之间的关联性。这里有两种常见的观点:
“深度学习已经淘汰了‘传统’的图像处理方式。”
“深度学习需要数以百万的学习样本,而且只能用于猫咪图片识别这类任务。”
但事实是:
深度学习和图像处理都是非常有效的工具,可以解决各种难题,这些任务通常非常复杂,只有使用正确的工具才能解决问题。
数独解谜
解算数独(如下图所示)的规则是:需确保每一行、每一列,以及所有 3x3 宫格都只包含 1 到 9 这九个数字,并且不能有任何重复,只有这样才算完成。
数独在开始时会提供一些数字。填入数字的大小和数量将决定解谜的难度。
我们希望算法能够识别出宫格,并填入答案。但如果只是这样,未免太简单了点。我们还希望无论数独位于图片中的哪个位置,算法都能给出答案。这里有张照片,形象地展示了算法在解谜时可能需识别何种图像:
为此,我们需要设计相应的步骤来处理任务。这就意味着我们可以把解谜分成若干步骤:
找到数独——在图像中定位数独
找到宫格——在 9x9 的盘面中确定所有宫格
识别数字——必须能够识别手写或打印数字
解算数独
以上所有步骤均可用深度学习或图像处理中的一种方法来实现。那么,哪些步骤应该用深度学习实现,哪些步骤应该用图像处理来实现呢?
步骤1. 找到数独
我们无法预计图像、图像背景和对象大小的情况。不同图像的拍摄角度也可能大不相同。更不用说光照、相机拍摄条件等其它因素。可变因素实在是太多了。
适用方法:深度学习
让我们试试能否用语义分割为数独图片中的像素分类。为此,我们需要标记训练数据。在 MATLAB 中使用 Image Labeler 标记需要的数据。这是标记完成后的最终输入数据:
https://www.mathworks.cn/help/vision/examples/semantic-segmentation-using-deep-learning.html
有一点值得注意,那就是数据集非常小——只有一百幅左右的图像。让我们试着训练语义分割网络,看看数据是否充分。
设置图像数据仓库,以便储存用于语义分割网络的像素信息。
然后我们要设置网络层。这里要注意,我们创建了一个能够借助分类权重使各个分类抵消的函数。
设置网络:
这是训练选项:
最后训练网络:
net = trainNetwork(train, layers, opts);
在这个阶段中,大约需要 20 分钟才能跑完 40 次样本训练。具体耗时可能因电脑硬件/GPU 性能不同而有所差异。网络经过训练后,我们又换了一幅测试图像,得出下述的结果:
结果很不错!尽管图片中的其它格状图形对算法产生了干扰,但影响十分有限。可在下个步骤去除这些小范围噪点。
步骤2. 找到宫格
现在,我们需要在数独盘面中识别出所有小宫格。这些宫格有着很明确的界定:笔直的边线、总是深色的墨迹,以及大小一致的方形网格。在此提醒,我们在步骤 1 中已经确定了数独盘面的大致区域。我们可以将该区域以外的图像全部涂黑,确保算法集中处理该区域。
适用方法:图像处理
我们曾多次探讨图像处理,如果你不是图像处理领域的专家,你只要记住——这并不会妨碍你!MATLAB提供了各种应用,能让处理过程十分轻松。试试 Image Segmenter (https://www.mathworks.cn/help/images/ref/imagesegmenter-app.html),尝试用它来检测图像中的宫格。下面这段代码由该应用自动生成,可用于检测图像中的所有宫格。
首先需清理图像,确保消除所有噪点。
BW_out = bwpropfilt(networkMask, 'Area', [100000 + eps(100000), Inf]);
然后要缩放遮罩,确保它覆盖住整个盘面。
maskDilated = imdilate(BW_out, strel('disk', 120));
由于只需注意盘面所在区域,所以将其它区域全部涂黑。
grayIm = rgb2gray(im); grayIm(~maskDilated) = 0;
然后在图像中精准抠取盘面。
可以看到执行的结果非常准确,而且能够经受住各种干扰!
步骤3. 识别数字
有很多种方法可以识别手写数字和打印数字。这个问题的难点在于,我们必须考虑到各种字号和字体。好在办法也不少:
光学字符识别(OCR)是一种常见方法
结合了机器学习分类器的方向梯度直方图(HOG)是另一种方法点击此处查看MATLAB示例
好在手写识别同样是一个被广泛研究的机器学习分类问题(请查看本示例,了解如何使用常见的MINST数据集来解决该问题;我写过一篇类似文章,请点击此处阅读)。
适用方法:深度学习
该环节旨在识别打印数字或手写数字,然后通过深度学习将其数字化(如下图所示)。
为此,我们需要海量训练数据来帮助算法理解字符之间的差异。考虑到训练数据的海量程度,我们不可能手写出所有训练样本,这太费时间了。
这时即可借助 MATLAB 生成合成数据。就手写数字而言,这一步很简单——只需从MNIST数据集中提取现成数据,然后与下图中的背景图像合成。在合成各类打印数字时,我们希望数字看上去尽可能不同,以便确保它们无论采用何种字体(新罗马、维丹娜等),都能被算法识别。
在合成以上两类数字时,我们会尽可能确保数字的大小和位置每次都不尽相同。因为这样我们就能尽可能多地生成数据!
合成图像:手写类型/打印类型
注:宫格的方框厚度同样会随机变厚或变薄,从而确保宫格各不相同。限于篇幅限制,本文对于合成数据的介绍十分有限。今后我会推出更多有关该主题的文章,请持续关注!
现在我们可以训练网络了。设置训练选项,创建层,然后像之前那样训练网络。
结果显示,该网络的准确度约为97.8%。就数独解算而言,这个结果已经足够精确了。
步骤4. 解算数独
我们已经识别了宫格和数字。现在轮到填写答案了。
适用方法:都不需要!这是一个优化问题
整合各个步骤
现在我们已经完成了所有四个步骤,借助深度学习和图像处理创建了一个能够寻找最优解的数独解算器。
当您在处理和图像或视频有关的任务时,请务必牢记以下两点重要提示:
深度学习适合解决某些问题,但并非所有问题都适合用深度学习解决。
图像处理和深度学习都是十分有用的工具,可以将它们组合使用以便寻求最优方案。
全部0条评论
快来发表一下你的评论吧 !