文档章节

opencv+python机读卡识别整合版

Digimon
 Digimon
发布于 2017/07/12 16:09
字数 2581
阅读 3594
收藏 173
点赞 18
评论 11

稍微整理了一下这个系列的一二三四章,可能看着更舒服吧……这个系列的解决方案不止一种,调参的方法也是各种各样,反正能够满足需求就极好了

1.预处理

这次的机读卡识别项目来源暑期培训,主要包括内容一张手机拍摄的机读卡位置定位,识别其中选择题模块及少量数字识别,给出样例图片: 机读卡样例

预处理目的:

对于这个识别问题而言,把图像变成二值图应该是最简单粗暴的方法了。为了找准边界,才能良好切割。而对于边缘检测的函数也只能传入灰度图……

1.1.环境配置: 环境是python3.5的,大体部分需要配置的是numpy+mlk版本,scipy,来支持opencv,另外辅助以imutils,这个包里面含有4点变换函数以及matplotlib来辅助绘图

import cv2
import matplotlib.pyplot as plt
import imutils
from imutils.perspective import four_point_transform

1.2.图片预处理 为了方便找出图片的4个顶点,所以需要一次自适应二值化,为了使图片效果更好,所以在二值化之前还加了一层高斯滤波

预处理效果图

#读入图片
image = cv2.imread("test10.jpg")
#转换为灰度图像
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#高斯滤波
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
#自适应二值化方法
blurred=cv2.adaptiveThreshold(blurred,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,51,2)
'''
adaptiveThreshold函数:第一个参数src指原图像,原图像应该是灰度图。
    第二个参数x指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
    第三个参数adaptive_method 指: CV_ADAPTIVE_THRESH_MEAN_C 或 CV_ADAPTIVE_THRESH_GAUSSIAN_C
    第四个参数threshold_type  指取阈值类型:必须是下者之一  
                                 •  CV_THRESH_BINARY,
                        • CV_THRESH_BINARY_INV
     第五个参数 block_size 指用来计算阈值的象素邻域大小: 3, 5, 7, ...
    第六个参数param1    指与方法有关的参数。对方法CV_ADAPTIVE_THRESH_MEAN_C 和 CV_ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数, 尽管它可以是负数。
'''
#这一步可有可无,主要是增加一圈白框,以免刚好卷子边框压线后期边缘检测无果。好的样本图就不用考虑这种问题
blurred=cv2.copyMakeBorder(blurred,5,5,5,5,cv2.BORDER_CONSTANT,value=(255,255,255))

2.图像切割

图像切割的目的是将图像定个便于识别的样子。比如这里四角变换结束以后会吧图像变为2400*2800的大小,无论是什么样的案例图片,都是这个格式,这样最后在局部分割,如选择题答案的识别和数字区域的确定这套程序才能有较好的通用性

2.1.边缘检测 预处理得到二值图像就很容易做边缘检测了,找出4个点,方便之后的4点变换

#canny边缘检测
edged = cv2.Canny(blurred, 10, 100)
# 从边缘图中寻找轮廓,然后初始化答题卡对应的轮廓
'''
findContours
image -- 要查找轮廓的原图像
mode -- 轮廓的检索模式,它有四种模式:
     cv2.RETR_EXTERNAL  表示只检测外轮廓                                  
     cv2.RETR_LIST 检测的轮廓不建立等级关系
     cv2.RETR_CCOMP 建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,
              这个物体的边界也在顶层。
     cv2.RETR_TREE 建立一个等级树结构的轮廓。
method --  轮廓的近似办法:
     cv2.CHAIN_APPROX_NONE 存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max (abs (x1 - x2), abs(y2 - y1) == 1
     cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需
                       4个点来保存轮廓信息
      cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
'''
cnts = cv2.findContours(edged, cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
docCnt = None
# 确保至少有一个轮廓被找到
if len(cnts) > 0:
    # 将轮廓按大小降序排序
    cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
    # 对排序后的轮廓循环处理
    for c in cnts:
        # 获取近似的轮廓
        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.02 * peri, True)
        # 如果近似轮廓有四个顶点,那么就认为找到了答题卡
        if len(approx) == 4:
            docCnt = approx
            break

顶点坐标的存放形式为3维数组,所以若想演示最大的4个顶点应做如下操作:

newimage=image.copy()
for i in docCnt:
    #circle函数为在图像上作图,新建了一个图像用来演示四角选取
    cv2.circle(newimage, (i[0][0],i[0][1]), 50, (255, 0, 0), -1)

四角选择图像

2.2.四点变换 四点变换直接调用大佬写好放在imutils中的函数就好了。这里存了两个,一个原图一个灰度图,原图用来配合展示,灰度图用来支配

