openCV银行卡数字匹配(重点归纳)

(1)引入模块

import cv2

import numpy as np

import myutils

(2)图像的基本操作

1.图像读取

img=cv2.imread("路径")

2.图像显示

cv2.imshow("窗口名",图像变量(img))

(3)图像的轮廓检测

1.图像的灰度化

gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

或直接img=cv2,imread("路径",0)

2.图像的二值化(黑白图像)

ref=cv2.threshold(gray,10,255,cv2.THRESH_BINARY_INV)[1]

10,255:表示阈值(超过10取255,小于取0)

cv2.THRESH_BINARY_INV表示取法相反

3.寻找轮廓

refCnt,hierarchy=cv2.findContours(ref,cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

ref表示在轮廓检测中要用二值图像

cv2.RETR_EXTERNAL只寻外轮廓

draw_refCnt=cv2.drawContours(img.copy(),refCnt,-1,(0,0,255),3)

img.copy()为在画轮廓时不印象原图

reCnts表示轮廓

-1表示所有的轮廓

3表示通道3

结果:

4.模块轮廓划分和排序

1.refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0]

将数字模板每个数字的轮廓计算出来,并且排序,reCnts可以看做轮廓合集

2.遍历每个轮廓,得到模块

for (i,c) in enumerate (reCnts)://遍历每个轮廓

enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

(x,y,w,h)=cv2.boundingrect(c)//获得轮廓的外接矩形,返回元组矩形左上坐标和矩阵长宽

roi=ref[x:x+w,    y:y+h]//获得roi感兴趣部分

roi=cv2.resize(58,88)//转化大小适用于模板匹配


4.定义卷积核

rectKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(9,3))

sqKernel=cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))


5.读入银行卡图像,预处理(使其可以更准确)

1.img2=cv2.imread("绝对路径")

img2=myutils.resize(img2,width=300)//把宽变为300,其高转变为对应的长度

gray=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)//灰度化

tophat=cv2.morphologyEx(gray,MORPH_TOPHAT,rectKernel)//礼帽处理,使图像变得更加明亮


6.sobel算子

gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0,  ksize=-1)# ksize=-1相当于用3*3的

gradX = np.absolute(gradX)

(minVal, maxVal) = (np.min(gradX), np.max(gradX))

gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))

gradX = gradX.astype("uint8")

print(np.array(gradX).shape)

cv_show('gradX', gradX)

sobel算子作用:Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。

7.进行闭运算【先腐后膨】(对输入图像没有规定(灰度图和二值图像))

gradx=cv2.morphologyEX(gradX,0,255,cv2.MORPH_CLOSE,rectKernel)

作用:使图像中的孔隙填充(先对图像膨胀后腐蚀 作用:用来填充物体内的小空洞,连接邻近的物体,连接断开的轮廓线,平滑其边界的同时不改变面积)

8.二值化cv2.threshold

# THRESH_OTSU会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0

thresh = cv2.threshold(gradX, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

9.在进行一个闭运算

thresh=cv2.morphologyEX(thresh,0,255,cv2.MORPH_CLOSE,rectKernel)

10.寻找轮廓

threshcnts,hierarchy=cv2.foundContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

cnts=theshcnts

11.遍历轮廓数字组

locs=[]

for(i,c) in enumerate(cnts):

    (x,y,w,h)=cv2.boundingRect(c)

    ar=w/float(h)

    # 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组

    if ar >2.5 and ar <4.0://根据实际大小比例

    if (w >40 and w <55)and (h >10 and h <20):

    # 符合的留下来

    locs.append((x, y, w, h)) 

# 将符合的轮廓从左到右排序(轮廓没有顺序,需要人为排序)

locs =sorted(locs, key=lambda x: x[0])

output = []

12.遍历数字轮廓(每个数字组中数字)

for(i,(gx,gy,gw,gh)) in enumberate(locs):

    groudOutput=[]//定义一个list

    # 根据坐标提取每一个组

    group = gray[gY -5:gY + gH +5, gX -5:gX + gW +5]//截取灰度图像

    cv_show('group', group)

    # 预处理

    group = cv2.threshold(group, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

    cv_show('group', group)

    # 计算每一组的轮廓

    digitCnts, hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,

     cv2.CHAIN_APPROX_SIMPLE)//轮廓检测

    digitCnts = myutils.sort_contours(digitCnts,method="left-to-right")[0]    //为数字组中每个数字轮廓排序

    for (c) in (digitCnts):

        (x,y,w,h)=cv2.boundingRect(c)

        roi=(y:y+h,x:x+w)

        roi=cv2.resize(roi,(58,88))

        # 计算匹配得分

        scores = []

        # 在模板中计算每一个得分

        for (digit, digitROI)in digits.items()://item()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回。digit为key,digitROI为value

         # 模板匹配

            result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF_NORMED))#TM_CCOEFF_NORMED,1表示完美的匹配;-1表示最差的匹配。即表示越大越好

            (_, score, _, _) = cv2.minMaxLoc(result)    

            scores.append(score)//#result参数表示匹配结果图像,必须是单通道32位浮点,scorce参数表示返回的最大值//score可以大致认为匹配度,匹配度越大,越准确

            # 得到最合适的数字

            groupOutput.append(str(np.argmax(scores)))//#numpy.argmax(array, axis) 用于返回一个numpy数组中最大值(最大可能性)的索引值(下标)

            # 画出来

            cv2.rectangle(img2, (gX -5, gY -5),(gX + gW +5, gY + gH +5), (0, 0, 255), 1)

            cv2.putText(img2, "".join(groupOutput), (gX, gY -15),cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

cv2.putText(img, str(i), (123,456)), font, 2, (0,255,0), 3)

各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细

Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串

print("Credit Card #: {}".format("".join(output)))//数字识别结果

cv2.imshow("Image", img2)

cv2.waitKey(0)

附言:这个小项目网上已经有许多源代码(本文只是代码简单说明),本博客只是自己对这个项目的理解,希望对读者有点帮助

银行卡图片:

模板图片:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容