【opencv】答题卡判分实验

news2024/9/20 18:32:38

实验环境: anaconda、jupyter notebook

实验用的包:numpy、matplotlib、opencv

实验的目的还是以熟悉图像的透视变换、轮廓特征提取为主要目的

关于如何判断答题卡被选项:通过几个覆盖备选项的掩膜与原二值图像想与,最终整个图像中白色像素点多的就是被选择的项

根据我的亲身体验cv2.bitwise_and(src1,src2)会把src1中与src2不同的点置为0!!!我排查了一下午,可恶啊!!!!

一、实验使用的图像

实验图片

一、引入包

import cv2
import numpy as np
import matplotlib.pyplot as plt

二、读入图像预处理为二值图片

answer_sheet = cv2.imread('answer_sheet.png')
# 灰度图
answer_sheet_gray = cv2.cvtColor(answer_sheet, cv2.COLOR_BGR2GRAY)

# 二值图
answer_sheet_bin = cv2.threshold(answer_sheet_gray, 127,255,cv2.THRESH_BINARY)[1]
answer_sheet_bin_inv = cv2.threshold(answer_sheet_gray, 127,255,cv2.THRESH_BINARY_INV)[1]

plt.imshow(answer_sheet_bin, 'gray')
plt.show()

二值图

三、获取答题卡轮廓

# 获取轮廓
binary, answer_sheet_contours, hierarchy = cv2.findContours(answer_sheet_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

answer_sheet_contour = None
answer_sheet_contour_length = 0
for c in answer_sheet_contours:
    # 做近似
    epsilon = 0.1 * cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, epsilon, True)
    # 求周长
    answer_sheet_contour_length_temp = cv2.arcLength(approx,True)
    # 找周长最大的轮廓
    if answer_sheet_contour_length_temp > answer_sheet_contour_length:
        answer_sheet_contour_length = answer_sheet_contour_length_temp
        answer_sheet_contour = approx

# 展示轮廓
answer_sheet_temp = answer_sheet.copy()
res = cv2.drawContours(answer_sheet_temp, [answer_sheet_contour], -1, (0,0,255),3)

plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()

轮廓展示

四、构建透视变换的两个矩阵

# 取到四个点
answer_sheet_contour_deal = np.float32(answer_sheet_contour[:,0,:])
A,B,C,D = answer_sheet_contour_deal 


# 在原始图像上画轮廓
answer_sheet_temp = answer_sheet.copy()
answer_sheet_contour_deal_temp = np.array([[np.int32(A)],[np.int32(B)],[np.int32(C)],[np.int32(D)]])
cv2.drawContours(answer_sheet_temp, [answer_sheet_contour_deal_temp], -1, (0,255,0),10)
plt.imshow(cv2.cvtColor(answer_sheet_temp, cv2.COLOR_BGR2RGB))
plt.show()

W1 = np.sqrt((A[0] - B[0]) ** 2 + (A[1] -B[1]) ** 2)
W2 = np.sqrt((C[0] -D[0]) ** 2 + (C[1] -D[1]) ** 2)
W = max(int(W1), int(W2))

H1 = np.sqrt((A[0] - C[0]) ** 2 + (A[1] -C[1]) ** 2)
H2 = np.sqrt((B[0] -D[0]) ** 2 + (B[1] -D[1]) ** 2)
H = max(int(H1), int(H2))

# 目标坐标
dest = np.array([
    [0,0],
    [0,H],
    [W,H],
    [W,0]
], dtype=np.float32)


# 在原始图像上画轮廓
answer_sheet_temp = answer_sheet.copy()
answer_sheet_contour_deal_temp = np.array([[np.int32(dest[0])],[np.int32(dest[1])],[np.int32(dest[2])],[np.int32(dest[3])]])
cv2.drawContours(answer_sheet_temp, [answer_sheet_contour_deal_temp], -1, (0,255,0),10)
plt.imshow(cv2.cvtColor(answer_sheet_temp, cv2.COLOR_BGR2RGB))
plt.show()

