图像处理实战-信用卡数字识别(二)

信用卡数字识别的项目需求是:给定一张信用卡,识别出信用卡上的卡号。

本文内容为该项目的实战,其中用到相关技术和函数请参考《图像处理实战-信用卡数字识别(一)》。
完整代码:https://github.com/YvanYan/image_processing.git


该项目的主要思路是使用模板匹配来进行信用卡数字识别。
其主要流程为:
1.对模板图片进行处理,得到每个数字的模板。
2.对信用卡信息进行处理,去除多余的背景信息。
3.对信用卡上的数字进行选取,对于非卡号数字进行剔除。
4.得到卡号区域后,对卡号进行数字划分。
5.进行模板匹配,得到每个数字图像所对应的数字。

1.对模板图片进行处理,得到每个数字的模板。

# 读取模板图片
template = cv2.imread('images/template.png')
cv_show('template', template)
# 模板图片灰度化。这里的模板图片本身就是二值化的因此没有明显区别。
template_gray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
cv_show('templage_gray', template_gray)
# 二值化,转化为数字为白色,背景为黑色的图片。
template_binary = cv2.threshold(template_gray, 127, 255, cv2.THRESH_BINARY_INV)[1]
cv_show('template_binary', template_binary)

这里threshold后面的[1]表示取threshold函数返回值的第二个threshold函数返回值为阈值二值化后的图片,即这里仅保存返回后的图片。

# 根据二值化的模板图,进行轮廓检测
template_fCnts, cnts, hierarchy = cv2.findContours(template_binary
                                                   , cv2.RETR_EXTERNAL
                                                   , cv2.CHAIN_APPROX_SIMPLE)
# 画出每个数字的轮廓
template_rect = cv2.drawContours(template.copy(), cnts, -1, (0,0,255), 2)
cv_show('template_rect', template_rect)
# 对十个数字根据左上角的位置进行排序,这样数字按照从小到大的顺序排列出来。
cnts = myutils.sort_contours(cnts, method="left-to-right")[0]
number = {}
# 根据排列的结果,将每个数字截取出来。将每个数字图片所对应的数字对应起来。
# 这里要注意,对像素值进行取值时,数组的行对应的是图片的y轴,列对应的图片的x轴。
for (i, cnt) in enumerate(cnts):
    (x,y,w,h) = cv2.boundingRect(cnt)
    roi = template_binary[y:y+h, x:x+w]
    roi = cv2.resize(roi, (57,88))
    
    number[i] = roi

2.对信用卡信息进行处理,去除多余的背景信息。

# 读取信用卡图片
cardImg = cv2.imread('images/credit_card_01.png')
cardImg = cv2.resize(cardImg
                          , (300, int(float(300 / cardImg.shape[1]) * cardImg.shape[0]))
                          , interpolation=cv2.INTER_AREA)
cv_show('cardImg', cardImg)
cardImg_gray = cv2.cvtColor(cardImg, cv2.COLOR_BGR2GRAY)
cv_show('cardImg_gray', cardImg_gray)

# 指定卷积核大小
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
# 进行礼帽操作
cardImg_tophat = cv2.morphologyEx(cardImg_gray, cv2.MORPH_TOPHAT, rectKernel)
cv_show('cardImg_open', cardImg_tophat)

3.对信用卡上的数字进行选取,对于非卡号数字进行剔除。

# 使用sobel算子进行边缘检测,这里仅适用x方向的梯度。因为经过实验,使用x,y混合的梯度,效果并不理想。
sobelx = cv2.Sobel(cardImg_tophat, cv2.CV_64F, 1, 0, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
(minX, maxX) = (np.min(sobelx), np.max(sobelx))
sobelx = (255 * ((sobelx - minX) / (maxX - minX)))
sobelx = sobelx.astype('uint8')

# sobely = cv2.Sobel(cardImg_tophat, cv2.CV_64F, 0, 1, ksize=3)
# sobely = cv2.convertScaleAbs(sobely)
# (minY, maxY) = (np.min(sobely), np.max(sobely))
# sobely = (255 * ((sobely - minY) / (maxY - minY)))
# # sobely = sobely.astype('uint8')
#
# sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
# sobelxy = sobelxy.astype('uint8')
cv_show('sobelx', sobelx)
#进行闭运算,使相邻的数字连接起来,这样便于筛选。
cardImg_close = cv2.morphologyEx(sobelx, cv2.MORPH_CLOSE, rectKernel)
cv_show('cardImg_close', cardImg_close)

cardImg_binary = cv2.threshold(cardImg_close, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY)[1]
cv_show('cardImg_binary', cardImg_binary)

cardImg_close = cv2.morphologyEx(cardImg_binary, cv2.MORPH_CLOSE, sqKernel)
cv_show('cardImg_close', cardImg_close)
# 轮廓检测,检测出每一个数字区块
cardImg_, cnts, hierarchy = cv2.findContours(cardImg_close
                                             , cv2.RETR_EXTERNAL
                                             , cv2.CHAIN_APPROX_SIMPLE)
cardImg_cnts = cv2.drawContours(cardImg.copy(), cnts, -1, (0,0,255), 2)
cv_show('cardImg_cnts', cardImg_cnts)

# 对轮廓进行筛选, 根据边框的尺寸仅保留卡号区域
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])

4.得到卡号区域后,对卡号进行数字划分。
5.进行模板匹配,得到每个数字图像所对应的数字。

output = []
# 对每个4数字块进行处理
for (i, (x, y, w, h)) in enumerate(locs):
    group_output = []
    group = cardImg_gray[y-5:y + h + 5, x-5:x + w + 5]
    group = cv2.threshold(group, 0, 255, cv2.THRESH_OTSU|cv2.THRESH_BINARY)[1]
    cv_show('group', group)
    group_, group_cnts, group_hierarchy = cv2.findContours(group
                                                           , cv2.RETR_EXTERNAL
                                                           , cv2.CHAIN_APPROX_SIMPLE)
    group_cnts = contours.sort_contours(group_cnts, method="left-to-right")[0]
    # 分割每个数字
    for cnt in group_cnts:
        (nx,ny,nw,nh) = cv2.boundingRect(cnt)
        roi = group[ny:ny+nh, nx:nx+nw]
        roi = cv2.resize(roi, (57, 88))
        cv_show('roi', roi)

        score = []
        # 对每个数字进行模板匹配
        for (number_i, number_roi) in number.items():
            result = cv2.matchTemplate(roi, number_roi, cv2.TM_CCOEFF)
            score_ = cv2.minMaxLoc(result)[1]

            score.append(score_)

        group_output.append(str(np.argmax(score)))
# 绘制每个数字
    cv2.rectangle(cardImg, (x - 5, y - 5),
                  (x + w + 5, y + h + 5), (0, 0, 255), 1)
    cv2.putText(cardImg, "".join(group_output), (x, y - 15),
                cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
    output.append(group_output)

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