Python答题卡识别并给出分数的实现代码


Posted in Python onJune 22, 2021

  哈喽大家好,这里是滑稽研究所。看过我们图像处理系列的朋友,应该知道识别答题卡那期文章。其中利用opencv框架,完美的实现了答题卡填涂区域的识别。在后台有小伙伴想要我完善一下判断选项对错并打分的功能,本期我们就来实现一下。
  那么我们来复习一下往期的代码原理。我们需要对图片素材进行灰度化处理、透视变换、轮廓检测、腐蚀膨胀处理、区域分割、边框计算、区域计算。实际上我们是通过像素面积的过滤、填涂区域优化和获取选项坐标来完成答题卡的识别的。
素材:

Python答题卡识别并给出分数的实现代码

  那么在获取到答题卡的填涂区域之后就好办了。我们首先分隔答题卡,去除干扰项,然后把不同的区域打上标签。我们的答题卡是自上而下排序的。那么我们获取到的填涂项的x坐标即横坐标就派上了用场。选项A~E一定是占据了五个不同的区域。我们已经为不同区域打上了标签。剩下的就是交给我们的if判断语句了。这时我们已经为填涂项赋上了实际的意义。即从像素坐标转换成了具有实际意义的选项。
  那y坐标就没有用了吗?非也。经过上面的处理我们只是得到了填涂区域对应的选项。但是我们还没有进行排序。大家知道无序的选项是没有意义的。而刚刚我们说了该答题卡的题号顺序是自上而下的。因为我们遍历选项时,是同时得到x、y坐标的,因此我们可以保证得到的坐标是配对的。
  其中横纵坐标分别填入两个list中,然后使用zip方法合并list。这时我们再按照每个list的第二个元素也就是纵坐标进行由小到大的排序,就可以得到正确的顺序。
  这时我们才真正获取到了需要的数据。即考生填涂的选项顺序,我们再新建一个list放正确的答案,与考生的答案进行对比,经计算得出考生的正确率,并给出分数。
  好,思路清晰,上代码!

import cv2
import numpy as np

path = './test_01.png'
img = cv2.imread(path)

imgGray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray,(3,3),1)
imgCanny = cv2.Canny(imgBlur,100,120)

cv2.imshow("O", imgCanny)

imgContour = img.copy()

cnts = cv2.findContours(imgCanny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
for cnt in cnts:
    area = cv2.contourArea(cnt)
    # 这个输出各个轮廓的面积
    #print(area)
#
if area >= 500:
    cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)
    peri = cv2.arcLength(cnt, True)
    # 找出轮廓的突变值
    approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
    # approx找到的是一个轮廓有几个突变值,有几个角就会有几个突变值
    # 返回的是一个list,输出他的长度,就可以知道到底有几个角
    #print(approx)
    a1,a2,a3,a4 = list(approx[0][0]),list(approx[1][0]),list(approx[2][0]),list(approx[3][0])

#cv2.imshow("Canny Image",imgContour)

mat1 = np.array([a1,a2,a3,a4],dtype=np.float32)

#透视变换
#计算矩形宽高
width = 402#int(((a4[0]-a1[0])+(a3[0]-a2[0]))/2)
height = 518#int(((a2[1]-a1[1])+(a3[1]-a4[1]))/2)

#计算还原后的坐标
new_a1 = [0,0]
new_a2 = [0,height]
new_a3 = [width,height]
new_a4 = [width,0]

mat2 = np.array([new_a1,new_a2,new_a3,new_a4],dtype=np.float32)
#计算变换矩阵
mat3 = cv2.getPerspectiveTransform(mat1,mat2)

#进行透视变换
res = cv2.warpPerspective(imgCanny,mat3,(width,height))
res1 = cv2.warpPerspective(img,mat3,(width,height))

imgxx = cv2.cvtColor(res1,cv2.COLOR_BGR2GRAY)
binary = cv2.threshold(imgxx,0,255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU )[1]
#变换完成
#cv2.imshow("Output",res1)

