目录

一、项目原理

二、环境准备

三、核心代码实现

1. 导入必要库

2. 定义关键函数

坐标点排序函数

透视变换函数

轮廓排序函数

图像显示函数

3. 主程序实现

图像预处理

轮廓检测与答题卡定位

透视变换矫正

答案识别与评分

四、实现效果


本文将介绍如何使用 OpenCV 实现一个简单的答题卡识别与评分系统,通过图像处理技术自动识别考生答案并进行评分。


一、项目原理

答题卡识别的核心思路是通过图像处理技术定位答题卡区域,识别考生填涂的答案,再与标准答案对比进行评分。主要步骤包括:图像预处理、轮廓检测、透视变换、答案识别和自动评分。


二、环境准备

pip install numpy opencv-python

三、核心代码实现

1. 导入必要库

import numpy as np
import cv2

2. 定义关键函数

坐标点排序函数

用于对四边形的四个顶点进行排序(左上、右上、右下、左下):

def order_points(pts):# 一共有4个坐标点rect = np.zeros(shape=(4, 2), dtype="float32")  # 用来存储排序之后的坐标位置# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下s = pts.sum(axis=1)  # 对pts矩阵的每一行进行求和操作,(x+y)rect[0] = pts[np.argmin(s)]rect[2] = pts[np.argmax(s)]diff = np.diff(pts, axis=1)  # 对pts矩阵的每一行进行求差操作,(y-x)rect[1] = pts[np.argmin(diff)]rect[3] = pts[np.argmax(diff)]return rect

透视变换函数

将倾斜的答题卡矫正为正视图:

def four_point_transform(image, pts):# 获取输入坐标点rect = order_points(pts)(tl, tr, br, bl) = rect# 计算输入的w和h值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))maxWidth = 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))maxHeight = max(int(heightA), int(heightB))# 变换后对应坐标位置dst = np.array([[0, 0], [maxWidth - 1, 0],[maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32")# 图像透视变换M = cv2.getPerspectiveTransform(rect, dst)warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))# 返回变换后结果return warped

轮廓排序函数

按照指定方向(左右或上下)对轮廓进行排序:

def sort_contours(cnts, method='left-to-right'):reverse = Falsei = 0if method == 'right-to-left' or method == 'bottom-to-top':reverse = Trueif method == 'top-to-bottom' or method == 'bottom-to-top':i = 1boundingBoxes = [cv2.boundingRect(c) for c in cnts](cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),key=lambda b: b[1][i], reverse=reverse))return cnts, boundingBoxes

图像显示函数

方便调试过程中显示图像:

def cv_show(name, img):cv2.imshow(name, img)cv2.waitKey(0)

3. 主程序实现

图像预处理

# 读取图像
image = cv2.imread(r'./images/test_01.png')
contours_img = image.copy()
# 转为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 高斯模糊去噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 边缘检测
edged = cv2.Canny(blurred, 75, 200)

轮廓检测与答题卡定位

# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
cv2.drawContours(contours_img, cnts, -1, (0, 0, 255), 3)docCnt = None# 根据轮廓大小进行排序,寻找答题卡轮廓(四边形)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
for c in cnts:peri = cv2.arcLength(c, True)approx = cv2.approxPolyDP(c, 0.02 * peri, True)  # 轮廓近似if len(approx) == 4:  # 找到四边形轮廓docCnt = approxbreak

透视变换矫正

# 执行透视变换
warped_t = four_point_transform(image, docCnt.reshape(4, 2))
warped_new = warped_t.copy()
warped = cv2.cvtColor(warped_t, cv2.COLOR_BGR2GRAY)# 阈值处理,将答案区域二值化
thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]

答案识别与评分

