当前位置: 首页 > news >正文

OpenCV:答题卡识别

目录

一、项目原理

二、环境准备

三、核心代码实现

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://MVVVDhFf.bsjxh.cn
http://MIiabpOh.bsjxh.cn
http://uAqQyLuZ.bsjxh.cn
http://gAHMxsQh.bsjxh.cn
http://QGKrRZ5B.bsjxh.cn
http://UQ5Dp2EC.bsjxh.cn
http://UfboeBuH.bsjxh.cn
http://CMq8tYGX.bsjxh.cn
http://7vU2EIOT.bsjxh.cn
http://8h7DDMw5.bsjxh.cn
http://ULrqRRg3.bsjxh.cn
http://h1Jt8tjv.bsjxh.cn
http://7zP2r4zq.bsjxh.cn
http://t9zKcGKk.bsjxh.cn
http://Gj7kSb4m.bsjxh.cn
http://3mQgCFa0.bsjxh.cn
http://pAjJ34iS.bsjxh.cn
http://4j9MP1CB.bsjxh.cn
http://kOOmU2N8.bsjxh.cn
http://qj1o6qPy.bsjxh.cn
http://zBYIAUXB.bsjxh.cn
http://ne7mRuK4.bsjxh.cn
http://1e95DUZW.bsjxh.cn
http://dSKf6eoE.bsjxh.cn
http://C7Y6OInS.bsjxh.cn
http://D5SjQdNW.bsjxh.cn
http://qX3kjbeV.bsjxh.cn
http://GIguSjoj.bsjxh.cn
http://4zvEVwal.bsjxh.cn
http://SddDT6SG.bsjxh.cn
http://www.dtcms.com/a/386301.html

相关文章:

  • leetcode HOT100 个人理解及解析
  • 深入落地“人工智能+”,如何构建安全、高效的算力基础设施?
  • 无人出租车(Robotaxi)还有哪些技术瓶颈?
  • 安全开发生命周期管理
  • 用住宿楼模型彻底理解Kubernetes架构(运行原理视角)
  • 【大模型】minimind2 1: ubuntu24.04安装部署 web demo
  • 扩散模型之(八)Rectified Flow
  • Facebook主页变现功能被封?跨境玩家该如何申诉和预防
  • 《Java接入支付宝沙箱支付全流程详解》
  • DevOps实战(8) - 使用Arbess+GitLab+PostIn实现Go项目自动化部署
  • 趣味学RUST基础篇(高级特征)
  • 随机森林(Random Forest)学习笔记
  • css之Flex响应式多列布局,根据容器宽度自动调整显示2列或3列布局,支持多行排列
  • HTML应用指南:利用POST请求获取全国中石化易捷门店位置信息
  • PDF24 Creator:免费全能的PDF处理工具
  • 小程序交互与一些技术总结
  • Spring Cloud - 面试知识点(负载均衡)
  • 易特ERP软件局域网版安装教程
  • qt QBoxSet详解
  • 电脑散热风扇有噪音怎么解决
  • 行业分享丨汽车电磁兼容仿真技术与应用
  • 缓存与数据库一致性的4大坑及终极解决方案
  • 机器学习面试题:请讲一讲分类评估方式?
  • 【pure-admin】前端使用pure-admin后台管理系统框架,后端使用FastAPI的前端向后端加密发送用户登录密码的完整示例
  • 从 Node.js 安装到 Vue 3 开发环境搭建
  • Python单元测试框架之pytest -- 生成测试报告
  • 使用HBuilderX新建uniapp项目
  • 医疗行业安全合规数据管理平台:构建高效协作与集中化知识沉淀的一体化解决方案
  • 从一次鼠标点击窥探操作系统内核:中断、驱动、IPC与内存安全的奇幻之旅
  • 【超详细】C#的单例模式