基于Android和OpenCV的答题卡识别软件

基于Android和OpenCV的答题卡识别软件

1. 软件介绍

设计目标是可以添加不同的考试,在不同考试下可以设置模板,包括题目数量、答题卡样式、每题分值以及每题答案;扫描结果按列表显示,并讲识别出的图片展示处理,点击列表项也可以弹出识别结果。

扫描答题卡可以从相机或者相册中选择。
基于Android和OpenCV的答题卡识别软件_第1张图片
基于Android和OpenCV的答题卡识别软件_第2张图片
基于Android和OpenCV的答题卡识别软件_第3张图片

2.识别原理介绍

以下主要介绍两个核心部分:获取答题区域和识别用户填涂选项。

2.1获取答题区域及矫正

2.1.1总体流程介绍

获取答题区域,首先要对原始的彩色图像进行预处理操作,包括转灰度图像、高斯模糊、边缘检测等,然后从得到的图像寻找轮廓,并对轮廓进行一定的筛选,接着求出答题卡矩形顶点的坐标,利用坐标进行透视变换从而得到答题区域。总体流程图如图所示。
基于Android和OpenCV的答题卡识别软件_第4张图片

2.1.2 图像预处理

在获取答题区域之前,需要对图片进行预处理。首先将用户输入的原始图像转化为灰度图像,以减少后续对图像处理的运算复杂度。然后使用高斯滤波(3*3)对图像去噪,再利用Sobel算子分别求出图像在X和Y方向上的梯度,将他们进行叠加以获取边缘。

在进行叠加时,对两个图像的叠加权重适当加大,否则可能导致边缘不够清晰使甚至断开寻找轮廓失败。权重设置为1,可以理解为X、Y方向的图像进行叠加,可以看到矩形的右边缘不太清晰,在图片质量差的情况下可能会发生断裂不完整,使后续查找矩形轮廓时失败。 权重设置为5,可以理解为将X、Y方向的图像进行5次叠加,原本不太清晰的边缘经过多次叠加后变得清晰。
基于Android和OpenCV的答题卡识别软件_第5张图片
权重设置为1

基于Android和OpenCV的答题卡识别软件_第6张图片
权重设置为5

在完成边缘检测后,可以看到图像还是存在很多细小的噪点,为了更好的提取出答题卡,对边缘检测后的图像进行二值化处理,结果如图所示,可以看到噪点很好的被去除了,图片显得更清晰了。
基于Android和OpenCV的答题卡识别软件_第7张图片

二值化的图像

在二值化之后,还对该图像进行了一次腐蚀操作(3*3),原因是后续获取矩形坐标时是通过多边形拟合的方式,在实际测试时发现若边缘存在一些噪点,拟合的时候可能会导致拟合不出四边形导致获取矩形顶点坐标失败。而通过一次腐蚀操作可以避免这种情况。因此在图像预处理的最后增加了一次腐蚀操作。最终获得的图像如图所示。
基于Android和OpenCV的答题卡识别软件_第8张图片
预处理后的图像

图像预处理的整体流程图如图所示。
在这里插入图片描述
图 3 6 图像预处理流程图

2.1.3 寻找轮廓及计算坐标

在图像预处理之后,我们可以得到一张拥有比较清晰的答题卡轮廓的图像,但图像中还有一些与答题区域无关的部分,例如纸张外的部分、纸张的其他部分,我们只需要答题卡矩形方框那一部分。这一节将找到答题卡矩形的四个顶点的坐标。

首先我们需要找到图像中的所有轮廓,正如前文所说,图像中具有许多无关的部分,我们需要设定一个条件来筛选我们需要的轮廓。针对答题卡在图像中的特点,本文设定的筛选条件时轮廓的长>原图片长的1/2,轮廓的宽>原图片宽的1/4,当然这个条件在某些情况下可能会发生误判,例如题目数量较少时,矩形的宽可能不能大于原图片宽的1/4,因此在这种情况下输入图片时尽量利用软件的裁剪功能使答题区域占大部分;另一种改进方法是在设置模板时,根据题目数量和答题卡类型,动态设置筛选条件,本文没有实现这种做法,但理论上是可以做到的。
识别结果如图 所示,将筛选出的轮廓用蓝色线画出。
基于Android和OpenCV的答题卡识别软件_第9张图片
图 识别出的矩形边框

