OpenCV+Python自动填涂机读卡

news2024/9/26 5:16:36

接上一篇OpenCV+Python识别机读卡-CSDN博客,既然可以识别机读卡填涂答案了,将标准答案绘制到机读卡上也就简单了。

工作原理

1.答题区域为整张图片最大轮廓,先找出答题区域。

2.答题区域分为6行,每行4组,第6行只有1组,我们暂不处理第6行,只处理前面5行。

3.给定每一行第一个选项中心点坐标,该行其余选项的中心点坐标可以推算出来。

4.有了每个选项的中心点坐标,结合标准答案,就可以将答案绘制到机读卡上了。

实现步骤

1.空白机读卡长这样:

2.标准答案(内容仅供参考,以实际为准)

answers = {1: "B", 2: "B", 3: "A", 4: "B", 5: "B", 6: "A", 7: "B", 8: "B", 9: "A", 10: "B", 11: "B", 12: "A", 13: "A", 14: "B", 15: "B", 16: "A", 17: "A", 18: "B", 19: "B", 20: "A", 21: "C", 22: "C", 23: "B", 24: "D", 25: "C", 26: "D", 27: "A", 28: "D", 29: "C", 30: "B", 31: "C", 32: "D", 33: "B", 34: "A", 35: "D", 36: "C", 37: "B", 38: "B", 39: "D", 40: "D", 41: "B",42: "C", 43: "B", 44: "D", 45: "B", 46: "A", 47: "D", 48: "D", 49: "C", 50: "D", 51: "B", 52: "D", 53: "A", 54: "A", 55: "D", 56: "D", 57: "C", 58: "D", 59: "B", 60: "D", 61: "A", 62: "B", 63: "D", 64: "B", 65: "C", 66: "D", 67: "B", 68: "C", 69: "D", 70: "A", 71: "ABCD", 72: "ABCD", 73: "AC", 74: "BCD", 75: "BC", 76: "BD", 77: "ABCD", 78: "ABCD", 79: "ACD", 80: "ABD"}

有单选题和多选题。需要实现将答案绘制到空白机读卡上。

直接贴源码,详细说明请参考代码注释。

import cv2

# 1.读取图片并缩放
orginImg = cv2.imread("01.jpg")
size = ((int)(650*1.8), (int)(930*1.8))  # 尽可能将图片弄大一点,下面好处理
img = cv2.resize(orginImg, size)

# 显示图像
def imshow(name, image):
    scale_percent = 50  # 缩放比例
    width = int(image.shape[1] * scale_percent / 100)
    height = int(image.shape[0] * scale_percent / 100)
    dim = (width, height)
    resized_image = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    cv2.imshow(name, resized_image)

imshow("1.orgin", img)

# 2.转灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imshow("2.gray", gray)

# 3.黑帽运算:移除干扰项
cvblackhat = cv2.morphologyEx(
    gray, cv2.MORPH_BLACKHAT, cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)))
imshow("3.black", cvblackhat)

