opencv实现答题卡识别
作者:qq_36008031 发布时间:2021-02-28 12:40:05
标签:opencv,答题卡,识别
本文实例为大家分享了opencv实现答题卡识别的具体代码,供大家参考,具体内容如下
"""
识别答题卡
"""
import cv2
import numpy as np
def showImg(img_name, img):
cv2.imshow(img_name, img)
cv2.waitKey()
cv2.destroyAllWindows()
def get_max_rect(sorted_cnts):
for cnt in sorted_cnts:
# 轮廓近似
possible_cnts = []
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
if len(approx) == 4:
possible_cnts.append(cnt)
possible_cnts = sorted(possible_cnts, key=lambda x: cv2.arcLength(x, True))
return possible_cnts
def get_max_bounding_rect(possible_cnts):
# for cnt in possible_cnts:
# x, y, w, h = cv2.boundingRect(cnt)
sorted_cnts = sorted(possible_cnts, key=lambda cnt: cv2.boundingRect(cnt)[2]*cv2.boundingRect(cnt)[3], reverse=True)
print(sorted_cnts[0])
def show_countour(img, cnt):
img_copy = img.copy()
cv2.drawContours(img_copy, cnt, -1, (0,255, 0), 3)
showImg("img_copy", img_copy)
# 读取答题卡图片,并显示
answer_sheet_img = cv2.imread("t1.jpg")
print(answer_sheet_img.shape)
showImg("answer_sheet_img", answer_sheet_img)
# 高斯滤波,去除噪音
blur = cv2.GaussianBlur(answer_sheet_img,(5,5),0)
showImg("blur", blur)
# 图像转灰度值
sheet_gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
showImg("sheet_gray", sheet_gray)
# 二值化
retval, sheet_threshold = cv2.threshold(sheet_gray,177, 255, cv2.THRESH_BINARY)
# print(type(sheet_threshold), sheet_threshold)
showImg("sheet_threshold", sheet_threshold)
# 边界检测
edges = cv2.Canny(sheet_threshold, 100, 200)
showImg("edges", edges)
# print(type(edges))
# 寻找轮廓
copy_edges = edges.copy()
img_copy = answer_sheet_img.copy()
img, cnts, hierarchy = cv2.findContours(copy_edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img_copy, cnts, -1, (0,0,255), 1)
showImg("img_copy", img_copy)
# 对所有轮廓加一个外接矩形,找最大的外接矩形
max_area_index = None
area = 0
for index, cnt in enumerate(cnts):
x, y, w, h = cv2.boundingRect(cnt)
if w*h > area:
max_area_index = index
show_countour(answer_sheet_img, cnts[max_area_index])
# 仿射,拿到答题卡主要部位
x, y, w, h = cv2.boundingRect(cnts[max_area_index]) # 最大的边界
cv2.rectangle(answer_sheet_img, (x, y),(x+w, y+h), (0,0,255), 2)
showImg("answer_sheet_img", answer_sheet_img)
pts1 = np.float32([[x,y], [x+w, y], [x+w, y+h]])
pts2 = np.float32([[0,0], [w, 0], [w, h]])
M = cv2.getAffineTransform(pts1, pts2)
sheet_threshold_copy = sheet_threshold.copy()
dst = cv2.warpAffine(sheet_threshold_copy, M, (w, h))
showImg("dst", dst)
print(answer_sheet_img.shape)
part_sheet_img = answer_sheet_img[y:y+h, x:x+w]
showImg("part_sheet_img", part_sheet_img)
# 对答案区域灰度,二值,找轮廓
part_answer_gray = cv2.cvtColor(part_sheet_img, cv2.COLOR_BGR2GRAY) # 灰度
ret, threshold_answer = cv2.threshold(part_answer_gray, 175, 255, cv2.THRESH_BINARY)
showImg("threshold_answer", threshold_answer)
img, answer_cnts, x = cv2.findContours(threshold_answer, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
part_sheet_img_copy = part_sheet_img.copy()
cv2.drawContours(part_sheet_img_copy, answer_cnts, -1, (0, 0, 255), 1)
showImg("dst_copy", part_sheet_img_copy)
# 对所有轮廓找外接矩形,想过滤掉不合适的矩形
print("画矩形")
answer_filter_cnts = []
answer_circles = []
img_ = part_sheet_img.copy()
for cnt in answer_cnts:
x, y, w, h = cv2.boundingRect(cnt)
if 30<w<40 and 30<h<40:
print(x, y, w, h)
circle_x = int(x + w/2)
circle_y = int(y+h/2)
r = int((w+h)/4)
answer_circles.append((circle_x, circle_y, r))
answer_filter_cnts.append(cnt)
answer_filter_cnts = np.array(answer_filter_cnts)
cv2.drawContours(img_, answer_filter_cnts, -1, (0, 0, 255), 1)
# cv2.rectangle(img, (x, y), (x+w, y+h), (0,255,0), 2)
showImg("img_", img_)
print("geshu", len(answer_circles))
# 从answer_circles中取25个
mask_dict = {1:[],2:[], 3:[], 4:[],5:[]} # 一共不一定是25个圆,将圆按照题目行分类,
sorted_y_answer_circles = sorted(answer_circles, key=lambda circle: circle[1])
print("sorted_y_answer_circles", sorted_y_answer_circles)
set_num = 1
for index, circle in enumerate(sorted_y_answer_circles):
if index == 0:
mask_dict[1].append(circle)
else:
if circle[1] - sorted_y_answer_circles[index-1][1] > 30:
set_num += 1
mask_dict[set_num].append(circle)
else:
mask_dict[set_num].append(circle)
print("mask_dict", mask_dict)
for k, mask_circle_list in mask_dict.items(): # 对每一个题目,保留五个答案,多余的舍去
if len(mask_circle_list) == 5:
sorted_x_mask_circle_list = sorted(mask_circle_list, key=lambda x:x[0])
mask_dict[k]=sorted_x_mask_circle_list
else:
sorted_x_mask_circle_list = sorted(mask_circle_list, key=lambda x: x[0])
sorted_x_mask_circle_list_5 = []
for i, c in enumerate(sorted_x_mask_circle_list):
if i == 0:
sorted_x_mask_circle_list_5.append(c)
else:
if abs(c[0] - sorted_x_mask_circle_list[i-1][0]) < 10:
pass
else:
sorted_x_mask_circle_list_5.append(c)
mask_dict[k] = sorted_x_mask_circle_list_5
print("mask_dict", mask_dict)
# mask_dict 分好组的按照顺序的圈圈
# 做掩码
mask_img = np.zeros_like(part_sheet_img, dtype='uint8') # 全黑图
showImg("threshold_answer", threshold_answer)
threshold_answer = np.array(threshold_answer)
# mask_dict = sorted(mask_dict, key=lambda x: mask_dict.keys())
all_scores = [] # 所有答案处的评分
for exercise_num, circle_mask_list in mask_dict.items():
# 对于每一题
score_list = [] # 每一题的每个选项的评分,涂黑的为选择的,值越接近0, 评分较低
for circle_mask in circle_mask_list:
mask_img_copy = cv2.cvtColor(mask_img, cv2.COLOR_BGR2GRAY)
# 做一个当前圆的掩码:
cv2.circle(mask_img_copy, (circle_mask[0], circle_mask[1]), circle_mask[2], (255, 255, 255), -1)
print(threshold_answer.shape, mask_img_copy.shape)
mask_img_ = cv2.bitwise_and(threshold_answer, threshold_answer, mask=mask_img_copy)
score = mask_img_.sum()
score_list.append(score)
# showImg("mask_img_", mask_img_)
all_scores.append(score_list)
all_score_np = np.array(all_scores)
s = np.argmin(all_score_np, axis=1) # 找评分最低处即为选择项
answer_dict = {
0: "A",
1: "B",
2: "C",
3: "D",
4: "E"
}
for index, v in enumerate(s):
print("第%s题的答案是%s" %(index+1, answer_dict[v]))
效果图:
来源:https://blog.csdn.net/qq_36008031/article/details/104464227
![](https://www.aspxhome.com/images/zang.png)
![](https://www.aspxhome.com/images/jiucuo.png)
猜你喜欢
- 本文实例为大家分享了python批量文件重命名的具体代码,供大家参考,具体内容如下问题描述最近遇到朋友求助,如何将大量文件名前面的某些字符删
- 前言推导式提供了更简洁高效的方法来生成序列而又不失代码的可读性。定义: 推导式是 Python 里很有用的一个特性,它可以用一行代码就可以创
- 可以把多个页面相同的部分提取出来,放在一个母板里,这些页面只需要继承这个母板就好了通常会在母板中定义页面专用的 CSS 块和 JS 块,方便
- 开启xp_cmdshell: exec sp_configure 'show advance
- 作为语言模型和文本挖掘中的常用工具,Word2Vec也可以用来构建聊天机器人。在本文中,我们将使用Python和Gensim库从头开始构建一
- 1.由于数据库设计问题造成SQL数据库新增数据时超时症状:Microsoft OLE DB Provider for SQL Server
- 思路1.将姓名和单号填入excel表格里面2.读取excel表格,将所有姓名存到ExeclName这个list中,单号存到ExeclId3.
- 本文实例讲述了Python脚本实现虾米网签到功能的方法。分享给大家供大家参考,具体如下:概述这个脚本完成了自动登录虾米网、签到的功能。大致要
- 定义和用法fopen() 函数打开文件或者 URL。如果打开失败,本函数返回 FALSE。语法fopen(filename,mode,inc
- 在我们有时需要迁移或部署项目时,需要知道项目所依赖的三方包和版本,下面就来一看一看该如何获取吧:1、首先安装pipreqs库使用pip命令,
- 获取一组href元素属性的值lst = driver.find_elements_by_class_name("ib-it-tex
- 我们可以通过 asyncio.wait() 函数等待异步任务完成。可以等待不同的条件,例如所有任务完成、第一个任务完成以及第一个任务因异常而
- 简介HTTP协议规定post提交的数据必须放在消息主体中,但是协议并没有规定必须使用什么编码方式。服务端通过是根据请求头中的Content-
- 一、Scrapy是什么Scrapy 是一个基于 Twisted 的异步处理框架,是纯 Python 实现的爬虫框架,其架构清晰,模块之间的耦
- 我们在工作中经常强调沟通能力,和产品、开发、测试等不同角色的人需要沟通,和领导、同事需要沟通,沟通是一个双向的过程,而沟通首先需要双方有良好
- 做了一个Python的小项目。利用了一点python的可视化技巧,做出烟花绽放的效果,文章的灵感来自网络上一位大神。一.编译环境Pychar
- 今天学习了如何使用pygame来制作小游戏,下面是五子棋的代码,我的理解都写在注释里了import pygame# 导入pygame模块pr
- 前段时间做一个小项目碰到了一个导航制作的方式然后突然想到曾经很久以前看到的梯形状的不规则导航,就尝试做了一下。结果碰到了几个问题,后来在同事
- 本文实例讲述了python简单实现基数排序算法。分享给大家供大家参考。具体实现方法如下:from random import randint
- 在PyCharm2017中同目录下import其他模块,会出现No model named ...的报错,但实际可以运行这是因为PyChar