前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。

作者: 机器学习与统计学

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

http://note.youdao.com/noteshare?id=3054cce4add8a909e784ad934f956cef

随着现代图像处理和人工智能技术的快速发展,不少学者尝试讲CV应用到教学领域,能够代替老师去阅卷,将老师从繁杂劳累的阅卷中解放出来,从而进一步有效的推动教学质量上一个台阶。

传统的人工阅卷,工作繁琐,效率低下,进度难以控制且容易出现试卷遗漏未改、登分失误等现象。

现代的“机器阅卷”,工作便捷、效率高、易操作,只需要一个相机(手机),拍照即可获取成绩,可以导入Excel表格便于存档管理。

下面我们从代码实现的角度来解释一下我们这个简易答题卡识别系统的工作原理。第一步,导入工具包及一系列的预处理

 import numpy as np
import argparse
import imutils
import cv2
# 设置参数
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default="test_01.png")
args = vars(ap.parse_args())
# 正确答案
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1} #
def order_points(pts):
# 一共4个坐标点
rect = np.zeros((4, 2), dtype = "float32")

# 按顺序找到对应坐标0,1,2,3分别是 左上,右上,右下,左下
# 计算左上,右下
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# 计算右上和左下
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect

def four_point_transform(image, pts):
# 获取输入坐标点
rect = order_points(pts)
(tl, tr, br, bl) = rect
# 计算输入的w和h值
widthA = np.sqrt(((br[0]-bl[0])** 2) + ((br[1]-bl[1])**2))
widthB = np.sqrt(((tr[0] -tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0]-br[0])**2)+((tr[1]-br[1])**2))
heightB = np.sqrt(((tl[0]-bl[0])**2)+((tl[1]-bl[1])**2))
maxHeight = max(int(heightA), int(heightB))
# 变换后对应坐标位置
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype = "float32")
# 计算变换矩阵
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped # 返回变换后结果