透视变换矩阵

五、透视变换

# 透视变换
M = cv2.getPerspectiveTransform(answer_sheet_contour_deal, dest)
answer_sheet_warped = cv2.warpPerspective(answer_sheet_gray, M, (int(W),int(H)))

# 转为二值图
answer_sheet_warped_bin = cv2.threshold(answer_sheet_warped, 0, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)[1]

plt.imshow(answer_sheet_warped_bin, cmap='gray')
plt.show()

透视变换

六、获取每个答案的轮廓

# 获取每个选项的外轮廓
cnts = cv2.findContours(answer_sheet_warped_bin.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

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:
        yt = np.sum(c[:,:,1])
        xt = np.sum(c[:,:,0])
        questionCnts.append(c)

# 行排序
for i in range(len(questionCnts) - 1):
    for j in range(len(questionCnts) - 1 - i):
        if questionCnts[j][0][0][1] > questionCnts[j + 1][0][0][1]:
            questionCnts[j],questionCnts[j + 1] = questionCnts[j + 1],questionCnts[j]
# 列排序
for i in range(5):
    temp_array = questionCnts[i * 5 : (i + 1) * 5]
    for j in range(len(temp_array) - 1):
        for k in range(len(temp_array) - 1 - j):
            if temp_array[k][0][0][0] > temp_array[k + 1][0][0][0]:
                temp_array[k],temp_array[k + 1] = temp_array[k + 1],temp_array[k]
    questionCnts[i * 5 : (i + 1) * 5] = temp_array

# 展示排序结果
answer_sheet_warped_bin_temp = answer_sheet_warped_bin.copy()
for c in questionCnts:
    cv2.drawContours(answer_sheet_warped_bin_temp, np.array([c]),-1,(0,255,0),3)
    plt.imshow(answer_sheet_warped_bin_temp, cmap='gray')
    plt.show()

可以看到排序后按从左到右,从上到下的顺序排列轮廓

排序

七、统计分数

sum = 0
for i in range(5):
    max_count = 0
    choose = 0
    temp_array = questionCnts[i * 5 : (i + 1) * 5]
    for (j,c) in enumerate(temp_array):
        #掩码图
        # 全黑
        mask = np.ones(answer_sheet_warped_bin.shape, dtype='uint8')
        # 在全黑图上画出白色圈
        cv2.drawContours(mask,[c], -1, 255, -1)
        # plt.imshow(mask, cmap='gray')
        # plt.show()

        t = cv2.bitwise_and(answer_sheet_warped_bin,mask)
        # 去除t里的杂色点
        for x in range(len(t)):
            for y in range(len(t[i])):
                if t[x][y] == 1:
                    t[x][y] = 0

        # 非0像素点最多的就是所选项
        total = cv2.countNonZero(t)
        if total > max_count:
            max_count = total
            choose = j
    # 假定答案为全A
    if choose == 0:
        sum += 20
answer_sheet_temp = answer_sheet_warped.copy()
cv2.putText(answer_sheet_temp, "{}%".format(sum), (0, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0,0,255),2)
plt.imshow(answer_sheet_temp, 'gray')
plt.show()

统计分数

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

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

相关文章

Springboot+MybatisPlus如何实现带验证码的登录功能

实现带验证码的登录功能由两部分组成&#xff1a;&#xff1a;1、验证码的获取 2、登录&#xff08;进行用户名、密码和验证码的判断&#xff09; 获取验证码 获取验证码需要使用HuTool中的CaptchaUtil.createLineCaptcha()来定义验证码的长度、宽度、验证码位数以及干扰线…

性能测试工具—jmeter的基础使用

1.Jmeter三个重要组件 1.1线程组的介绍&#xff1a; 特点&#xff1a; 模拟用户&#xff0c;支持多用户操作多个线程组可以串行执行&#xff0c;也可以并行执行 线程组的分类&#xff1a; setup线程组&#xff1a;前置处理&#xff0c;初始化普通线程组&#xff1a;编写…

遥感数据集制作(Potsdam数据集为例):TIF图像转JPG,TIF标签转PNG,图像重叠裁剪

文章目录 TIF图像转JPGTIF标签转PNG图像重叠裁剪图像重命名数据集转COCO格式数据集转VOC格式 遥感图像不同于一般的自然图像&#xff0c;由于波段数量、图像位深度等原因&#xff0c;TIF图像数据不能使用简单的格式转换方法。本文以Potsdam数据集为例&#xff0c;制作能够直接用…

你了解 pom.xml 吗

你了解pomxml吗 springboot 是 java 利器&#xff0c;几乎每个写 java 的同学都会用&#xff0c;但是你了解 pom.xml 吗&#xff1f; 这篇干货查漏补缺。 首先我们创建个 springboot 项目 都选了默认设置&#xff1a; 我把这篇完整粘贴出来 pom.xml <?xml version&quo…

网络安全|隐藏IP地址的5种不同方法

隐藏计算机的IP地址在互联网在线活动种可以保护个人隐私&#xff0c;这是在线活动的一种常见做法&#xff0c;包括隐私问题、安全性和访问限制内容等场景。那么如何做到呢?有很5种方法分享。每种方法都有自己的优点和缺点。 1. 虚拟网络 当您连接到虚拟服务器时&#xff0c;您…

JVM内存模型最新面试题(持续更新)

问题&#xff1a;java中创建的对象一般放在哪里&#xff1f;(全流程包含从创建到回收) 回答 大部分对象在堆中&#xff0c;这个基本都知道&#xff1b; 少部分对象是会在栈中的&#xff0c;比如作用域不局限于方法内的方法内部变量&#xff0c;这类对象的特征一般就是生命周期…

第33次CSP认证Q3:化学方程式配平

&#x1f344;题目描述 为了配平一个化学方程式&#xff0c;我们可以令方程式中各物质的系数为未知数&#xff0c;然后针对涉及的每一种元素&#xff0c;列出关于系数的方程&#xff0c;形成一个齐次线性方程组。然后求解这个方程组&#xff0c;得到各物质的系数。这样&#x…

【C语言】通讯录系统实现

目录 1、通讯录系统介绍 2、代码分装 3、代码实现步骤 3.1制作菜单函数以及游戏运行逻辑流程 3.2、封装人的信息PeoInfo以及通讯录Contact结构体类型 3.3、初始化通讯录InitContact函数 3.4、增加联系人AddContact函数 3.5、显示所有联系人ShowContact函数 3.6、删除联系人D…

视频号小店是个风口吗?今年去做是明智的选择吗?一篇详解!

大家好&#xff0c;我是电商小V 视频号才刚刚推出一年半的时间&#xff0c;可以说自从推出之后这个项目的知名度一直是处于飙升的状态&#xff0c;一直处于爆火的状态&#xff0c;也是吸引了很多想做电商&#xff0c;想去创业的小伙伴&#xff0c;最主要的就是视频号小店背靠的…

流程图用什么软件做?选择这4款,让工作事半功倍

流程图用什么软件做&#xff1f;流程图是一种直观、清晰的图形表示法&#xff0c;它通过节点、箭头和符号等元素&#xff0c;精确地展示出一系列步骤、决策点以及操作过程。这种图形化的呈现方式不仅简化了复杂信息的处理&#xff0c;还使得读者能够迅速把握整体流程&#xff0…

C++容器:list(双向链表)

一丶list介绍 C中的list容器底层确实是以双向链表的形式实现的。 list容器是C标准模板库&#xff08;STL&#xff09;中的一部分&#xff0c;它提供了对列表数据结构的实现。 双向链表结构&#xff1a;list容器的每个元素都是通过指针链接在一起的&#xff0c;每个元素都包含…

C++11 新特性 常量表达式 constexpr

为了解决常量无法确定的问题&#xff0c;C11在新标准中提出了关键字constexpr&#xff0c;它能够有效地定义常量表达式&#xff0c;并且达到类型安全、可移植、方便库和嵌入式系统开发的目的。 一、常量的不确定性 在C11标准以前&#xff0c;我们没有一种方法能够有效地要求一…

为什么Python中会有集合set类型?

知乎上有人提问&#xff0c;为什么Python有了列表list、元组tuple、字典dict这样的容器后&#xff0c;还要弄个集合set&#xff1f; 确实set和list、tuple、dict一样&#xff0c;都是python的主要数据类型&#xff0c;它们的作用是不同的。 因为set是数学意义上的集合&#xf…

战网国际服加速器用哪个好 暴雪战网好用加速器介绍

战网国际版&#xff0c;又称Battle.net环球版&#xff0c;是暴雪娱乐操盘的全球性游戏互动平台&#xff0c;它跨越地理界限&#xff0c;服务于全球游戏爱好者。与地区限定版本相异&#xff0c;国际版赋予玩家自由进入暴雪旗下众多经典游戏的权利&#xff0c;无论身处何方&#…

使用C++实时读取串口数据(window使用已编译LibModbus库并用QT实现一个实时读取串口数据)

Modbus协议简介&#xff1a; 在工业自动化领域&#xff0c;Modbus协议是一个应用层通讯协议&#xff0c;它为电子设备之间的通信提供了一个常用的标准方法。Modbus协议最初由Modicon公司&#xff08;现为施耐德电气的一部分&#xff09;在1979年发明用于其可编程逻辑控制器&…

AIGC行业现在适合进入吗

AIGC行业目前正处于快速发展阶段,市场需求正处于爆发期,上大学网&#xff08;www.sdaxue.com&#xff09;认为&#xff0c;对于有兴趣的个人或企业而言&#xff0c;现在可能是一个适合进入的时机&#xff0c;以下是具体的分析&#xff0c;供大家参考&#xff01; 一、AIGC行业前…

java医院信息系统HIS源码SaaS模式Java版云HIS系统 接口技术RESTful API + WebSocket + WebService

java医院信息系统HIS源码SaaS模式Java版云HIS系统 接口技术RESTful API WebSocket WebService 云HIS是基于云计算的医疗卫生信息系统&#xff08;Cloud-Based Healthcare Information System&#xff09;&#xff0c;它运用云计算、大数据、物联网等新兴信息技术&#xff0c;…

熬了快两个月,终于拿到了淘天后端offer!

今年的暑期实习挺难找的&#xff0c;很多同学忙了几个月到现在还没有一个offer&#xff0c;真的很常见&#xff01;没找到暑期实习的同学千万不要太焦虑&#xff0c;可以留意留意日常实习&#xff0c;日常实习也找不到&#xff0c;那就去完善自己的项目经历&#xff0c;认真准备…

软件工程期末复习(8)需求的表达方法和状态转换图

需求的表达方法 系统模型 需求分析的任务就是借助于当前系统的逻辑模型导出目标系统的逻辑模型&#xff0c;解决目标系统 “做什么” 的问题 通常软件开发项目是要实现目标系统的物理模型。目标系统的具体物理模型是由它的逻辑模型经实例化&#xff0c;即具体到某个业务领域而…

苹果电脑里面的资料为什么不能拷贝到硬盘 mac硬盘权限限制怎么解决 mac东西拷不进硬盘怎么办

你在使用Mac电脑的时候有没有遇到过文件无法拷贝的情况呢&#xff1f;这种情况多见于Mac电脑使用U盘或者移动硬盘的时候&#xff0c;不少用户都发现&#xff1a;可以正常读取U盘里的数据但是无法拷贝文件进去&#xff0c;为什么会有这种情况呢&#xff1f; 一、mac东西拷不进硬…