使用python3+opencv3实现的识别答题卡的例子(02)
时间:2022-06-04
本文章向大家介绍使用python3+opencv3实现的识别答题卡的例子(02),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
answer_sheet_scan
使用python3+opencv3实现的一些识别答题卡的例子
识别例子02
例子02是ayoungprogrammer博客上参考作者原版C++代码和思路,然后改造成python版本的,先在本地运行成功之后,然后加上自己的理解,给大多数核心代码加上了详细的中文注释,并在每一个关键阶段都会弹出具体的窗体展示识别流程,这样便于大家更能详细的看到核心部分的细节,感兴趣的同学,可以自己在再尝试加一些更细部分的debug弹窗。
本地PyCharm运行后一些截图:
(1)原图
(2)灰度后的图
(3)自适应二值化后的图
(4)标记轮廓后的原图
(5)提取答题内容区后的图
(6)对答题内容区进行自适应二值化后的图
(7)标记答案后的图
代码如下:
# -*- coding:utf-8 -*-
import matplotlib.pyplot as plt
from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
import imutils
import cv2 as cv
# 加载原图,可在项目imgs/example02目录下找到
img=cv.imread("E:\tmp\test6.jpg")
# 打印原图
cv.imshow("orgin",img)
# 灰度化
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 打印灰度图
cv.imshow("gray",gray)
# 高斯滤波,清除一些杂点
blur=cv.GaussianBlur(gray,(3,3),0)
# 自适应二值化算法
thresh2 = cv.adaptiveThreshold(blur,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV,131,4)
# 打印二值化后的图
cv.imshow("thresh2",thresh2)
# 寻找轮廓
image, cts, hierarchy = cv.findContours(thresh2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 打印找到的轮廓
print("轮廓数:",len(cts))
# 对拷贝的原图进行轮廓标记
contour_flagged=cv.drawContours(img.copy(), cts, -1, (0, 0, 255), 3)
# 打印轮廓图
cv.imshow("contours_flagged", contour_flagged)
# 按像素面积降序排序
list = sorted(cts, key=cv.contourArea, reverse=True)
# 遍历轮廓
for ct in list:
# 周长,第1个参数是轮廓,第二个参数代表是否是闭环的图形
peri = 0.01 * cv.arcLength(ct, True)
# 获取多边形的所有定点,如果是四个定点,就代表是矩形
approx = cv.approxPolyDP(ct, peri, True)
# 只考虑矩形
if len(approx) == 4:
# 从原图中提取所需的矫正图片
ox = four_point_transform(img, approx.reshape(4, 2))
# 从原图中提取所需的矫正图片
tx = four_point_transform(gray, approx.reshape(4, 2))
# 打印矫正后的灰度图
cv.imshow("tx",tx)
# 对矫正图进行高斯模糊
blur = cv.GaussianBlur(tx, (3, 3), 0)
# 对矫正图做自适应二值化
thresh2 = cv.adaptiveThreshold(blur, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY_INV, 131, 4)
# 打印矫正后的二值化图
cv.imshow("tx_thresh2", thresh2)
# 获取轮廓
r_image, r_cts, r_hierarchy = cv.findContours(thresh2, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
# 打印得到轮廓数量
print("第二层轮廓数:", len(r_cts))
# 用于存储答案的python list变量
question_list=[]
for r_ct in r_cts :
# 转为矩形,分别获取 x,y坐标,及矩形的宽和高
x, y, w, h = cv.boundingRect(r_ct)
# 过滤掉不符合答案坐标和长宽的选项
if x>2 and y>2 and w>20 and h>20 :
# cv.drawContours(ox, r_ct, -1, (0, 0, 255), 1)
question_list.append(r_ct)
print("答案总数:",len(question_list))
# 按坐标从上到下排序
questionCnts = contours.sort_contours(question_list, method="top-to-bottom")[0]
# 使用np函数,按5个元素,生成一个集合
for (q, i) in enumerate(np.arange(0, len(questionCnts), 5)):
# 每一个行5个答案,从左到右排序
cnts = contours.sort_contours(questionCnts[i:i + 5])[0]
# 存储一行题里面的每个答案
ans_list = []
for (j, cc) in enumerate(cnts):
# 生成全黑画布
mask = np.zeros(thresh2.shape, dtype="uint8")
# 将每一个答案按轮廓写上去,并将填充颜色设置成白色
tpp = cv.drawContours(mask, [cc], -1, 255, -1)
# 两个图片做位运算
mask = cv.bitwise_and(thresh2, thresh2, mask=mask)
# 统计每个答案的像素
total = cv.countNonZero(mask)
# 添加到集合里面
ans_list.append( (total,j) )
# 按像素大小排序
ans_list=sorted(ans_list,key=lambda x:x[0],reverse=True)
max_ans_num=ans_list[0][1]
max_ans_size=ans_list[0][0]
print("答案序号:",max_ans_num,"列表:",ans_list)
# 给选中答案,标记成红色
cv.drawContours(ox, cnts[max_ans_num], -1, (0, 0, 255), 2)
cv.imshow("answer_flagged", ox)
# 最大的轮廓就是我们想要的,之后的就可以结束循环了
break
# 阻塞等待窗体关闭
cv.waitKey(0)
源码已经上传我的github上,欢迎大家fork学习.
https://github.com/qindongliang/answer_sheet_scan
- 深度学习:能击败欧洲围棋冠军,还能防恶意软件
- Metasploit中的JAVA反向TCP做法的研究
- 商业级别Fortify白盒神器介绍与使用分析
- [WCF安全系列]消息的保护等级[上篇]
- QEMU 1: 使用QEMU创建虚拟机
- [WCF安全系列]绑定、安全模式与客户端凭证类型:NetNamedPipeBinding、NetTcpBinding与NetMsmqBinding
- 操作系统级虚拟化概述
- 让javascript中的异步请求同步起来
- [WCF REST] WebHttpBinding与消息编码
- React 概要
- [WCF REST] UriTemplate、UriTemplateTable与WebHttpDispatchOperationSelector
- [WCF REST] WebServiceHost有何特别之处?
- OSX SIP机制的“那些事”
- [WCF REST] 帮助页面与自动消息格式(JSON/XML)选择
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- kmskeys10 By HKL, Saturday 7
- C# Random 生成不重复随机数 By HKL, We
- Resin4配置 By HKL, Thursday 11
- 从源码编译N(ginx)+M(ySQL)+P(HP)并安装WordPress By HKL,
- MPEG4视频中,I帧、p帧、B帧的判定(转载) By HKL,
- 使用attrib命令解决存储器中毒后文件夹被隐藏的方法 By HKL,
- 在Archlinux上通过qemu运行ReactOS By HKL,
- Archlinux下解决wireshark普通用户抓包权限问题 By HKL,
- 使用jemalloc对nginx进行优化 By HKL,
- 配置IIS6+PHP5.6+MySQL5 By HKL,
- haproxy与nginx集成实例 By HKL, Wed
- 通过JavaScript实现HTTP到HTTPS的强制跳转 By HKL,
- 在Windows10 Bash中默认启动其他shell By HKL,
- 基于R的竞争风险模型的列线图
- 安装zabbix的windows系统agent By HKL,