OpenCV实例(三)答题卡识别

作者:Xiou

1.答题卡识别概述

随着信息化的发展,计算机阅卷已经成为一种常规操作。在大型考试中,客观题基本不再需要人工阅卷。

答题卡识别的基本实现原理如图所示,其主要包含以下步骤。
(1)进行反二值化阈值处理,将后续操作中要使用的选项处理为前景(白色),将答题卡上其他不需要进行后续处理的位置处理为背景(黑色)。
(2)将每个选项提取出来,并计算各选项的白色像素点个数。
(3)筛选出白色像素点个数最大的选项,将该选项作为考生作答选项。
(4)将考试作答选项与标准答案进行比较,给出评阅结果。

在这里插入图片描述

2.单道题目的识别

为了方便理解,先讨论单道题目的情况。

2.1基本流程及原理

在这里插入图片描述
下面对具体步骤进行详细介绍。
Step 1:导入库将需要使用的库导入。
Step 2:答案及选项初始化为了方便处理,将各个选项放入一个字典内保存,让不同的选项对应不同的索引。例如,“选项A”对应索引0,“选项B”对应索引1,以此类推。本题目的标准答案为“选项C”。
Step 3:读取原始图像将选项图像读取到系统内。
Step 4:图像预处理图像预处理主要包含色彩空间转换、高斯滤波、阈值变换三个步骤。
Step 5:获取轮廓及排序获取轮廓是图像处理的关键,借助轮廓能够确定每个选项的位置、选项是否被选中等。

Step 6:计算每个选项包含的白色像素点个数本步骤主要完成任务如下。
任务1:提取每一个选项。
任务2:计算每一个选项内的白色像素点个数。

Step 7:识别考生作答选项白色像素点个数最多的选项即考生作答选项。

8)Step 8:输出结果用不同颜色标注考生作答选项正确与否,并打印输出结果。

根据考生作答选项是否与标准答案一致设置要绘制的颜色,
具体如下:
● 考生作答选项与标准答案一致,将考生填涂选项的轮廓设置为绿色。
● 考生作答选项与标准答案不一致,将考生填涂选项的轮廓设置为红色。按照上述规则,在考生作答选项上绘制轮廓,并打印输出文字提示。

2.2代码实例:

# -*- coding: utf-8 -*-
# ==================导入库=======================
import numpy as np
import cv2
# ==================答案及选项初始化=======================
# 将选项放入字典内
ANSWER_KEY = {
    
    0: "A", 1: "B", 2: "C", 3: "D"}
# 标准答案
ANSWER = "C"
# ==================读取原始图像=======================
img = cv2.imread('xiaogang.jpg')
cv2.imshow("original",img)
# ==================图像预处理=======================
# 转换为灰度图像
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 高斯滤波
gaussian_bulr = cv2.GaussianBlur(gray, (5, 5), 0)  
# 阈值变换,将所有选项处理为前景(白色)
ret,thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
# cv2.imshow("thresh",thresh)
# cv2.imwrite("thresh.jpg",thresh)
# ==================获取轮廓及排序=======================
# 获取轮廓
cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 将轮廓按照从左到右排列,方便后续处理
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                    key=lambda b: b[1][0], reverse=False))
# ==================构建列表,用来存储每个选项非零值个数及序号===================
options=[]
# 自左向右,遍历每一个选项的轮廓
for (j, c) in enumerate(cnts):
    # 构造一个与原始图像大小一致的灰度图像,用来保存每一个选项用
    mask = np.zeros(gray.shape, dtype="uint8")
    # 获取单个选项
    # 这里通过循环,将每一个选项单独放入一个mask中
    cv2.drawContours(mask, [c], -1, 255, -1)
    # 获取thresh中mask指定部分,每次循环,mask对应ABCD的不同选项
    cv2.imshow("s1",mask)
    cv2.imwrite("s1.jpg",mask)
    cv2.imshow("thresh",thresh)
    cv2.imwrite("thresh.jpg",thresh)
    mask = cv2.bitwise_and(thresh, thresh, mask=mask)  
    cv2.imshow("s2",mask)
    cv2.imwrite("s2.jpg",mask)
    # cv2.imshow("mask"+str(j),mask)
    # cv2.imwrite("mask"+str(j)+".jpg",mask)
    # 计算每一个选项的非零值(白色像素点)
    # 涂为答案的选项,非零值较多;没有涂选的选项,非零值较少   
    total = cv2.countNonZero(mask)
    #将选项非零值个数、选项序号放入列表options内
    options.append((total,j))
    # print(options)  #在循环中打印存储的非零值(白色点个数)及序号
# =================识别考生的选项========================
# 将所有选项按照非零值个数降序排序
options=sorted(options,key=lambda x: x[0],reverse=True)
# 获取包含最多白色像素点的选项索引(序号)
choice_num=options[0][1]
# 根据索引确定选项值:ABCD
choice= ANSWER_KEY.get(choice_num)
print("该生的选项:",choice)
# =================根据选项正确与否,用不同颜色标注考生选项==============
# 设定标注的颜色类型,绿对红错
if choice == ANSWER:
    color = (0, 255, 0)   #回答正确,用绿色表示
    msg="回答正确"
else:
    color = (0, 0, 255)   #回答错误,用红色表示
    msg="回答错误"
#  在选项位置上标注颜色(绿对红错)
cv2.drawContours(img, cnts[choice_num], -1, color, 2)
cv2.imshow("result",img)
# 打印识别结果
print(msg)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行上述程序,将根据考生作答情况打印出对应提示信息,同时会在答题卡上提示,具体情况如下:
● 当考生作答选项与标准答案一致时,在填涂的答案处绘制绿色边框。
● 当考生作答选项与标准答案不一致时,在填涂的答案处绘制红色边框。

输出结果:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41600018/article/details/130034769