# 4.二值化突出轮廓,自动阈值范围 cv2.THRESH_BINARY|cv2.THRESH_OTSU
thresh = cv2.threshold(
    cvblackhat, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
imshow("4.thresh", thresh)

# 5.提取轮廓,并在图上标记轮廓
cnts, hierarchy = cv2.findContours(
    thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
mark = img.copy()
cv2.drawContours(mark, cnts, -1, (0, 0, 255), 2)
imshow("5.contours", mark)

# 6.提取我们感兴趣的部分(这里我们只需要答题部分) img[y:y+h, x:x+w]
_top = 0
_left = 0
roi = None
for (i, c) in enumerate(cnts):
    (x, y, w, h) = cv2.boundingRect(c)
    ar = w/float(h)
    if w > 500 and h > 500 and ar > 0.9 and ar < 1.1:
        roi = img[y:y+h, x:x+w]
        _top = y
        _left = x
        break
imshow("5.roi", roi)

# 7.查找每个选项的中心点坐标
# 思路:
# 通过分析:
# 1.答题区域分为6行,每行4组,第6行只有1组,我们暂不处理第6行,只处理前面5行。
# 2.只要给定每一行第一个选项中心坐标,该行其余选项的中心坐标可以推算出来。
# 3.通过找到每个选项中心点坐标,再加上选项宽高,就可以在答题区域绘出每个选项的范围。
# 4.通过计算每个选项范围图像里非0像素点个数,结合阈值判断该选项是否选中。
# 5.结合题目个数,遍历每个选项,构造出最终答案。
item = [34, 20]  # 每个选项宽度(跟图形缩放有关系)
x_step = 44  # x方向行距(两个选项水平方向距离)
y_step = 28  # y方向行距(两个选项垂直方向距离)
blank = 92  # 每组间距(5个一组)水平方向距离
centers = []  # 每个选项的中心点坐标,用来框选选项
# 答题区域有5行多1组,这里只处理前面5行,最后一组暂不处理
startPonits = [(25, 44), (25, 216), (26, 392), (26, 566), (28, 744)]
for (i, p) in enumerate(startPonits):
    temp = []  # 暂存该组选项坐标
    start = list(p)  # 该行起始点坐标
    for g in range(0, 4, 1):  # 每行有4组
        if len(temp) > 0:
            startx = temp[len(temp)-1][0] + blank  # 最后一个选项的x坐标+每组间距
        else:
            startx = start[0]
        start[0] = startx
        for i in range(start[0], start[0]+5*x_step, x_step):  # 水平5个选项
            for j in range(start[1], start[1]+4*y_step, y_step):  # 垂直4个选项
                temp.append((i, j))
    for (i, c) in enumerate(temp):
        centers.append(c)

questions = []  # 二维数组:保存每个题目ABCD4个选项对应的中心点坐标
group = []  # 将点分组,每4个1组,对应每题的4个选项
for (i, (x, y)) in enumerate(centers):
    group.append((x, y))
    if (i+1) % 4 == 0:
        questions.append(group)
        group = []
def find_key_by_value(dictionary, value):
    for key, val in dictionary.items():
        if val == value:
            return key
    return None

# 标准答案
answers = {1: "B", 2: "B", 3: "A", 4: "B", 5: "B", 6: "A", 7: "B", 8: "B", 9: "A", 10: "B", 11: "B", 12: "A", 13: "A", 14: "B", 15: "B", 16: "A", 17: "A", 18: "B", 19: "B", 20: "A", 21: "C", 22: "C", 23: "B", 24: "D", 25: "C", 26: "D", 27: "A", 28: "D", 29: "C", 30: "B", 31: "C", 32: "D", 33: "B", 34: "A", 35: "D", 36: "C", 37: "B", 38: "B", 39: "D", 40: "D", 41: "B",
           42: "C", 43: "B", 44: "D", 45: "B", 46: "A", 47: "D", 48: "D", 49: "C", 50: "D", 51: "B", 52: "D", 53: "A", 54: "A", 55: "D", 56: "D", 57: "C", 58: "D", 59: "B", 60: "D", 61: "A", 62: "B", 63: "D", 64: "B", 65: "C", 66: "D", 67: "B", 68: "C", 69: "D", 70: "A", 71: "ABCD", 72: "ABCD", 73: "AC", 74: "BCD", 75: "BC", 76: "BD", 77: "ABCD", 78: "ABCD", 79: "ACD", 80: "ABD"}

# 8.将答案绘制到答题卡上
option = {0: "A", 1: "B", 2: "C", 3: "D"}
show = img.copy()
for (i, q) in enumerate(questions):
    if i < len(answers):
        an = answers.get(i+1)  # 根据题目号获取答案
        if an is None:
            continue
        characters = [char for char in an]  # 将答案拆分成字符列表,适配多选题
        for char in characters:
            index = find_key_by_value(option, char)  # 根据答案查找对应选项的索引,标记对应选项
            for (j, (x, y)) in enumerate(q):
                if j == index:
                    left = _left + x-(int)(item[0]/2)  # 中心点坐标-1/2*选项宽度+左侧距离
                    top = _top + y-(int)(item[1]/2)  # 中心点坐标-1/2*选项宽度+顶部距离
                    # 绘制黑块遮挡选项
                    cv2.rectangle(show, (left, top), (left +
                                  item[0], top + item[1]), (0, 0, 0), -1)
imshow("5.show", show)

cv2.waitKey(0)
cv2.destroyAllWindows()

运行效果

存在的缺陷:

1.这里每行起始点坐标和每个选项宽高,都是写的固定值(手动量出来的)。

2.不同机读卡需调整对应参数。

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

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

相关文章

【Java设计模式】抽象文档模式:以灵活性简化数据处理

文章目录 抽象文档设计模式的意图抽象文档模式的详细解释及实际示例Java中抽象文档模式的编程示例抽象文档模式类图Java中何时使用抽象文档模式抽象文档模式的优点和权衡源码下载参考和致谢 抽象文档设计模式的意图 Java中的抽象文档设计模式是一种关键的结构设计模式&#xf…

【mysql集群之组复制】

目录 一、 mysql高可用之组复制 (MGR)组复制单主和多主模式实现mysql的组复制 二、 mysql-router&#xff08;mysql路由&#xff09;实现负载均衡 一、 mysql高可用之组复制 (MGR) MySQL Group Replication(简称 MGR )是 MySQL 官方于 2016 年 12 月推出的一个全新的高可用与高…

OpenHarmony南向开发:SmartPerf-Device使用说明

简介 SmartPerf 端是一款基于 OpenHarmony 系统开发的性能功耗测试工具&#xff0c;操作简单易用&#xff0c;可提供包括性能、功耗的关键 KPI 指标&#xff0c;给出具体指标的测试值&#xff0c;包括采集设备的 FPS、CPU、GPU、Ftrace 等指标数据&#xff1b; 目前 SmartPer…

uniapp之app版本更新,整体更新和热更新

目录 需求&#xff1a; 版本更新有两种更新模式&#xff1a; 实现&#xff1a; 前提&#xff1a; 热更新&#xff1a; 打包wgt包&#xff1a;菜单->发行->原生App-制作移动App资源升级包 代码逻辑: 整体更新&#xff1a; 实际项目开发&#xff1a; 需求&#xf…

Linux网络编程——C/C++Web服务器(二):IO多路复用select/poll/epoll实现服务器监听多客户端事件

环境配置&#xff1a;windows电脑用户可以安装WSL配置Linux环境&#xff0c;并且安装vscode及wsl的插件通过vscode连接本机电脑的Linux。 前置内容&#xff1a; Linux网络编程——C/CWeb服务器&#xff08;一&#xff09;&#xff1a;不断创建新线程处理多客户端连接和通信-C…

代码随想录算法训练营第二十七天(贪心 一)

硬拖拖到现在才写完。。。 关于贪心: 文章链接: 代码随想录 文章摘要: 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 贪心算法并没有固定的套路。 和其他算法不同&#xff0c;贪心没有能看出局部最优是否能推出整体最优的通法。 用来验证可不可以…

软件渗透测试必要性简析,第三方软件测试机构如何进行渗透测试?

在信息技术迅速发展的今天&#xff0c;软件渗透测试逐渐成为了确保信息安全的重要环节。软件渗透测试指的是对系统或应用程序进行模拟攻击&#xff0c;以发现其潜在的安全风险与脆弱性。不同于传统的安全审计&#xff0c;渗透测试更注重实际攻击过程和攻击者的视角&#xff0c;…

IO进程线程8月26ri

1&#xff0c;思维导图 2&#xff0c;用两个进程分别复制文件的上下两部分到另一个文件 #include<myhead.h> int main(int argc, const char *argv[]) {int fpopen("./1.txt",O_RDONLY);if(fp-1){perror("open");return -1;}int countlseek(fp,0,SE…

如何在 mind+ 中编写 python 程序

打开Mind&#xff0c;点击窗口右上角的【Python模式】按钮&#xff0c;由实时模式切换到Python模式。 将默认的循环执行模块拖动到左边的模块区删除。 点击【变量】&#xff0c;将【打印【Hello World】】模块拼接到【Python主程序开始】下方。 将【获取输入&#xff0c;提示语…

redis(未授权访问漏洞)

环境准备 下载并安装Redis 首先&#xff0c;下载Redis的源代码包并解压&#xff1a; wget http://download.redis.io/releases/redis-2.8.17.tar.gz tar xzf redis-2.8.17.tar.gz cd redis-2.8.17接着&#xff0c;编译安装Redis&#xff1a; 编译完成后&#xff0c;进入src目录…

自动化任务工具 | zTasker v1.97.1 绿色版

在自动化任务管理领域&#xff0c;一款名为zTasker的软件以其卓越的性能和易用性脱颖而出。今天&#xff0c;电脑天空将为大家详细介绍这款软件的亮点和使用场景。 功能特点 1. 轻量级设计&#xff0c;快速启动 zTasker以其小巧的体积和快速的启动速度&#xff0c;为用户提供…

模型 7S分析法(麦肯锡)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。组织全面诊断&#xff0c;战略协同优化。 1 7S分析法(麦肯锡)的应用 1.1 邮储银行的转型&#xff1a;基于麦肯锡7S模型的竞争力提升 中国邮储银行面临着激烈的金融行业竞争&#xff0c;为了迅速提升…

考研数学 高等数学----导数应用

核心框架 前置知识 正式内容 知识点1: 知识点2: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知识点: 知…

嵌入式系统课后习题(带答案)

资料截图&#xff08;部分&#xff09;&#xff1a; &#x1f680; 获取更多详细资料可点击链接进群领取&#xff0c;谢谢支持&#x1f447; 点击免费领取更多资料

Ant Design Vue中Modal.confirm无法自动关闭

温馨tips:着急看解决方法可跳过碎碎念~ 前两天经理扔给我一个问题&#xff1a;“这个弹窗怎么关不上了&#xff1f;” 我怀着无所谓的心态&#xff1a;小意思啦&#xff0c;5分钟之内解决完~ …当然flag是不能随便乱立的 拉下来项目&#xff08;原神启动&#xff08;不是&…

@ohos.systemParameterEnhance系统参数接口调用:获取系统属性

在去年的文章&#xff0c;笔者介绍了如何使用修改修改OpenHarmony 设备厂家名称 、硬件版本号 等系统属性&#xff0c;本文介绍一下在应用层怎么获取系统属性。 开发环境 DAYU200 rk3568开发板OpenHarmony 4.1r API 10 (full sdk)DevEco Studio 4.1 Release 开发步骤 1.首先…

浅谈【数据结构】树与二叉树之哈夫曼树

目录 1、哈夫曼树 1.1哈夫曼编码 1.2哈夫曼树 1.3构建一棵哈夫曼树 谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注 没错&#xff0c;说的就是你&#xff0c;不用再怀疑&#xff01;&#xff01;&#xff01; 希望我的文章内容能对你有帮助&#xff0c;一起努力吧…

【FPGA数字信号处理】什么是信号?

在数字信号处理的奇妙世界里&#xff01;“信号”是一切的基础&#xff0c;理解了信号&#xff0c;就相当于拿到了开启数字信号处理大门的钥匙。 今天&#xff0c;就让我们一起深入探究数字信号处理基础中的“信号”。 一、信号的基本概念 信号&#xff0c;简单来说&#xf…

【持续更新】Mχ Plaayer Pro 1.86.0安卓知名播放器最新免费高级修改版

Mχ Plaayer Pro MOD 版本免费 APK&#xff0c;专为安卓手机和平板打造。这是一款功能强大的视频播放器&#xff0c;具备先进的硬件加速技术和字幕支持功能。 • 硬件加速 - 新增 HW 解码器帮助更多视频格式实现硬件加速。 • 多核心解码 - Mχ Plaayer 是首款支持多核心解码的…

链接 -- 动静态链接 --特点、区别、静态库安装下载

1.链接是什么&#xff1f; 我们的程序&#xff0c;和 库&#xff08;语言一定会有自己的标准库&#xff09; 结合的过程就叫做链接。 2.为什么有链接&#xff1f; 让开发站在巨人的肩膀&#xff0c;提高开发效率。 c语言库&#xff1a; ls /user/include/ 动静态库的特点与区别…