在得到了答题卡矩形轮廓后,需要计算出四个顶点的坐标,并确定四个顶点的相对位置。
计算坐标采用的是多边形拟合的方式,利用approxPolyDP方法将该轮廓拟合成一个多边形,判断拟合的结果顶点是否为4,若为4则说明成功拟合了一个四边形,该四边形的四个顶点的坐标可以认为是答题卡矩形轮廓的坐标。

得到四个顶点坐标后,计算出四个顶点坐标的中心点. 通过比较每个点与中心点的x和y值来判断该顶点所在位置,例如A点x坐标小于O点x坐标,y坐标大于O点y坐标,则说明A点是左上顶点,其他点的确定同理。因此我们就获得了答题卡矩形轮廓四个顶点的坐标。

2.1.4 透视变换矫正图像

在获取答题卡的图片时,拍照角度很难保证刚好垂直纸张,导致会发生一定的倾斜,因此获得的答题卡区域有可能是倾斜甚至发生一定的形变的,我们需要对这种图片进行透视变换校正。

透视变换本质是将图像从一个平面投影到另一个平面,在这里就是将答题卡图像所在倾斜的平面投影到一个水平的平面中。我们知道了矩形四个顶点在倾斜平面的坐标,还需要四个在水平平面的坐标,将矩形的四个顶点投影到水平平面中,就可以得到校正后的图像。水平平面的坐标选取采用了轮廓的外接矩形的四个顶点坐标。
整个做法流程运行结果对比如图所示,可以看到成功提取出了答题区域和对该区域进行了校正,效果还不错。
基于Android和OpenCV的答题卡识别软件_第10张图片
图 原始图像
基于Android和OpenCV的答题卡识别软件_第11张图片
图 获取答题区域校正后图像

2.2 识别填涂答案

2.2.1 整体流程介绍

识别答题卡,判断一个选项是否被选中,有两个很重要的步骤,一是获得每道题每个选项的区域,例如某个范围内认定是第一题的A选项,另一个范围是第一题的B选项等;二是获得答题卡上被填涂的区域,就是用户实际涂写的区域;通过判断每个选项区域内用户涂写的部分占该区域的占比来判断该选项是否被选中。整体流程图如图所示,其中去噪和二值化与第三章类似,就不再赘述。
基于Android和OpenCV的答题卡识别软件_第12张图片
图 识别填涂答案整体流程

2.2.2 获取单个选项区域

因为本次答题卡设计每个选项采用的不是封闭的矩形,是“[ A ]”这样的结构,如果直接寻找轮廓会找到三个轮廓难以区分,因此可以先采取使用图像腐蚀和膨胀操作对其处理,具体实现有两种思路:

(1) 对图像进行膨胀操作,使每个选项的“[ ”、“A”、“]”三个部分变成一个部分,然后进行腐蚀操作,避免不同选项间连在一起。然后寻找所有的轮廓,理论上每一题应该能找到五个轮廓,题号加四个选项,对它们根据坐标进行排序从而就能够获取每个选项的区域。但是这种方式操作难度很大,在进行膨胀操作时不同选项间很容易存在粘连,再通过腐蚀去除粘连时,题号的区域很可能会变得很小甚至被消去,导致寻找轮廓时数量对不上,也难以过滤掉答题卡上的一些干扰区域。
如图所示,若腐蚀参数选的小,则很难分离出四个选项区域;若腐蚀参数选择过大,可能会导致序号区域被消去,或者选项三个部分没有达到我们的目的被膨胀成一块区域,同时对于答题卡上的一些干扰信息无法去除。
基于Android和OpenCV的答题卡识别软件_第13张图片
图 方法一
(2) 考虑到第一种方式很难去除选项中的粘连,那就考虑直接将选项膨胀参数调大,使四个选项变成同一个区域,再将该区域平均划分四部分,每部分对应一个选项。 本文中采取做法是先进行四次膨胀(5*1),然后进行一次腐蚀操作(1*5),最终效果如图 4 3所示,可以看到每题选项区域都被融合在一起,也能很好的区分题号和选项区域,以及过滤掉一些小的干扰。
基于Android和OpenCV的答题卡识别软件_第14张图片
图 方法二

