基于opencv的答题卡识别

文章目录

  • 一、背景需求
  • 二、处理步骤
    • 图片预处理
    • 检测到答题卡轮廓
    • 透视变换
    • 找每个圆圈的轮廓
    • 轮廓排序
    • 判断是否答题正确

一、背景需求

传统的手动评分方法耗时且容易出错,自动化评分可以可以显著提高评分过程的速度和准确性、减少人工成本。
答题卡图片处理效果如下:

在这里插入图片描述

在这里插入图片描述

二、处理步骤

图片预处理

# 读图片
img = cv2.imread('./images/test_01.png')
cv_show('img', img)
# 变成黑白图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 去掉一些噪点
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
cv_show('blurred', blurred)# 边缘检测
edged = cv2.Canny(blurred, 75, 200)
cv_show('edged', edged)

检测到答题卡轮廓

# 检测轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
# 画轮廓会修改被画轮廓的图. 
contours_img = img.copy()
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)
cv_show('contous_img', contours_img)
# 确保我们拿到的轮廓是答题卡的轮廓.
if len(cnts) > 0:# 根据轮廓面积对轮廓进行排序.cnts = sorted(cnts, key=cv2.contourArea, reverse=True)# 遍历每一个轮廓for c in cnts:# 计算周长perimeter = cv2.arcLength(c, True)# 得到近似的轮廓approx = cv2.approxPolyDP(c, 0.02 * perimeter, True)# 近似完了之后, 应该只剩下4个角的坐标.
#         print(c)
#         print(approx)if len(approx) == 4:# 保存approxdocCnt = approx# 找到答题卡近似轮廓, 直接推荐.break

在这里插入图片描述

透视变换

# 把透视变换功能封装成一个函数
def four_point_transform(image, pts):# 对输入的4个坐标排序rect = order_points(pts)(tl, tr, br, bl) = rect# 空间中两点的距离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)max_width = 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)max_height = max(int(heightA), int(heightB))# 构造变换之后的对应坐标位置.dst = np.array([[0, 0],[max_width - 1, 0],[max_width - 1, max_height - 1],[0, max_height - 1]], dtype='float32')# 计算变换矩阵M = cv2.getPerspectiveTransform(rect, dst)# 透视变换warped = cv2.warpPerspective(image, M, (max_width, max_height))return warped

透视变化后二值化:

thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cv_show('thresh', thresh)

在这里插入图片描述


此时图片预处理好后如果需要 OCR 文本识别可借助 tesseract 工具识别文字

import pytesseract
from PIL import Image
# pytesseract要求的image不是opencv读进来的image, 而是pillow这个包, 即PIL,按照 pip install pillow
text = pytesseract.image_to_string(Image.open('./scan.jpg'))
print(text)

找每个圆圈的轮廓

# 找到每一个圆圈的轮廓
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
thresh_contours = thresh.copy()
cv2.drawContours(thresh_contours, cnts, -1, 255, 3)
cv_show('thresh_contours', thresh_contours)
plt.imshow(thresh_contours, cmap='gray')# 遍历所有的轮廓, 找到特定宽高和特定比例的轮廓, 即圆圈的轮廓.
question_cnts = []
for c in cnts:# 找到轮廓的外接矩形(x, y, w, h) = cv2.boundingRect(c)# 计算宽高比ar = w / float(h)# 根据实际情况制定标准.if w >= 20 and h >= 20 and 0.9 <= ar <= 1.1:question_cnts.append(c)

在这里插入图片描述

轮廓排序

对轮廓按照y轴排序

# 轮廓排序功能封装成函数
def sort_contours(cnts, method='left-to-right'):reverse = False# 排序的时候, 取x轴数据, i=0, 取y轴数据i =1i = 0if method == 'right-to-left' or method == 'bottom-to-top':reverse = True# 按y轴坐标排序if method == 'top-to-bottom' or method == 'bottom-to-top':i = 1# 计算每个轮廓的外接矩形bounding_boxes = [cv2.boundingRect(c) for c in cnts](cnts, bounding_boxes) = zip(*sorted(zip(cnts, bounding_boxes), key=lambda b: b[1][i], reverse=reverse))return cnts, bounding_boxes
# 按照从上到下的顺序对question_cnts排序.
question_cnts = sort_contours(question_cnts, method='top-to-bottom')[0]

判断是否答题正确

进一步按照x轴排序,构造圆圈轮廓的掩膜得出答案,最后和正确答案比较评判答题卡分数。