# 找到每一个圆圈轮廓
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]questionCnts = []
# 筛选出符合条件的答案圆圈轮廓
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:questionCnts.append(c)# 按照从上到下进行排序
questionCnts = sort_contours(questionCnts, method="top-to-bottom")[0]
correct = 0
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}  # 正确答案# 每排有5个选项
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):cnts = sort_contours(questionCnts[i:i + 5])[0]  # 排序bubbled = None# 遍历每一个选项for (j, c) in enumerate(cnts):# 使用mask来判断结果mask = np.zeros(thresh.shape, dtype="uint8")cv2.drawContours(mask, [c], -1, 255, -1)  # -1表示填充# 通过计算非零点数量来判断是否选择这个答案thresh_mask_and = cv2.bitwise_and(thresh, thresh, mask=mask)total = cv2.countNonZero(thresh_mask_and)  # 统计灰度值不为0的像素数if bubbled is None or total > bubbled[0]:  # 保存灰度值最大的序号(被填涂的选项)bubbled = (total, j)# 对比正确答案color = (0, 0, 255)  # 错误为红色k = ANSWER_KEY[q]if k == bubbled[1]:  # 判断正确color = (0, 255, 0)  # 正确为绿色correct += 1cv2.drawContours(warped_new, [cnts[k]], -1, color, 3)  # 绘制结果# 计算得分
score = (correct / 5.0) * 100
print("[INFO] score: {:.2f}%".format(score))# 显示得分
cv2.putText(warped_new, "{:.2f}%".format(score), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
cv2.imshow("Original", image)
cv2.imshow("Exam", warped_new)
cv2.waitKey(0)

四、实现效果

程序会自动识别答题卡中的填涂区域,与标准答案对比后,用绿色标记正确答案,红色标记错误答案,并在图像上显示最终得分。

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

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

相关文章

机器宠物(以四足宠物为主)四肢与关节的系统化设计指南

1. 目标与约束先行 目标&#xff1a;自然步态&#xff08;走/小跑/小跳&#xff09;、安全亲和、低噪、跌倒不致损&#xff1b;支持地毯/木地板/瓷砖等家庭地面。约束&#xff1a;体重 1–6 kg&#xff1b;单次续航 ≥ 30–60 min&#xff1b;整机成本与可维护性&#xff1b;室…

spark hive presto doris 对substr函数的差异

Spark、Hive、Presto&#xff08;现更名为 Trino&#xff09;和 Doris&#xff08;原百度 Palo&#xff09;的 substr 函数在功能上都是用于截取字符串的子串&#xff0c;但在起始索引规则和参数含义上存在差异&#xff0c;这是导致结果不同的主要原因。以下是它们的具体区别&a…

开题报告之基于AI Agent智能问答的旅游网站

课题题目&#xff1a; 基于AI Agent智能问答的旅游网站 学生姓名&#xff1a; 学 号&#xff1a; 学 院&#xff1a; 专业年级&#xff1a; 指导教师&#xff1a; 开题报告word版&#xff1a; 开题报告word版 一、课题的研究目的和意义&#xff08;本…

HTB打靶复个小盘

文章目录jerrySauGoodGamesdevvotexpaper最近打了不少靶场&#xff0c;虽然难度都不算高&#xff0c;但也学到不少东西&#xff0c;中间去打了一周的实网渗透&#xff0c;打完后联系了一家企业准备面试&#xff0c;感觉面试准备的差不多了&#xff0c;回来继续打靶&#xff0c;…

云手机的技术架构可分为哪些

一、基础设施层为其提供计算、存储和网络资源&#xff0c;高性能的服务器 CPU 是关键&#xff0c;它需具备多核多线程处理能力&#xff0c;以同时支持多个云手机实例的运行&#xff0c;比如英特尔至强系列处理器&#xff0c;能够有效处理复杂的运算任务&#xff1b;通过虚拟化技…

[创业之路-585]:初创公司的保密安全与信息公开的效率提升

初创公司处于快速发展与资源有限的双重约束下&#xff0c;平衡保密安全与信息公开效率是生存与发展的关键。保密安全可保护核心资产&#xff08;如技术、客户数据、商业计划&#xff09;&#xff0c;避免被竞争对手模仿或恶意攻击&#xff1b;而信息公开的效率则直接影响团队协…

如何在Docker容器中为Stimulsoft BI Server配置HTTPS安全访问

在 Stimulsoft BI Server 2025.3.1 版本中&#xff0c;新增了在 Docker 容器中运行 BI Server 的能力。本文将为大家介绍如何在容器环境中为 BI Server 配置 HTTPS 协议的数据传输&#xff0c;从而实现安全、加密的访问。 为什么需要 HTTPS&#xff1f; **HTTPS&#xff08;S…

PPT中将图片裁剪为爱心等形状

在WPS演示和PowerPoint中&#xff0c;使用裁剪功能&#xff0c;可以将插入的图片裁剪为各种形状&#xff0c;例如心形、五角形、云朵形等等。WPS演示还可以指定裁剪的位置&#xff0c;更加灵活。一、在PowerPoint中裁剪图片为爱心等形状将图片插入到幻灯片后&#xff0c;选中图…

深入理解Docker网络:实现容器间的内部访问

目录一、利用宿主机 IP 外部端口实现容器互访1.思路2.示例操作3.访问测试4.工作原理5.总结二、Docker 容器之间的网络通信&#xff08;docker0 与自定义桥接网络&#xff09;1. docker0 简介2. 通过容器 IP 访问3. 自定义桥接网络&#xff08;推荐方式&#xff09;创建自定义网…

ESD静电保护二极管焊接时需要区分方向和极性吗?-深圳阿赛姆

ESD静电保护二极管焊接时需要区分方向和极性吗&#xff1f;一、ESD二极管极性概述1.1 单向与双向ESD二极管的基本区别ESD静电保护二极管根据其内部结构和工作原理可分为两种主要类型&#xff1a;单向ESD二极管&#xff08;Unidirectional&#xff09;&#xff1a;具有明确的阳极…

Qt QML Switch和SwitchDelegate的区别?

在 Qt QML 中&#xff0c;Switch和 SwitchDelegate主要区别体现在定位、使用场景和功能特性上。以下是具体分析&#xff1a;​1. 核心定位​​Switch​&#xff1a;是一个基础的独立交互控件​&#xff08;继承自 ToggleButton&#xff09;&#xff0c;用于直接提供“开/关”&a…

no module name ‘kaolin‘

如果报错 no module named xxx 一般是没安装这个库&#xff0c;但是各种邪修安装了kaolin之后&#xff0c;还是报错&#xff0c;这个报错的核心信息是&#xff1a; ImportError: .../kaolin/_C.so: undefined symbol: _ZN3c104cuda20CUDACachingAllocator9allocatorE意思是 Ka…

OBS使用教程:OBS歌曲显示插件如何下载?如何安装使用?

OBS使用教程&#xff1a;OBS歌曲显示插件如何下载&#xff1f;如何安装使用&#xff1f; 第一步&#xff1a;下载OBS歌曲显示插件&#xff0c;并完成安装 OBS歌曲显示插件下载地址①&#xff1a; https://d.obscj.com/obs-Setup_BGM.exe OBS歌曲显示插件下载地址②&#xf…

基于 Java EE+MySQL+Dart 实现多平台应用的音乐共享社区

基于多平台应用的音乐共享社区 1 绪论 1.1 课题依据及意义 随着互联网娱乐项目的日益增多&#xff0c;内容也日渐丰富&#xff0c;加之网络便利性的增强&#xff0c;越来越多的用户喜欢在网上听音乐。但是各平台音乐资源残次不齐&#xff0c;也包含了许多假无损音乐&#xf…

贪心算法在物联网能耗优化中的应用

Java中的贪心算法在物联网能耗优化中的应用 贪心算法是一种在每一步选择中都采取当前状态下最优决策的算法策略&#xff0c;它在物联网(IoT)能耗优化问题中有着广泛的应用。下面我将全面详细地讲解如何使用Java实现贪心算法来解决物联网中的能耗优化问题。 一、物联网能耗优化问…

59.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--新增功能--MinIO对象存储服务

在孢子记账中我们需要存储用户的头像、账单的图片等文件&#xff0c;这些文件的存储我们可以使用MinIO对象存储服务&#xff0c; MinIO提供了高性能、可扩展的对象存储解决方案&#xff0c;能够帮助我们轻松管理这些文件资源。通过MinIO&#xff0c;我们可以将用户上传的图片文…

ESP32三种主流的开发环境

ESP32三种主流的开发环境 1. ESP-IDF (Espressif IoT Development Framework) 这是乐鑫官方提供的专业开发框架&#xff0c;基于FreeRTOS实时操作系统。 特点&#xff1a; 功能最全面&#xff0c;性能最优支持所有ESP32硬件特性使用C/C编程专业级调试工具完整的组件库和API 适合…

HarmonyOS图形处理:Canvas绘制与动画开发实战

本文将全面介绍HarmonyOS 5中Canvas组件的使用方法和动画开发技巧&#xff0c;通过详细的代码示例和最佳实践&#xff0c;帮助您掌握图形绘制和动态效果实现的核心技能。 1. Canvas组件基础与核心API Canvas是HarmonyOS中用于2D图形绘制的重要组件&#xff0c;提供了丰富的绘图…

CCAFusion:用于红外与可见光图像融合的跨模态坐标注意力网络

CCAFusion&#xff1a;用于红外与可见光图像融合的跨模态坐标注意力网络 CCAFusion: Cross-Modal Coordinate Attention Network for Infrared and Visible Image Fusion 摘要 红外与可见光图像融合旨在生成一幅包含全面信息的图像&#xff0c;该图像既能保留丰富的纹理特征&a…

ESP32-P4小智编译历险记:从“编译失败“到“成功烧录“的奇幻之旅,xiaozhi智能聊天机器人编译避坑心得

🚀 ESP32-P4:AI小智编译历险记:从"编译失败"到"成功烧录"的奇幻之旅 要编译其他芯片esp32s3-s2-c3,遇到问题也可以在这里交流 “每一个编译错误都是成长的机会,每一次成功都是坚持的胜利!” —— 某位被编译器折磨的程序员 源码地址:https://githu…