OpenCV实现答题卡自动打分!
目录
1,主要原理以及函数介绍
全部代码,以
2 , 实现过程
3,结果展示
1,主要原理以及函数介绍
ap = argparse.ArgumentParser()
创建一个ArgumentParser对象,并将其赋值给变量ap。这个对象可以接受我们的脚本的命令行参数,从而实现根据命令行参数执行不同的操作。
ap.add_argument("-i", "--image", required=True,help="path to the input image")
添加一个命令行参数-i/--image,并指定它是必需的,用户必须提供一个图像路径作为输入。
args = vars(ap.parse_args())
解析命令行参数并将它们存储在args字典中
ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}
定义一个字典,其中包含每道题目的正确答案。键是问题的索引、值是正确答案的编号
def order_points(pts):
定义函数order_points,用于按照指定顺序找到四个坐标点
def four_point_transform(image, pts):
定义函数four_point_transform,用于执行透视变换。
透视变换是一种在图像处理领域中常用的变换方式,它可以将一个平面上的图像投影到一个新的视平面上。透视变换通常用于纠正图像中的透视畸变,例如将斜着拍摄的照片转换为正常的平面视图。在透视变换中,图像中的平行线可能会变成不平行线,而原本不平行的线可能会变成平行线。函数four_point_transform就是利用四个特定的点来确定透视变换矩阵,并对输入的图像进行透视变换。
def sort_contours(cnts, method="left-to-right"):
定义函数sort_contours,用于对轮廓进行排序
def cv_show(name,img):
定义函数cv_show,用于显示图像。
image = cv2.imread(args["image"]):
读取输入的图像,并将其存储在变量image中
contours_img = image.copy():
复制图像,用于在图像上绘制轮廓
cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]
查找图像中的轮廓,使用外部轮廓检查模式(RETR_EXTERNAL)只检测外部轮廓,使用简单的轮廓逼近算法(CHAIN_APPROX_SIMPLE)来减少点数。返回值是一个元组,其中第二个元素表示轮廓列表
cv2.drawContours(contours_img,cnts,-1,(0,0,255),3)
将找到的轮廓绘制在图像上,颜色为红色
docCnt = None
初始化docCnt变量,用于存储找到的四个坐标点
if len(cnts) > 0:
如果找到了轮廓,则执行以下操作
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
对轮廓根据其面积大小进行排序,从大到小排列
peri = cv2.arcLength(c, True) 计算轮廓周长
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
使用迭代逼近算法获取轮廓的近似形状。如果近似的轮廓拥有四个顶点,则将其作为找到的四个坐标点。
if len(approx) == 4: 如果找到了四个坐标点,则将它们保存到docCnt变量中,并跳出循环。
warped = four_point_transform(gray, docCnt.reshape(4, 2))
对图像进行透视变换,将找到的四个坐标点映射到新的矩阵中
thresh = cv2.threshold(warped, 0, 255,cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
对变换后的图像进行二值化处理,使用Otsu's自适应阈值处理算法来获取最优阈值。返回值是一个元组,其中第二个元素是处理后的图像。
hresh_Contours = thresh.copy()
复制处理后的图像,用于在上面绘制轮廓
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[1]
查找处理后图像中的轮廓,并使用RETR_EXTERNAL和CHAIN_APPROX_SIMPLE参数。
cv2.drawContours(thresh_Contours,cnts,-1,(0,0,255),3)
将找到的轮廓绘制在二值化图像上
cv_show('thresh_Contours',thresh_Contours)
调用cv_show函数,显示绘制轮廓后的图像
questionCnts = []:初始化questionCnts列表。
for c in cnts::遍历所有轮廓:
(x, y, w, h) = cv2.boundingRect(c):获取表示轮廓区域位置和大小的矩形。
ar = w / float(h):计算矩形的宽高比。
if w >= 20 and h >= 20 and ar >= 0.9 and ar <= 1.1::如果矩形满足指定的条件,则将其添加到questionCnts列表中。
questionCnts = sort_contours(questionCnts,method="top-to-bottom")[0]:对questionCnts列表中的轮廓按照从上到下的顺序进行排序。for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):对每一行问题进行处理。cnts = sort_contours(questionCnts[i:i + 5])[0]:对每个问题中的五个选项进行排序。bubbled = None:初始化变量bubbled,用于保存所选答案。for (j, c) in enumerate(cnts)::遍历每个选项。mask = np.zeros(thresh.shape, dtype="uint8") :创建一个全黑图像,大小与thresh相同。cv2.drawContours(mask, [c], -1, 255, -1):绘制选项的轮廓。mask = cv2.bitwise_and(thresh, thresh, mask=mask):将二值化图像thresh与mask相乘,只保留交集部分。total = cv2.countNonZero(mask):计算非零像素的数目,用于判断是否选择了这个选项。if bubbled is None or total > bubbled[0]::如果之前没有选择过或者当前选择的像素数更多,则更新选择。bubbled = (total, j):将当前选择的像素数以及选项索引保存到bubbled变量中。color = (0, 0, 255):初始化颜色为红色。k = ANSWER_KEY[q]:获取当前问题的正确答案。if k == bubbled[1]::如果当前选项是正确的,则将颜色设置为绿色,并增加正确计数器。color = (0, 255, 0)
correct += 1cv2.drawContours(warped, [cnts[k]], -1, color, 3):将选项的轮廓绘制在变换后的图像上,颜色为正确或错误的颜色。score = (correct / 5.0) * 100:计算得分。print("[INFO] score: {:.2f}%".format(score)):输出得分。cv2.putText(warped, "{:.2f}%".format(score), (10, 30),cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2):在变换后的图像上绘制得分。cv2.imshow("Original", image):显示原始图像。cv2.imshow("Exam", warped):显示处理结果。cv2.waitKey(0):等待用户按下任意键。
全部代码,以
2 , 实现过程
-
导入所需的库和模块:首先导入numpy、argparse、imutils和cv2等库,用于进行科学计算、命令行参数解析、图像处理和机器视觉任务。
-
解析命令行参数:使用argparse库创建ArgumentParser对象,并添加一个命令行参数-i/--image,要求用户提供一个输入图像的路径。然后使用ap.parse_args()方法解析命令行参数,并将结果存储在args字典中。
-
定义正确答案字典:创建一个字典,其中包含每道题目的正确答案编号。
-
函数定义:定义了四个函数,分别用于找到四个坐标点、执行透视变换、对轮廓进行排序以及显示图像。
-
读取输入图像:使用cv2.imread()函数读取输入的图像,并将其存储在变量image中。
-
图像预处理:将图像转换为灰度图像,然后应用高斯模糊以平滑边缘并减少噪声。接下来使用Canny算法进行边缘检测,得到二值化的边缘图像。
-
查找轮廓:使用cv2.findContours()函数查找图像中的轮廓,并使用外部轮廓检查模式(RETR_EXTERNAL)只检测外部轮廓,使用简单的轮廓逼近算法(CHAIN_APPROX_SIMPLE)来减少点数。获取到的轮廓存储在cnts变量中。
-
找到答题卡区域:遍历轮廓,计算轮廓周长和近似形状,并通过一系列条件判断找到包含答题卡的轮廓。将找到的四个坐标点保存到docCnt变量中。
-
执行透视变换:使用four_point_transform()函数对图像进行透视变换,将找到的四个坐标点映射到新的矩阵中。
-
对变换后的图像进行二值化处理:使用Otsu's自适应阈值处理算法对图像进行二值化处理,得到二值化的图像。
-
查找轮廓:使用cv2.findContours()函数再次查找处理后图像中的轮廓,并使用RETR_EXTERNAL和CHAIN_APPROX_SIMPLE参数。
-
处理每个问题的选项:遍历每个问题所在的行,对该行中的五个选项进行处理。首先对选项进行排序,然后使用遍历的方式计算每个选项中非零像素的数量,根据数量确定是否选择了该选项,同时记录最多选择的选项和其索引。
-
计算得分:根据正确的选项数量和总题数计算得分,同时输出得分。
-
显示结果:在变换后的图像上绘制得分,并显示原始图像和处理结果。
-
等待用户按下任意键结束程序。
3,结果展示
高斯滤波后结果
边缘检测结果
图像轮廓检测结果:
透视变换结果:
二值化处理结果:
mask,只保留交集部分
遍历每一个结果,计算非零像素的数目,用于判断是否选择了这个选项
计算的得分并显示
相关文章:

