书名:计算机视觉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()


网友评论