paper = four_point_transform(image, docCnt.reshape(4, 2))
warped = four_point_transform(gray, docCnt.reshape(4, 2))

四角变换效果

3.对选择题识别

3.1.对选择题图像部分预处理 经过四点变换后的图像需要经过重新转换标准长宽,以便对选择题部分标定题号及答案。这里的图像定义为2400*2800 。选择题部分最大的特点是需要将黑块突出,以及过滤掉没填涂的选项,以便确认。预处理方法选择均值滤波及二进制二值化的方法。

# 对灰度图应用二值化算法
thresh=cv2.adaptiveThreshold(warped,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,53,2)
#重塑可能用到的图像
thresh = cv2.resize(thresh, (width1, height1), cv2.INTER_LANCZOS4)
paper = cv2.resize(paper, (width1, height1), cv2.INTER_LANCZOS4)
warped = cv2.resize(warped, (width1, height1), cv2.INTER_LANCZOS4)
#均值滤波
ChQImg = cv2.blur(thresh, (23, 23))
#二进制二值化
ChQImg = cv2.threshold(ChQImg, 100, 225, cv2.THRESH_BINARY)[1]
 '''
    threshold参数说明
    第一个参数 src    指原图像,原图像应该是灰度图。
   第二个参数 x      指用来对像素值进行分类的阈值。
   第三个参数 y      指当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
   第四个参数 Methods  指,不同的不同的阈值方法,这些方法包括:
                •cv2.THRESH_BINARY        
                •cv2.THRESH_BINARY_INV    
                •cv2.THRESH_TRUNC        
                •cv2.THRESH_TOZERO        
                •cv2.THRESH_TOZERO_INV    
    '''

选择题部分预处理

3.2.寻找结果中黑块坐标 这里寻找坐标的目的是为了确定黑块所代表的题号及选项,用轮廓中心来进行描述