OpenCV实现答题卡自动打分!
目录 1,主要原理以及函数介绍 全部代码,以 2 , 实现过程 3,结果展示 1,主要原理以及函数介绍 ap argparse.ArgumentParser() 创建一个ArgumentParser对象,并将其赋值给变量ap。这个对象可以接受我们的脚…...

Python编程必备:掌握列表遍历的6种神级技巧!
更多资料获取 📚 个人网站:涛哥聊Python 遍历列表是Python中最常见的任务之一,因为列表是一种非常常用的数据结构,它用于存储一组项目。 在编程中,经常需要对这些项目进行操作,例如查找特定元素ÿ…...

nodejs+vue校园失物招领平台
失物人可以在该平台中发布自己的拾物信息,本毕业设计题目将设计与实现一个基于校园的非商业行为的网上校园失物招领平台。并给出自己附加的各项条件, 失物招领管理系统主要分为两个部分,涉及前台和后台,然后由失主通过校园失物招…...

leetcode做题笔记171. Excel 表列序号
给你一个字符串 columnTitle ,表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 示例 1: 输入: columnTitle "A" 输出: 1示例 2: 输入: colu…...

SW曲面实体导出工程图
...

Docker的私有仓库部署——Harbor
Harbor 简介 一、什么是Harbor Harbor 是 VMware 公司开源的企业级 Docker Registry 项目, 其目标是帮助用户迅速搭建一个企业级的 Docker Registry 服务。 Harbor以 Docker 公司开源的 Registry 为基础, 提供了图形管理 UI 、基于角色的访问控制(Role…...

JavaScript反爬虫技巧详细攻略
在互联网时代,网站采取了各种手段来防止被爬虫抓取数据,其中最常见的就是JavaScript反爬虫技巧。本文将揭示一些常用的JavaScript反爬虫技巧,并提供一些实际操作建议,帮助您保护自己的爬虫免受检测和封禁。 1、为什么网站使用Java…...

C++基础入门学习笔记
问题1:什么是 C 中的多态?如何实现多态? 回答1:C 中的多态是指同一种类型的实体,可以在不同的情况下表现出不同的行为。实现多态的方式有两种:虚函数和模板函数。虚函数是在基类中声明为虚函数的函数&…...

手机cpu架构查看及armeabi、armeabi-v7a、arm64-v8a及x86等说明
一、如何查看cpu加购 winR,输入cmd 填下指令如下 adb shell getprop ro.product.cpu.abi 二、架构描述 1.armeabiv-v7a: 第7代及以上的 ARM 处理器。2011年15月以后的生产的大部分Android设备都使用它. 2.arm64-v8a: 第8代、64位ARM处理器,很少设备&a…...

node-sass报错,node16运行node14的项目
原来项目是node14的版本,现在用node16运行npm i 会报以下错误 node-sass4.14.1 postinstall: node scripts/build.js npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the node-sass4.14.1 postinstall script. npm ERR! This is probably not a problem with …...

在Linux中掌握不同的命令,让创建文件变得易如反掌
在Linux中创建一个新文件很简单,但也有一些令人惊讶和灵巧的技术。在本教程中,学习如何从Linux终端创建文件。 先决条件 访问命令行/终端窗口(Ctrl-Alt-F2或Ctrl-Alt-T) 具有sudo权限的用户帐户(对于某些文件/目录是可选的) 从命令行创建新的Linux文件 Linux的设计…...

iOS 14 YYAnimatedImageView加载图片失败处理
升级到iOS14,之前使用的YYimage框架全部不能正常显示图片,当然动态图正常显示,静态图无法显示; 原因是:14.0 系统调用了下面方法,YYAnimatedImageView没有正确处理 -(void)displayLayer:(CALayer )layer; 1 可以用以下…...

兴趣社如何搭建一个兴趣社区?
社交产品的本质是帮助用户提升社交的质量与效率,而兴趣则是找到本质的捷径。用户对兴趣社区的使用主要是围绕兴趣爱好,社交属性以及粉丝活动三个方向。对感兴趣的话题,用户天然有更强的分享讨论欲,更期待与人社交互动。“越垂直的…...

腾讯wifi码推广如何代理?方法详解!
腾讯wifi码推广是一种利用微信扫码连接商家wifi的方式,用户看完广告后就可以免费上网,而推广者则可以获得广告收益。 那么怎样代理腾讯wifi码推广呢? 答案是腾讯官方没有这个项目,那是怎么回事呢,腾讯wifi码正确的名称…...

linux下读取socket相关的系统调用总结
recv 函数原型 /* Read N bytes into BUF from socket FD.Returns the number read or -1 for errors.This function is a cancellation point and therefore not marked with__THROW. */ extern ssize_t recv (int __fd, void *__buf, size_t __n, int __flags); 所属文件…...

kafka生产者发送消息报错 Bootstrap broker localhost:9092 (id: -1 rack: null) disconnected
报这个错误是因为kafka里的配置要修改下 在config目录下 server.properties配置文件 这下发送消息就不会一直等待,就可以发送成功了...

内存文件初始化
要在内存中初始化一个SQLite数据库文件,可以使用SQLite提供的特殊URI格式进行连接。以下是一种常见的方法: #include <sqlite3.h>int main() {sqlite3* db;// 在内存中创建或打开数据库文件int ret sqlite3_open(":memory:", &db)…...

【`opencv_core` 和 `opencv_imgcodecs` 是 OpenCV(开源计算机视觉库)的两个主要模块】
opencv_core 和 opencv_imgcodecs 是 OpenCV(开源计算机视觉库)的两个主要模块。下面简要描述这两个模块的主要功能: opencv_core: 这是 OpenCV 的核心模块,为其他 OpenCV 模块提供了基础的数据结构和函数。它包含了基本的数据类型…...

系统架构师备考倒计时24天(每日知识点)
模块独立性的度量 聚合:衡量模块内部各元素结合的紧密程度偶然聚合:模块完成的动作之间没有任何关系,或者仅仅是一种非常松散的关系。逻辑聚合:模块内部的各个组成在逻辑上具有相似的处理动作,但功能用途上彼此无关。…...

多无人机编队集群飞行
matlab2016b可直接运行 多无人机集群编队飞行(8架无人机)资源-CSDN文库...

【C++杂货铺】一文带你走进哈希:哈希冲突 | 哈希函数 | 闭散列 | 开散列
文章目录 一、unordered 系列关联式容器二、unordered_map1.1 unordered_map 介绍1.2 unordered_map 的接口说明1.2.1 unordered_map 的构造1.2.2 unordered_map 的容量1.2.3 unordered_map 的迭代器1.2.4 unordered_map 的元素访问1.2.5 unordered_map 的查询1.2.6 unordered_…...

docker 搭建本地Chat GPT
要在CentOS7上安装Docker,您可以按照以下步骤进行操作: 1、更新系统包列表 sudo yum update2、安装Docker存储库的必要软件包 sudo yum install -y yum-utils device-mapper-persistent-data lvm23、添加Docker存储库 sudo yum-config-manager --add…...

电脑怎么剪辑视频?高手分享的独家秘诀
视频剪辑是一项有趣而具有创造性的活动,可以帮助您将录制的视频片段转化为有趣、有启发性的作品。无论您是想创建家庭影片、Vlog视频、教程,还是其他任何类型的视频,掌握视频剪辑技巧都是必要的。那电脑怎么剪辑视频呢?在本篇文章…...

LCR 171.训练计划 V
题目来源: leetcode题目,网址:LCR 171. 训练计划 V - 力扣(LeetCode) 解题思路: 双指针。node1 指向headA,node2 指向headB,将两节点每次移动一个节点直至两指针指向同一节点或…...

CH6-中断和异常处理
6.1 中断和异常处理概述 中断和异常概述(INTERRUPT AND EXCEPTION OVERVIEW) 中断和异常向量:中断和异常在处理器中都有对应的编号,被称为向量。当中断或异常发生时,处理器会根据向量找到相应的中断处理程序或异常处理…...

Hive的文件合并
背景:Flink数据写入到stage层,然后再入ods层,中间导致hive数据实时性不强,随后做优化,Flink之间以orc格式写入到hive 问题:单表日800亿数据量,产生过多的小文件,影响Impala查询 解决:对hive小文件进行合并, ALTER TABLE lt_ipsy_xdr_temp PARTITION (day20230829, hour9,type…...

Mac删除不在程序坞的程序
现象描述:删除某个程序时(通过‘程序’列表中将该应用移动到废纸篓里),该应用程序正在运行中,删除过程该程序未提示正在运行中,仅仅删除了图标(在此吐槽下该程序的交互,产品没有考虑…...

c#删除数组中符合条件的元素
错误写法 List<int> list new List<int>() { 1, 2, 3, 4 }; for (int i 0; i < sz; i){if (i 1 || i 2)list.RemoveAt(i);}在迭代过程中删除数组元素会有很大的问题,例如删除后位置移动,导致不该被删除的元素被删除。还可能导致数组…...

sqoop 脚本密码管理
1:背景 生产上很多sqoop脚本的密码都是铭文,很不安全,找了一些帖子,自己尝试了下,记录下细节,使用的方式是将密码存在hdfs上然后在脚本里用别名来替代。 2:正文 第一步:创建密码对…...

微信小程序使用CryptoJS加密PassWord(MD5)
微信小程序使用CryptoJS加密PassWord(MD5) 背景及环境: 微信小程序登录页面,需要加密登录密码发送给后端,使用 MD5 来加密密码 开发工具:微信开发者工具 npm安装CryptoJS 查看有哪些crypto的包 npm search crypto 找到自己需要的包…...