# 正确答案
ANSWER_KEY = {0:1, 1:4, 2:0, 3:3, 4:1}
correct = 0
for (q, i) in enumerate(np.arange(0, 25, 5)):
#     print(q, i)# 每次取出5个轮廓, 再按照x轴坐标从小到大排序cnts = sort_contours(question_cnts[i: i + 5])[0]bubbled = None# 遍历每一个结果for (j, c) in enumerate(cnts):# 使用掩膜, 即maskmask = np.zeros(thresh.shape, dtype='uint8')cv2.drawContours(mask, [c], -1, 255, -1) 
#         cv_show('mask', mask)# 先做与运算mask = cv2.bitwise_and(thresh, thresh, mask=mask)
#         cv_show('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]:correct += 1color = (0, 255, 0)# 绘图cv2.drawContours(warped, [cnts[k]], -1, color, 3)# 计算分数
print(correct)
score = (correct / 5.0) * 100
print(f'score: {score:.2f}%')
cv2.putText(warped, str(score) + '%', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
cv_show('result', warped)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

想出国?去外企?建议网工无脑冲思科认证。

近年来&#xff0c;国内职场竞争愈发激烈&#xff0c;内卷现象严重&#xff0c;大部分人都面临着巨大的就业压力&#xff0c;或是找工作无门、或是中年危机悄然来临&#xff0c;时刻担心被职场优化。 在这样的背景下&#xff0c;出国或进入外企工作&#xff0c;成为了许多人寻…

[JavaScript] 动态获取方法参数名

JavaScript&#xff08;简称“JS”&#xff09;是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的编程语言。虽然它是作为开发Web页面的脚本语言而出名&#xff0c;但是它也被用到了很多非浏览器环境中&#xff0c;JavaScript基于原型编程、多范式的动态脚本语言&am…

Optional类的使用 java8(附代码)

&#x1f370; 个人主页:_小白不加班__ &#x1f35e;文章有不合理的地方请各位大佬指正。 &#x1f349;文章不定期持续更新&#xff0c;如果我的文章对你有帮助➡️ 关注&#x1f64f;&#x1f3fb; 点赞&#x1f44d; 收藏⭐️ 文章目录 一、什么是Optional&#xff1f;二、…

科研绘图系列:R语言和弦图 (Chord diagram)

介绍 和弦图(Chord Diagram)是一种用于展示多个实体之间相互关系的数据可视化方法。它通常用于表示网络或系统中不同节点(实体)之间的连接强度或流量。和弦图由一个圆形布局组成,每个节点在圆周上占据一个扇形区域,节点之间的连接通过圆内的线条(和弦)来表示。 特点:…

数据结构第七讲:栈和队列OJ题

数据结构第七讲&#xff1a;栈和队列OJ题 1.有效的括号2.用队列实现栈3.用栈实现队列4.设计循环队列 1.有效的括号 链接: OJ题目链接 typedef char StackDataType;typedef struct Stack {StackDataType* arr;//使用一个指针来指向开辟的数组int capacity;//保存数组的空间大…

springboot爱宠屋宠物商店管理系统-计算机毕业设计源码52726

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 2.2.5 修改信息流程 2.2.6 删除信息流程 2.3 系统功能…

【机器学习】正规方程的简单介绍以及如何使用Scikit-Learn实现基于正规方程的闭式解线性回归

引言 Scikit-learn 是一个开源的机器学习库&#xff0c;它支持 Python 编程语言。它提供了多种机器学习算法的实现&#xff0c;并用于数据挖掘和数据分析 文章目录 引言一、正规方程的定义二、正规方程的原理三、使用 Scikit-Learn 实现基于正规方程的闭式解线性回归3.1 工具3.…

实验15.多线程调度

简介 实验.多线程调度 内核线程 1.在时钟中断函数中处理中&#xff0c;减少当前线程pcb的tick&#xff0c;tick为0则启动调度2.调度&#xff0c;把当前线程pcb放入就绪对立队尾&#xff0c;把就绪线程队首拿出来执行主要代码 引导 省略内核 list.h #ifndef __LIB_KERNEL_…

【2024最新】 服务器安装Ubuntu20.04 (安装教程、常用命令、故障排查)持续更新中.....

安装教程&#xff08;系统、NVIDIA驱动、CUDA、CUDNN、Pytorch、Timeshift、ToDesk、花生壳&#xff09; 制作U盘启动盘&#xff0c;并安装系统 在MSDN i tell you下载Ubuntu20.04 Desktop 版本&#xff0c;并使用Rufus制作UEFI启动盘&#xff0c;参考UEFI安装Ubuntu使用GPTU…

mysql 的MHA

mysql 的MHA 什么是MHA 高可用模式下的故障切换&#xff0c;基于主从复制。 单点故障和主从复制不能切换的问题。 至少需要3台。 故障切换过程0-30秒。 vip地址&#xff0c;根据vip地址所在的主机&#xff0c;确定主备。 主 vip 备 vip 主和备不是优先确定的&#xff…

InternLM Linux 基础知识

完成SSH连接与端口映射并运行hello_world.py 创建并运行test.sh文件 使用 VSCODE 远程连接开发机并创建一个conda环境

“pandas”的坑

参考&#xff1a;百度安全验证 本文基于python第三方数据分析库pandas&#xff0c;分享这几天所遇到的3个爬坑的案例&#xff0c;希望对也在爬坑的同学们尽一份绵薄之力&#xff0c;如有错误或者写得不好的地方&#xff0c;烦请指正&#xff0c;谢谢。 01df中startswith的坑 …

led灯什么牌子的质量好?led灯护眼效果好的五款爆品分享

大家在选择led灯的时候&#xff0c;最关心的就是“led灯什么牌子的质量好&#xff1f;”市面上商家推出来的led灯品牌众多&#xff0c;型号以及功能也是令人眼花缭乱的&#xff0c;既然如此&#xff0c;那我们应该如何买到质量过关又好用的led灯呢&#xff1f;接下来我将为大家…

敏感信息泄露wp

1.右键查看网页源代码 2.前台JS绕过&#xff0c;ctrlU绕过JS查看源码 3.开发者工具&#xff0c;网络&#xff0c;查看协议 4.后台地址在robots,拼接目录/robots.txt 5.用dirsearch扫描&#xff0c;看到index.phps,phps中有源码&#xff0c;拼接目录&#xff0c;下载index.phps …

网页封装app:如何将网站转换为移动应用程序?(网页封装app)

随着移动互联网的普及&#xff0c;越来越多的企业开始关注移动应用程序的开发。但是&#xff0c;对于一些小型企业或个人&#xff0c;开发一款移动应用程序可能需要投入大量的时间和金钱。这时&#xff0c;网页封装app就成了一个不错的选择。 app在线封装www,ppzhu.net 什么是…

【AI人工智能】文心智能体,00后疯感工牌生成器,低代码工作流的简单应用以及图片快速响应解决方案,干货满满,不容错过哦

背景 文心智能体平台&#xff0c;开启新一轮活动&#xff0c;超级创造营持续百日活动。 在AI 浪潮席卷的今天&#xff0c;如雨后春笋般丛生的 AI 应用&#xff0c;昭告着时代风口显然已随之到来。 如何能把握住时代红利&#xff0c;占据风口&#xff0c;甚至打造新风向&#x…

探索 Kubernetes 持久化存储之 Longhorn 初窥门径

作者&#xff1a;运维有术星主 在 Kubernetes 生态系统中&#xff0c;持久化存储扮演着至关重要的角色&#xff0c;它是支撑业务应用稳定运行的基石。对于那些选择自建 Kubernetes 集群的运维架构师而言&#xff0c;选择合适的后端持久化存储解决方案是一项至关重要的选型决策。…

因为媳妇的一句话,我做了一个AI画图软件

因为媳妇的一句话&#xff0c;我做了一个AI画图软件 T恤的配图 前些天媳妇参加了一个创业比赛&#xff0c;其中一个比赛任务是参赛成员需要穿主题队服&#xff0c;队服的图案完全需要自己设计&#xff0c;需要独一无二还得漂亮。 问我&#xff1a;“能不能用AI做一张图&#…

Python酷库之旅-第三方库Pandas(052)

目录 一、用法精讲 191、pandas.Series.drop方法 191-1、语法 191-2、参数 191-3、功能 191-4、返回值 191-5、说明 191-6、用法 191-6-1、数据准备 191-6-2、代码示例 191-6-3、结果输出 192、pandas.Series.droplevel方法 192-1、语法 192-2、参数 192-3、功能…

C# 介绍

文章目录 一. 一个简单的helloworld二. 程序结构三. 类型和变量四. 表达式1. f(x)2. []3. typeof4. default5. new6. checked和unchecked7. sizeof8. 移位9. is和as10. null合并 五. 语句六. 类和对象1. 可访问性2. 类型参数3. 基类和派生类4. 字段5. 方法6. 参数7. 扩展方法&a…