获取到每道题选项区域后,还需要确定每个区域对应的题号,本文采取的策略是先对轮廓进行过滤,排除掉题号的轮廓区域、一些小的干扰项,之后按照轮廓左上顶点进行排序,排序策略是根据y值进行排序,若y相等或相差在10以内,则根据x值进行排序。Y值的排序策略意思是因为每个轮廓不一定y都相等,可能因为拍照角度会存在一定的误差,这里将y相差在10以内的轮廓都认为是同一行,从而对拍照角度有一定的容错率。

4.3 获取填涂区域
获取填涂区域比较好操作,只需要通过腐蚀去除或者削弱一些没有填涂的选项和序号,使它们满足不了识别为选项的条件,即它们在选项区域的占比达不到要求即可,不需要完全消除这些部分,否则可能导致填涂的答案也被腐蚀的比较小。对此,经过多次实验测试,本文选择的方式是进行一次腐蚀(5*5),得到的效果如图所示。
可以看到填涂的区域比较明显。若采取多次腐蚀或增大腐蚀参数,可以消去其他未填涂的选项,但是填涂的部分面积也会变小,采用这种方式已经足够区分填涂和未填涂的部分了。
基于Android和OpenCV的答题卡识别软件_第15张图片
图 填涂区域

2.2.3 识别填涂答案

通过上述步骤我们可以获取每题选项的区域获取和填涂的区域,因此识别选项是否被标记,只需要**计算出每个选项区域内白色像素点的个数占该区域总像素的比例,提前设定好一个阈值,超过这个值就认为该选项被填涂,即可识别出填涂的选项。**为直观看出效果,将第二节识别的轮廓画在识别出的填涂区域中,如图 所示。将轮廓四等分再统计四个区域的像素值即可实现选项的识别。
基于Android和OpenCV的答题卡识别软件_第16张图片
图 识别填涂答案

三、测试结果

测试用例图片说明:按照图片处理顺序如下
- src: 原图
- sobel: 用sobel算子进行边缘检测后的结果
- threshold: 二值化
- dilate: 对二值化图像进行腐蚀,排除边界的噪音,防止拟合矩形失败
- sheetArea: 对识别出的轮廓进行透视变换和裁剪,得到的答题卡区域
- select: 获取填涂的选项
- ansArea: 腐蚀图片,获取每一题选项区域。对该区域划分四部分,每部分即对应的四个选项
- rect:将识别出的每题的选项区域标出,不是必要的,用于观察结果。
- result:将识别出的填涂的选项标记在图片中

部分测试结果如下: 以下测试均识别成功

  1. 正常拍摄
    在这里插入图片描述
  2. 图片部分倾斜
    在这里插入图片描述
  3. 纸面弯曲
    在这里插入图片描述
  4. 存在折痕
    在这里插入图片描述

四、局限

对于答题卡矩形的识别中,对轮廓的筛选采用了固定的方式,即长宽分别超过原图的一定比例,若题目数量少的答题卡可能会无法满足而导致答题卡的矩形被过滤,最终无法识别。后续可以考虑根据题目数量采用动态的过滤条件,避免这种误判。此外,因为对图像处理过程是固定的,可能不使用于所有情况,例如放大、缩小超出一定范围则会无法识别,后续可考虑使用动态的处理方式,根据答题卡具体情况选择对图像处理方式(例如对图像腐蚀的次数等)。

此外,改软件存在兼容性的问题,生成图片时因为采用的是固定像素,在不同手机上可能显示会出问题(例如题号溢出到方框外),精力有限没有去继续研究这部分了,但是识别应该是一样的。

欢迎批评指正交流,若留言没看见可以加微信:aaaalulala

你可能感兴趣的:(安卓,android,图像识别,opencv,android,studio,java)