美文网首页每周一赞
单道题目的识别

单道题目的识别

作者: 大龙10 | 来源:发表于2023-10-17 11:29 被阅读0次

书名:计算机视觉40例从入门到深度学习:OpenCV-Python
作者:李立宗
出版社:电子工业出版社
出版时间:2022-07-01
ISBN:9787121436857


第9章 答题卡识别

9.1 单道题目的识别

一、基本流程及原理

  • 单道题目的答题卡识别基本原理与图9-1相同,将上述步骤分解,得到如图9-2所示的实现步骤。


    图9-2 实现步骤

    下面对具体步骤进行详细介绍。

1)Step 1:导入库

  • 将需要使用的库导入

2)Step 2:答案及选项初始化

  • 为了方便处理,将各个选项放入一个字典内保存,让不同的选项对应不同的索引。
    例如,“选项A”对应索引0,“选项B”对应索引1,以此类推。
  • 本题目的标准答案为“选项C”。
# 将选项放入字典内
ANSWER_KEY={0:"A",1:"B",2:"C",3:"D"}   
# 标准答案
ANSWER ="C"

3)Step 3:读取原始图像

  • 将选项图像读取到系统内

4)Step 4:图像预处理

  • 图像预处理主要包含色彩空间转换、高斯滤波、阈值变换三个步骤。
  • 阈值变换使用的是反二值化阈值处理,
    将图像内较暗的部分(如铅笔填涂的答案、选项标记等)处理为白色,将图像内相对较亮的部分(如白色等)处理为黑色。
    之所以这样处理是因为,通常用白色表示前景,前景是需要处理的对象;用黑色表示背景,背景是不需要额外处理的部分。

5)Step 5:获取轮廓及排序

  • 获取轮廓是图像处理的关键,借助轮廓能够确定每个选项的位置、选项是否被选中等。

  • 需要注意的是,使用findContours函数获取的轮廓的排列是没有规律的。
    因此需要将获取的各选项的轮廓按照从左到右出现的顺序排序。将轮廓从左到右排列后:
    ● 索引为0的轮廓是选项A的轮廓。
    ● 索引为1的轮廓是选项B的轮廓。
    ● 索引为2的轮廓是选项C的轮廓。
    ● 索引为3的轮廓是选项D的轮廓。

# 获取轮廓
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))

6)Step 6:计算每个选项包含的白色像素点个数

本步骤主要完成任务如下。

  • 任务1:提取每一个选项。

  • 任务2:计算每一个选项内的白色像素点个数。

  • 对于任务1,使用按位与运算的掩模方式完成,示意图如图9-3所示,根据“任意数值与自身进行按位与运算,结果仍旧是自身值”及掩模指定计算区域的特点:
    例如,针对图像i使用图像m作为掩模,进行按位与运算:
    得到的结果图像x是图像i中被掩模图像m中像素值非0区域指定的部分。
    具体来说:
    ● 在结果图像x中,与掩模图像m中像素值非0区域对应位置的像素值来源于图像i。
    ● 在结果图像x中,与掩模图像m中像素值为0区域对应位置的像素值为0。
    针对图像i,利用掩模图像m提取掩模对应的选项D,提取选项示例示意图如图9-4所示。


    图9-4 提取选项示例示意图

    具体来说,在图9-4右侧的结果图像中:
    ● 与掩模图像m中像素值非0区域(白色区域)对应位置的像素值来源于图像i。
    ● 与掩模图像m中像素值为0区域(黑色区域)对应位置的像素值为0(黑色)。

  • 如何构造每一个选项的掩模图像。
    在图9-4中的掩模图像m,来源于选项D的实心轮廓,可以通过函数drawContours得到。具体分成如下两步:
    ● 首先,构造一个与原始图像i等尺寸的灰度图像,图像内像素值均为0(纯黑色)。
    ● 其次,将图像i中选项D的轮廓以实心形式绘制出来。综上所叙,使用该方式,依次将各个选项提取出来,并计算每个选项包含的白色像素点个数,

7)Step 7:识别考生作答选项

  • 白色像素点个数最多的选项即考生作答选项。如图9-5所示,选项B是考生使用铅笔填涂的选项,其白色像素点个数最多。


    图9-5 作答选项示例
  • 根据轮廓内白色像素点的个数将轮廓按照降序排列,排在最前面的轮廓就是考生作答选项。

8)Step 8:输出结果

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

二、实现程序

# -*- coding: utf-8 -*-
"""
Created on Tue Oct 17 11:15:17 2023

@author: dalong10
"""

import numpy as np
import cv2

# 将选项放入字典内
ANSWER_KEY={0:"A",1:"B",2:"C",3:"D"}   
# 标准答案
ANSWER ="C"

# ====读取原始图像====
img =  cv2.imread('d:\\OpenCVpic\\xiaogang.jpg')  
cv2.imshow('img', 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+cv2.THRESH_OTSU)

# ====提取轮廓及排序====
cnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  

# contoursOK=[]
# for i in cnts:
#     contoursOK.append(i)
# print(contoursOK)    
# draw=cv2.drawContours(thresh,contoursOK,-1,(0,255,0),5)
# cv2.imshow('thresh', thresh)

# 将获取的各选项的轮廓按照从左到右出现的顺序排序
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 对应不同选项
    # cv2.imshow("s1",mask)
    cv2.imwrite("sl.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]

# 根据索引确定选项
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()
cv2.destroyAllWindows()

运行结果 输出数据

相关文章

网友评论

    本文标题:单道题目的识别

    本文链接:https://www.haomeiwen.com/subject/xkmxidtx.html