# 在二值图像中查找轮廓
cnts = cv2.findContours(ChQImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
for c in cnts:
     # 计算轮廓的边界框,然后利用边界框数据计算宽高比
      (x, y, w, h) = cv2.boundingRect(c)
      if (w > 60 & h > 20)and y>900 and y<2000:
            M = cv2.moments(c)
            cX = int(M["m10"] / M["m00"])
            cY = int(M["m01"] / M["m00"])
            #绘制中心及其轮廓
            cv2.drawContours(paper, c, -1, (0, 0, 255), 5, lineType=0)
            cv2.circle(paper, (cX, cY), 7, (255, 255, 255), -1)
            #保存题目坐标信息
            Answer.append((cX, cY))

选择题轮廓勾勒

3.3.计算选择题题号及答案 比较绕,主要还是根据取余和倍数关系

def judgey0(y):
    if (y / 5 < 1):
        return  y + 1
    elif y / 5 < 2 and y/5>=1:
        return y % 5 + 20 + 1
    else:
        return y % 5 + 40 + 1
def judgex0(x):
    if(x%5==1):
        return 'A'
    elif(x%5==2):
        return 'B'
    elif(x%5==3):
        return 'C'
    elif(x%5==4):
        return 'D'
def judge0(x,y):
    if x/5<1 :
        #print(judgey0(y))
        return (judgey0(y),judgex0(x))
    elif x/5<2 and x/5>=1:
        #print(judgey0(y)+5)
        return (judgey0(y)+5,judgex0(x))
    elif x/5<3 and x/5>=2:
       # print(judgey0(y)+10)
        return (judgey0(y)+10,judgex0(x))
    else:
        #print(judgey0(y)+15)
        return (judgey0(y)+15,judgex0(x))

输出运算结果:

IDAnswer=[]
for i in Answer:
    for j in range(0,len(xt1)-1):
        if i[0]>xt1[j] and i[0]<xt1[j+1]:
            for k in range(0,len(yt1)-1):
                if i[1]>yt1[k] and i[1]<yt1[k+1]:
                    judge0(j,k)
                    IDAnswer.append(judge0(j,k))
#对答案部分重新排序,以最好的方式输出
IDAnswer.sort()
print(IDAnswer)
print(len(IDAnswer))

至此完成选择题部分 选择题演示

4.数字识别,调用百度api

数字识别经过测试总的感觉还是可以。需要注意的地方是要对数字板块需要切割出来这样给机器会好认点,但也不能单个字拿出来。最好能有一串,同时也需要注意图片尺寸

4.1.对数字图像部分进行处理 预处理部分同样需要,步骤与选择题模块相似,但目的不同,文字部分主要将数字变粗,便于识别。其实也就是和选择题模块相比变了几个参数

NumImg=cv2.blur(thresh,(15,15))
NumImg=cv2.threshold(NumImg, 170, 255, cv2.THRESH_BINARY)[1]

数字部分预处理

4.2.调用百度ocr api 试过多种检测方式,还是用别人家现成的好http://apistore.baidu.com/ 百度api使用方法: 首先需要注册一个百度云账号,这样在个人中心里就会看到apikey。这个就是和百度进行交流的钥匙。然后找到百度ocr的入口找到接口地址。虽然这里给出了但还是可以看下文档,里面有些细节,比如图片想免费就要300k以内。上面给的python示例代码是py2的,这里给出py3的方式(http://apis.baidu.com/idl_baidu/baiduocrpay/idlocrpaid)

import sys, urllib, json
import urllib.request
import urllib.parse
import base64
url = 'http://apis.baidu.com/idl_baidu/ocridcard/ocridcard'

data = {}
data['fromdevice'] = "pc"
data['clientip'] = "10.10.10.0"
data['detecttype'] = "LocateRecognize"
data['languagetype'] = "ENG"#英文模式
data['imagetype'] = "1"
#图片在本地

file_object = open('T.png','rb')
try:
     img = file_object.read( )
finally:
     file_object.close( )
data['image'] =base64.b64encode(img)


decoded_data = urllib.parse.urlencode(data)
decoded_data = decoded_data.encode('utf-8')

req = urllib.request.Request(url,decoded_data)

req.add_header("Content-Type", "application/x-www-form-urlencoded")
req.add_header("apikey", "这里填入个人中心的apikey") 

resp = urllib.request.urlopen(req)
content = resp.read()
if(content):
    content = json.loads(content.decode())
    print(content)

4.3.切割图片 根据具体情况需要切割图片才能让百度api识别,具体限制因素还是图片大小,切割方式,这里只给出示例

#切割具体位置[起始y:终止y,起始x:终止y]
tempimg1=img[240:461,213:939]
#图片切割,width,height分别填入目标宽高
tempimg1 = cv2.resize(tempimg1, (width, height), cv2.INTER_LANCZOS4)
#图片保存,png,jpg格式均可
 cv2.imwrite("T.png", tempimg1)

之后调用,若识别为英文需要转化,比如可能将0识别为D,这时转换即可,如:

def temp(char):
    if(char=='D'):
        return '0'

效果如图展示 数字识别样例 当然若是能想办法去掉答题卡外围边框效果应该会更好……

© 著作权归作者所有

共有 人打赏支持
Digimon
粉丝 40
博文 17
码字总数 14810
作品 0
成都
程序员
加载中

评论(11)

dereklin
dereklin
xt1,yt1的值是怎么来的?
kkHAIKE
kkHAIKE
把框印成虚的,会淡一些
Digimon
Digimon

引用来自“笨二十一”的评论

最后 考号识别好像有问题,1识别成0了?

引用来自“Digimon”的评论

是的,因为他有一个框,如果能想办法把框去掉应该还能再准确些

引用来自“笨二十一”的评论

可以尝试红色边框或者打分用红色笔,然后滤色,我也在做这块的产品
嗯嗯,有道理,想的是印刷出来的黑白是最经济的:joy:
笨二十一
笨二十一

引用来自“笨二十一”的评论

最后 考号识别好像有问题,1识别成0了?

引用来自“Digimon”的评论

是的,因为他有一个框,如果能想办法把框去掉应该还能再准确些
可以尝试红色边框或者打分用红色笔,然后滤色,我也在做这块的产品
Digimon
Digimon

引用来自“liujiest”的评论

666
:exclamation:
yyll11
yyll11
哇,
Digimon
Digimon

引用来自“笨二十一”的评论

最后 考号识别好像有问题,1识别成0了?
是的,因为他有一个框,如果能想办法把框去掉应该还能再准确些
liujiest
liujiest
666
bobo2cj
bobo2cj
不错
不点
mark 一下
北京中安未来电子护照阅读器(最新版本)

一、产品描述: 北京中安未来电子护照阅读器是一款外形轻巧美观的证件识读设备,它配备高清500万像素成像系统,采用TH-OCR技术可识别多种身份证件。可识读符合国际民航组织ICAO DOC 9303标准...

wenzuoyong123 ⋅ 04/23 ⋅ 0

关于机器识别或图像识别的问题求助?

把机读卡扫描成图片,通过对图片的处理,得到学生的得分。尽管有现成的机读卡,但是这个卡是定制的,所以只有通过自己做识别了,但是还没有思路,求大家集思广益。

小昭归来 ⋅ 2017/02/17 ⋅ 2

“智享未来 知行合一”,开为科技助力企业开启人工智能新时代

开为科技在春节后还将举行一场纵向产品发布会,以“新零售、新未来”为主题,届时会公布他们在新零售领域实现的两款杀手级产品。 2月6日,开为科技在中新南京生态科技岛召开了2018年首场产品...

行者武松 ⋅ 04/11 ⋅ 0

opencv中复制视频不成功,请大牛请教

大牛们,本人在opencv+python环境下,用下列代码创建新视频,文件创建成功,但是该视频文件没有任何内容写入,是什么原因啊 import cv2 videoCapture = cv2.VideoCapture('f://video.avi') ...

哈泥湖 ⋅ 2013/07/19 ⋅ 1

android如何设置默认的nfc读卡程序

自己的工程里面带有nfc读卡的功能,如果手机上面装了另外一个nfc的读卡应用(例如:快拍nfc),在读卡的时候就会弹出一个选择窗口,里面可以选择自己工程来读卡,也可以选择其他读卡应用来读...

生姜可乐 ⋅ 2013/09/22 ⋅ 1

Arduino下HY502B读卡实验

HY502B模块支持SPI接口,数字电路具有双电压工作模式(TTL和COMS),主要在一些计费系统和身份识别读卡器系统中应用,该系列模块功耗低,工作电压范围2.7v—5.5v。 现在简单介绍下Arduino接H...

pc朵拉 ⋅ 2013/07/02 ⋅ 0

关于BHO 对DHtml的操作问题

大家好,因为项目需求,在系统中会通过读卡设备读取磁卡的信息进入到系统中。 因为现在项目是用B/S开发的。所以现在就存在有怎么能让读卡设备获取到的信息写入html页面中。 自然,想到了IE插...

职通网 ⋅ 2012/03/03 ⋅ 2

【AI TOP 10】扎克伯格要整顿Facebook;LeCun飙脏话批机器人Sophia;北京站加装人脸识别检票机

今日头条 扎克伯格公布2018年个人挑战:整顿Facebook 产业要闻 谷歌云:平台上的可抢占GPU资源将以50%折扣出售 Intel芯片漏洞或成 Nvidia 和 AMD的营销顺风车 台积电7nm领跑三星 签下40多家...

技术小能手 ⋅ 01/05 ⋅ 0

射频识别技术漫谈(28)——基于MF1射频卡的酒店门锁设计

【转自】http://blog.sina.com.cn/s/blog9ed067ad0101dupi.html 电子门锁是现代星级酒店管理电子化、智能化的重要电子设备。相较于传统的机械锁,基于RFID技术的电子门锁使用方便,易于管理,...

wxh0000mm ⋅ 03/28 ⋅ 0

如何搭建自己的乐高机器人编程环境

为什么要学习乐高机器人编程 为抢抓人工智能发展的重大战略机遇,构筑我国人工智能发展的先发优势,加快建设创新型国家和世界科技强国,国务院印发《新一代人工智能发展规划》。规划中建议实...

oncast ⋅ 2017/09/12 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

聊聊spring cloud的RequestRateLimiterGatewayFilter

序 本文主要研究一下spring cloud的RequestRateLimiterGatewayFilter GatewayAutoConfiguration @Configuration@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMi......

go4it ⋅ 41分钟前 ⋅ 0

Spring JavaConfig 注解

JavaConfig注解允许开发者将Bean的定义和配置放在Java类中。它是除使用XML文件定义和配置Bean外的另一种方案。 配置: 如一个Bean如果在XML文件可以这样配置: <bean id="helloBean" class="...

霍淇滨 ⋅ 48分钟前 ⋅ 0

Spring clound 组件

Spring Cloud技术应用从场景上可以分为两大类:润物无声类和独挑大梁类。 润物无声,融合在每个微服务中、依赖其它组件并为其提供服务。 Ribbon,客户端负载均衡,特性有区域亲和、重试机制。...

英雄有梦没死就别停 ⋅ 50分钟前 ⋅ 0

Confluence 6 重新获得站点备份文件

Confluence 将会创建备份,同时压缩 XML 文件后存储熬你的 <home-directory>/backups> 目录中。你需要自己访问你安装的 Confluence 服务器,并且从服务器上获得这个文件。 运行从 Confluence...

honeymose ⋅ 54分钟前 ⋅ 0

informix的常用SQL语句

1、创建数据库 eg1. 创建不记录日志的库testdb,参考语句如下: CREATE DATABASE testdb; eg2. 创建带缓冲式的记录日志的数据库testdb(SQL语句不一定在事务之中,拥有者名字不被用于对象的解...

wangxuwei ⋅ 今天 ⋅ 0

matplotlib画图

最简单的入门是从类 MATLAB API 开始,它被设计成兼容 MATLAB 绘图函数。 from pylab import *from numpy import *x = linspace(0, 5, 10)y = x ** 2figure()plot(x, y, 'r')...

Dr_hu ⋅ 今天 ⋅ 0

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 今天 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

小致dad ⋅ 今天 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 今天 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部