cntss = cv2.findContours(res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
for cnt1 in cntss:
    area1 = cv2.contourArea(cnt1)
    # 这个输出各个轮廓的面积
    #print(area)
#
    if area1 >= 1500 and area1<=1700:
        #把圆的轮廓画成黑色
        cv2.drawContours(binary, cnt1, -1, (0, 0, 0), 10)

        kernel = np.ones((5, 5), np.uint8)
        imgDialation = cv2.dilate(binary, kernel, iterations=1)

cv2.imshow("Out", imgDialation)

cntsss = cv2.findContours(imgDialation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]

l1 = []
l2 = []
l3 = ['B','E','A','D','B']

for cnt2 in cntsss:
    area2 = cv2.contourArea(cnt2)
            #print(area)

    if area2 <= 1200 and 800<=area2:
                #cv2.drawContours(res1, cnt, -1, (0, 255, 0), 5)
                #轮廓长
        peri = cv2.arcLength(cnt2, True)
                # 找出轮廓的突变值
        approx1 = cv2.approxPolyDP(cnt2, 0.02 * peri, True)

        x, y, w, h = cv2.boundingRect(approx1)
                #外接矩形
        #print(x+w//2,y+h//2)

        m = x+w//2
        n = y+h//2
        l1.append(m)
        l2.append(n)
        #拼接两个一维列表,使x,y坐标配对。
        mix1 = list(zip(l1,l2))
        #按列表第二个元素升序,即按y值由小到大排列。
        #这是我们得到的答案为正确顺序。
        mix1.sort(key=lambda x: x[1])

        if 400>x>80 and 50<y<350:
            cv2.rectangle(res1, (x, y), (x + w, y + h), (0, 0, 255), 2)
            #圆心
            # (图像,x.y位置,半径,颜色,轮廓粗细)
            cv2.circle(res1, (x+w//2,y+h//2), 1, (255, 0, 0), 5)

l4 = []
for i in mix1:
    if 75 < i[0] < 130:
        print("A")
        l4.append('A')
    elif 130 < i[0] < 185:
        print("B")
        l4.append('B')
    elif 185 < i[0] < 240:
        print("C")
        l4.append('C')
    elif 240 < i[0] < 295:
        print("D")
        l4.append('D')
    elif 295 < i[0] < 350:
        print("E")
        l4.append('E')

print('正确答案:',l3)
print('考生答案',l4)


h = 0
for i in range(0, len(l3)):
    if l3[i] == l4[i]:
        h=h+1
print('得分:',str(h/5*100)+'分')

cv2.imshow("cc Image",res1)

cv2.imshow("dd Image",binary)

cv2.waitKey(0)

运行结果:

Python答题卡识别并给出分数的实现代码
Python答题卡识别并给出分数的实现代码

  以上为两个图片素材的运行结果,我们只放出其中一部分。剩余的素材大家自行实验。
  可以看到,程序成功的识别了考生填涂的答题卡,并给出了考生答案、正答案和考生最后的得分。
  综上功能实现,任务完成。大家学会了吗?

以上就是Python识别答题卡并给出分数的详细内容,更多关于Python识别答题卡的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
简单介绍Python中的RSS处理
Apr 13 Python
Windows中安装使用Virtualenv来创建独立Python环境
May 31 Python
总结python实现父类调用两种方法的不同
Jan 15 Python
Python排序搜索基本算法之归并排序实例分析
Dec 08 Python
python如何在列表、字典中筛选数据
Mar 19 Python
Django-Rest-Framework 权限管理源码浅析(小结)
Nov 12 Python
Python判断是否json是否包含一个key的方法
Dec 31 Python
对python中的控制条件、循环和跳出详解
Jun 24 Python
python用requests实现http请求代码实例
Oct 31 Python
Python列表list操作相关知识小结
Jan 29 Python
Django实现将views.py中的数据传递到前端html页面,并展示
Mar 16 Python
Python Selenium安装及环境配置的实现
Mar 17 Python
Python 中的单分派泛函数你真的了解吗
Jun 22 #Python
Python实现DBSCAN聚类算法并样例测试
python中sqllite插入numpy数组到数据库的实现方法
Jun 21 #Python
利用Python第三方库实现预测NBA比赛结果
Django实现drf搜索过滤和排序过滤
python生成可执行exe控制Microsip自动填写号码并拨打功能
详解Python自动化之文件自动化处理
Jun 21 #Python
You might like
兼容性比较好的PHP生成缩略图的代码
2011/01/12 PHP
php限制上传文件类型并保存上传文件的方法
2015/03/13 PHP
Laravel 5框架学习之用户认证
2015/04/09 PHP
PHP仿微信多图片预览上传实例代码
2016/09/13 PHP
PHP创建自己的Composer包方法
2018/04/09 PHP
根据分辩率调用不同的CSS.
2007/01/08 Javascript
从零开始学习jQuery (六) jquery中的AJAX使用
2011/02/23 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
javascript四舍五入函数代码分享(保留后几位)
2013/12/10 Javascript
jquery制作漂亮的弹出层提示消息特效
2014/12/23 Javascript
Bootstrap实现登录校验表单(带验证码)
2016/06/23 Javascript
AngularJS 入门教程之HTML DOM实例详解
2016/07/28 Javascript
JS获取当前页面名称的简单实例
2016/08/19 Javascript
详解Node.js:events事件模块
2016/11/24 Javascript
jQuery中过滤器的基本用法示例
2017/10/11 jQuery
深入理解JS的事件绑定、事件流模型
2018/05/13 Javascript
vue 集成 vis-network 实现网络拓扑图的方法
2019/08/07 Javascript
在vue项目实现一个ctrl+f的搜索功能
2020/02/28 Javascript
[52:52]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第三局
2016/02/27 DOTA
Python 多线程实例详解
2017/03/25 Python
flask + pymysql操作Mysql数据库的实例
2017/11/13 Python
Python random模块用法解析及简单示例
2017/12/18 Python
wxPython实现窗口用图片做背景
2018/04/25 Python
Python 实现字符串中指定位置插入一个字符
2018/05/02 Python
python实现周期方波信号频谱图
2018/07/21 Python
Python零基础入门学习之输入与输出
2019/04/03 Python
python使用paramiko模块通过ssh2协议对交换机进行配置的方法
2019/07/25 Python
Python 中使用 PyMySQL模块操作数据库的方法
2019/11/10 Python
Pandas DataFrame求差集的示例代码
2020/12/13 Python
全方位了解CSS3的Regions扩展
2015/08/07 HTML / CSS
FILA德国官方网站:来自意大利的体育和街头服饰品牌
2019/07/19 全球购物
年终晚会主持词
2014/03/25 职场文书
地震捐款倡议书
2014/08/29 职场文书
社保代办委托书怎么写
2014/10/06 职场文书
团员自我评价范文
2015/03/10 职场文书
摩登时代观后感
2015/06/03 职场文书