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基础教程之基本内置数据类型介绍
Feb 20 Python
Python实现的监测服务器硬盘使用率脚本分享
Nov 07 Python
教你学会使用Python正则表达式
Sep 07 Python
解决python3中自定义wsgi函数,make_server函数报错的问题
Nov 21 Python
python判断一个集合是否为另一个集合的子集方法
May 04 Python
django_orm查询性能优化方法
Aug 20 Python
使用Python+wxpy 找出微信里把你删除的好友实例
Feb 21 Python
Python 获取windows桌面路径的5种方法小结
Jul 15 Python
基于python的列表list和集合set操作
Nov 24 Python
python3 assert 断言的使用详解 (区别于python2)
Nov 27 Python
pytorch masked_fill报错的解决
Feb 18 Python
PyCharm 2020.1版安装破解注册码永久激活(激活到2089年)
Sep 24 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
《破坏领主》销量已超100万 未来将继续开发新内容
2020/03/08 其他游戏
PHP COOKIE设置为浏览器进程
2009/06/21 PHP
改写ThinkPHP的U方法使其路由下分页正常
2014/07/02 PHP
PHP实现自动登入google play下载app report的方法
2014/09/23 PHP
PHP的pcntl多进程用法实例
2015/03/19 PHP
CI框架中redis缓存相关操作文件示例代码
2016/05/17 PHP
搭建自己的PHP MVC框架详解
2017/08/16 PHP
一个高效的JavaScript压缩工具下载集合
2007/03/06 Javascript
JQuery与Ajax常用代码实现对比
2009/10/03 Javascript
Seajs 简易文档 提供简单、极致的模块化开发体验
2016/04/13 Javascript
全面解析JavaScript中“&amp;&amp;”和“||”操作符(总结篇)
2016/07/18 Javascript
如何清除IE10+ input X 文本框的叉叉和密码输入框的眼睛图标
2016/12/21 Javascript
vue如何在自定义组件中使用v-model
2018/05/14 Javascript
JavaScript设计模式之工厂模式和抽象工厂模式定义与用法分析
2018/07/26 Javascript
Javascript作用域和作用域链原理解析
2020/03/03 Javascript
vue router返回到指定的路由的场景分析
2020/11/10 Javascript
详解Vue3.0 + TypeScript + Vite初体验
2021/02/22 Vue.js
[01:24:16]2018DOTA2亚洲邀请赛 4.6 全明星赛
2018/04/10 DOTA
python批量下载图片的三种方法
2013/04/22 Python
解析Python中的变量、引用、拷贝和作用域的问题
2015/04/07 Python
Python中用format函数格式化字符串的用法
2015/04/08 Python
Python反射用法实例简析
2017/12/22 Python
python selenium 对浏览器标签页进行关闭和切换的方法
2018/05/21 Python
python 执行终端/控制台命令的例子
2019/07/12 Python
python学习之使用Matplotlib画实时的动态折线图的示例代码
2021/02/25 Python
HTML5 背景的显示区域实现
2020/07/09 HTML / CSS
在加拿大在线租赁和购买电子游戏:Game Access
2019/09/02 全球购物
情人节活动策划方案
2014/02/27 职场文书
买房协议书
2014/04/11 职场文书
我们的节日国庆活动方案
2014/08/19 职场文书
2014年新农村建设工作总结
2014/12/01 职场文书
2014年建筑工程工作总结
2014/12/03 职场文书
求职自荐信范文(优秀篇)
2015/03/27 职场文书
go语言-在mac下brew升级golang
2021/04/25 Golang
聊聊pytorch测试的时候为何要加上model.eval()
2021/05/23 Python
MySQL transaction事务安全示例讲解
2022/06/21 MySQL