def sort_contours(cnts, method="left-to-right"):
reverse = False
i = 0
if method == "right-to-left" or method == "bottom-to-top":
reverse = True
if method == "top-to-bottom" or method == "bottom-to-top":
i = 1
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
def cv_show(name,img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()


image = cv2.imread(args["image"])
contours_img = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(blurred, 75, 200)
# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
docCnt = None

# 确保检测到了
if len(cnts) > 0:
# 根据轮廓大小进行排序
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts: # 遍历每一个轮廓
# 近似
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# 准备做透视变换
if len(approx) == 4:
docCnt = approx
break
# 执行透视变换
warped = four_point_transform(gray, docCnt.reshape(4, 2))

thresh = cv2.threshold(warped, 0, 255,
cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
thresh_Contours = thresh.copy()
# 找到每一个圆圈轮廓
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
cv2.drawContours(thresh_Contours,cnts,-1,(0,0,255),3)
questionCnts = []
for c in cnts:# 遍历
# 计算比例和大小
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 根据实际情况指定标准
if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1:
questionCnts.append(c)
# 按照从上到下进行排序
questionCnts = sort_contours(questionCnts,
method="top-to-bottom")[0]
correct = 0
# 每排有5个选项
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
cnts = sort_contours(questionCnts[i:i + 5])[0]
bubbled = None
for (j, c) in enumerate(cnts): # 遍历每一个结果
# 使用mask来判断结果
mask = np.zeros(thresh.shape, dtype="uint8")
cv2.drawContours(mask, [c], -1, 255, -1) #-1表示填充
# 通过计算非零点数量来算是否选择这个答案
mask = cv2.bitwise_and(thresh, thresh, mask=mask)
total = cv2.countNonZero(mask)
# 通过阈值判断
if bubbled is None or total > bubbled[0]:
bubbled = (total, j)
# 第二步,与正确答案进行对比
color = (0, 0, 255)
k = ANSWER_KEY[q]
# 判断正确
if k == bubbled[1]:
color = (0, 255, 0)
correct += 1
cv2.drawContours(warped, [cnts[k]], -1, color, 3) #绘图

#正确率的文本显示
score = (correct / 5.0) * 100
print("[INFO] score: {:.2f}%".format(score))
cv2.putText(warped, "{:.2f}%".format(score), (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
cv2.imshow("Input", image)
cv2.imshow("Output", warped)
cv2.waitKey(0)

最终实现的效果如下:

.

利用Python开发智能阅卷系统的更多相关文章

  1. 利用python开发app实战

    你说,我们的未来 被装进棺材,染不上尘埃 *** 我很早之前就想开发一款app玩玩,无奈对java不够熟悉,之前也没有开发app的经验,因此一直耽搁了.最近想到尝试用python开发一款app,goo ...

  2. 吴裕雄 python 人工智能——智能医疗系统后台用户复诊模块简约版代码展示

    #复诊 import sys import os import time import operator import cx_Oracle import numpy as np import pand ...

  3. 吴裕雄 python 人工智能——智能医疗系统后台用户注册、登录和初诊简约版代码展示

    #用户注册.登录模块 #数据库脚本 CREATE TABLE usertable( userid number(8) primary key not null , username varchar(5 ...

  4. 吴裕雄 PYTHON 人工智能——智能医疗系统后台智能分诊模块及系统健康养生公告简约版代码展示

    #coding:utf-8 import sys import cx_Oracle import numpy as np import pandas as pd import tensorflow a ...

  5. 利用python开发的flappy bird 游戏

    python 中 pygame模块能让我们很方便的编写游戏,16年我用python 仿制了flappy bird 游戏,下面是游戏的完整代码以及素材,分享给大家. 第一个python文件,flappy ...

  6. 利用Sails.js+MongoDB开发博客系统

    http://yoyoyohamapi.me/categories/利用Sails-js-MongoDB开发博客系统/ 利用Sails.js+MongoDB开发博客系统 Apr 14, 2016 利用 ...

  7. Home Assistant + 树莓派:强大的智能家居系统 · 安装篇

    Home Assistant + 树莓派:强大的智能家居系统 · 安装篇 转载:Home Assistant + 树莓派:强大的智能家居系统 · 安装篇 目录 1. 初始安装 3. Homebridg ...

  8. 利用python 提取log 文件里的关键句子,并进行统计分析

    利用python开发了一个提取sim.log 中的各个关键步骤中的时间并进行统计的程序: #!/usr/bin/python2.6 import re,datetime file_name='/hom ...

  9. 一次学生时代的经历,利用Python在机房杀红蜘蛛,脱离老师控制!

    这个为什么说是一次学生时代的经历呢,我的出发点并没有是为了吊胃口.确实,这个Python小应用,只能在学生时代用得着吧,尤其是高中和大学,如果你没有想到也没关系,看完我下面说的就会明白了.   在这里 ...

随机推荐

  1. .Net Core 发布到 Linux 上时遇到的问题记录

    目前遇到的问题: 1.路径区分大小写及路径用“/”,而不是常用的"\\". windows下路径为:"xxxx\\yyyy",Linux路径下为:"x ...

  2. Javascript继承的问题

    说到Javascript的继承,相信只要是前端开发者都有所了解或应用,因为这是太基础的知识了.但不知各位有没有深入去理解其中的玄机与奥秘.今本人不才,但也想用自己的理解来说一说这其中的玄机和奥秘. 一 ...

  3. JS---涉及到的common.js

    //格式化日期的代码 //获取指定标签对象 //获取元素的文本内容 //获取元素的文本内容 //获取父级元素中的第一个子元素 //获取父级元素中的最后一个子元素 //获取某个元素的前一个兄弟元素 // ...

  4. ReactNative: 使用AppReistry注册类

    一.简介 每一个应用程序的运行都有一个入口文件或者入口函数,例如iOS中的使用UIApplicationMain类完成入口函数的实现,在React-Native中,AppRegistry类就肩负着这个 ...

  5. fastjson又被发现漏洞,这次危害可能会导致服务瘫痪

    0x00 漏洞背景 2019年9月5日,fastjson在commit 995845170527221ca0293cf290e33a7d6cb52bf7上提交了旨在修复当字符串中包含\\x转义字符时可 ...

  6. Spring Boot 静态资源能加载css 不能加载js

    Spring Boot 配置拦截器的时候默认 是放行 静态资源 , 也就是说不需要进行配置 registry.addResourceHandler("/**") .addResou ...

  7. Skulpt在线模拟运行Python工具

    1. Skulpt是一个完全依靠浏览器端模拟实现Python运行的工具 2. 不需要预处理.插件或服务器端支持,只需编写python并重新载入即可. 3. 由于代码完全是在浏览器中运行的,所以不必担心 ...

  8. netcore 2.2 使用 AutoMapper 实现实体之间映射

    一.什么是AutoMapper? AutoMapper是一个简单的对象映射框架(OOM),将一个对象映射到另一个对象. 二.AutoMapper的好处 以前的时候我们将DTO对象转换为Model对象时 ...

  9. Change Style of Navigation Items 更改导航项的样式

    In this lesson, you will learn how to change the style of navigation items in a WinForms XAF applica ...

  10. Add a Preview to a List View将预览添加到列表视图

    In this lesson, you will learn how to show a preview section in a List View's grid. For this purpose ...