【opencv】答题卡判分实验

实验环境: 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_tempanswer_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 = 0choose = 0temp_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 = totalchoose = j# 假定答案为全Aif 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.hqwc.cn/news/705481.html

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

相关文章

基于BF算法和KMP算法的病毒感染检测

案例引入&#xff1a; 实验任务&#xff1a; 代码实现&#xff1a; 注意&#xff1a;我以项目的形式编写此代码&#xff0c;编译环境为VS2022&#xff0c;代码移植可能会导致错误 mystring.h&#xff1a;存放结构体以及函数声明 #define _CRT_SECURE_NO_WARNINGS #pragma on…

2024中国应急(消防)品牌巡展西安站成功召开!惊喜不断

消防品牌巡展西安站 5月10日&#xff0c;由中国安全产业协会指导&#xff0c;中国安全产业协会应急创新分会、应急救援产业网联合主办&#xff0c;陕西消防协会协办的“一切为了安全”2024年中国应急(消防)品牌巡展-西安站成功举办。该巡展旨在展示中国应急&#xff08;消防&am…

Linux服务器lvm磁盘管理fdisk和df磁盘大小不同修改

服务器端由于硬盘是通过VCenter原来100G磁盘复制的虚拟机,复制完成后,原来100G的磁盘通过选择 磁盘重新复制出150G的磁盘,开机后发现还是原来的100G的磁盘,通过fdisk -l 查看有个sdb是150G, 但是已经划转的lvm盘只有100G, 通过df查看也是原来的100G: pvs查看pv里也是10…

服务器之间实现免密码传输文件(scp免密传输)

问题&#xff1a;需要定时将本服务器的文件传输到指定服务器上作为备份 通过scp实现不同服务器之间的文件传输 正常使用scp传输文件 传输文件命令&#xff1a;scp /data/文件 root服务器地址&#xff1a;/指定目录 传输文件夹命令&#xff1a;scp -r /data/文件 root服务…

ZYNQ专题-在自己的板子上移植U-boot无法联网问题

背景 在开发自己项目的时候&#xff0c;需要针对自己的板子进行相关的移植工作。在完成初步的U-boot移植后&#xff0c;发现U-boot无法ping通host主机。 移植方法是参考的正点原子教程。会不会是因为硬件不一致导致的&#xff1f;如前面文中提到的&#xff0c;板载使用的是KSZ…

pytest自动化测试框架tep环境变量、fixtures、用例三者之间的关系

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号&#xff1a;互联网杂货铺&#xff0c;回复1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 tep是一款测试工具&#xff0c;在pytest测试框架基础上集…

GPT-4o 引领人机交互新风向,向量数据库赛道沸腾了

OpenAI 发布 ChatGPT-4o&#xff0c;意味着人机交互进入新的时代。Chat-GPT4o 是一个跨文本、视觉和音频端到端训练的新模型&#xff0c;所有输入和输出都由同一个神经网络处理。这也在告诉所有人&#xff0c;GenAI 连接非结构化数据&#xff0c;非结构化数据之间跨模态的交互正…

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销 《https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-mechanisms.pdf》 Measuring the cost of a context switch is a little trickier. The lmbench benchmark does so by running two processes on a single CPU…

[Vs2019报错找不到服务实例的解决方案]

计算机疑难杂症记录与分享004 Vs2019报错找不到服务实例的解决方案1、问题现象1.1、问题一&#xff1a;打开vs2019打开项目工程文件&#xff0c;直接弹窗报错1.2、问题二&#xff1a;能打开工程了。识别项目文件不兼容问题。 2、问题原因3、问题一的解决3.1、方法一(亲测无效)3…

Java 自然排序和比较器排序区别?Comparable接口和Comparator比较器区别?

注&#xff1a;如果你对排序不理解&#xff0c;请您耐心看完&#xff0c;你一定会明白的。文章通俗易懂。建议用idea运行一下案例。 1&#xff09;自然排序和比较器排序的区别&#xff1f; 自然排序是对象本身定义的排序规则&#xff0c;由对象实现 Comparable 接口&#xff…

Linux 第三十四章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

基础之音视频2

01 前言 02 mp 03 mp实例 简易音乐播放器 04 音频 sound-pool 1.作用 播放多个音频&#xff0c;短促音频 2.过程 加载load- 3.示例 模拟手机选铃声 步骤&#xff1a; 创建SoundPool对象&#xff0c;设置相关属性 音频流存入hashmap 播放音频 05 videoview 3gp 体积小 mp4 …