1 前言
?? 今天學長向大家分享一個畢業(yè)設(shè)計項目
?? 畢業(yè)設(shè)計 基于opencv的銀行卡識別
??學長這里給一個題目綜合評分(每項滿分5分)
- 難度系數(shù):3分
- 工作量:3分
- 創(chuàng)新點:4分
項目運行效果:
畢業(yè)設(shè)計 機器視覺opencv銀行卡識別系統(tǒng)
項目獲?。?/strong>文章來源:http://www.zghlxwxcb.cn/news/detail-806524.html
https://gitee.com/sinonfin/algorithm-sharing文章來源地址http://www.zghlxwxcb.cn/news/detail-806524.html
2 算法設(shè)計流程
銀行卡卡號識別技術(shù)原理是先對銀行卡圖像定位,保障獲取圖像絕對位置后,對圖像進行字符分割,然后將分割完成的信息與模型進行比較,從而匹配出與其最相似的數(shù)字。主要流程圖如圖
1.銀行卡號圖像
由于銀行卡卡號信息涉及個人隱私,作者很難在短時間內(nèi)獲取大量的銀行卡進行測試和試驗,本文即采用作者個人及模擬銀行卡進行卡號識別測試。
2.圖像預處理
圖像預處理是在獲取圖像后必須優(yōu)先進行的技術(shù)性處理工作,先對銀行卡卡號圖像進行色彩處理,具體做法與流程是先將圖像灰度化,去掉圖像識別上無用的信息,然后利用歸一化只保留有效的卡號信息區(qū)域。
3.字符分割
字符分割是在對圖像進行預處理后,在獲取有效圖像后對有效區(qū)域進行進一步細化處理,將圖像分割為最小識別字符單元。
4.字符識別
字符識別是在對銀行卡卡號進行字符分割后,利用圖像識別技術(shù)來對字符進行分析和匹配,本文作者利用的模板匹配方法。
2.1 顏色空間轉(zhuǎn)換
由于銀行卡卡號識別與顏色無關(guān),所以銀行卡顏色是一個無用因素,我們在圖像預處理環(huán)節(jié)要先將其過濾掉。另外,圖像處理中還含有顏色信息,不僅會造成空間浪費,增加運算量,降低系統(tǒng)的整體效率,還會給以后的圖像分析和處理帶來干擾。因此,有必要利用灰度處理來濾除顏色信息。
灰度處理的實質(zhì)是將顏色信息轉(zhuǎn)化為亮度信息,即將原始的三維顏色信息還原為一維亮度信息?;叶然乃枷胧怯没叶戎礸來表示原始彩色圖像的R(綠色)、g(紅色)和B(藍色)分量的值,具體的流程設(shè)計如圖
2.2 邊緣切割
對于采集到的銀行卡號圖像,由于背景圖案的多樣性和卡號字體的不同,無法直接對卡號圖像進行分割。分割前要準確定位卡號,才能得到有效區(qū)域。數(shù)字字符所在的區(qū)域有許多像素。根據(jù)該特征,通過設(shè)置閾值來確定原始圖像中卡號圖像的區(qū)域。銀行卡圖像的切邊處理設(shè)計如圖
2.3 模板匹配
模板匹配是一種將需要識別的字符與已有固定模板進行匹配的算法技術(shù),該技術(shù)是將已經(jīng)切割好的字符圖像逐個與模板數(shù)字圖像進行對比分析,其原理就是通過數(shù)字相似度來衡量兩個字符元素,將目標字符元素逐個與模板數(shù)字圖像進行匹配,找到最接近的數(shù)字元素即可。匹配計算量隨特征級別的增加而減少。根據(jù)第一步得到的特征,選擇第二種相關(guān)計算方法來解決圖像匹配問題。銀行卡模板匹配流程設(shè)計如圖
2.4 卡號識別
銀行卡卡號識別有其獨有的特性,因為目前市面上大多數(shù)銀行卡卡號是凹凸不平的數(shù)字形式,如果使用傳統(tǒng)的計算機字符識別技術(shù)已顯然不適用,本文針對銀行卡此類特點,研究了解決此類問題的識別方案。從銀行卡待識別的凸凹字符進行預處理,然后根據(jù)滑塊算法逐個窗口對銀行卡字符進行匹配識別,卡號識別一般從切割后的圖像最左端開始,設(shè)定截圖選定框大小為64*48像素,因為銀行卡所需要識別的字符一般為45像素左右。故而以此方式循環(huán)對卡片上所有數(shù)字進行匹配、識別,如果最小值大于設(shè)置的閾值,我們將認為這里沒有字符,這是一個空白區(qū)域,并且不輸出字符。同時,窗口位置J向下滑動,輸出f<19&&j+20<圖像總長度并判斷,最后循環(huán)得到字符數(shù)f、j。
3 銀行卡字符定位 - 算法實現(xiàn)
首先就是將整張銀行卡號里面的銀行卡號部分進行識別,且分出來,這一個環(huán)節(jié)學長用的技術(shù)就是faster-rcnn的方法
將目標識別部分的銀行卡號部門且分出來,進行保存
主程序的代碼如下(非完整代碼):
#!/usr/bin/env python
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import argparse
import os
import cv2
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from lib.config import config as cfg
from lib.utils.nms_wrapper import nms
from lib.utils.test import im_detect
from lib.nets.vgg16 import vgg16
from lib.utils.timer import Timer
os.environ["CUDA_VISIBLE_DEVICES"] = '0' #指定第一塊GPU可用
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.8 # 程序最多只能占用指定gpu50%的顯存
config.gpu_options.allow_growth = True #程序按需申請內(nèi)存
sess = tf.Session(config = config)
CLASSES = ('__background__','lb')
NETS = {'vgg16': ('vgg16_faster_rcnn_iter_70000.ckpt',), 'res101': ('res101_faster_rcnn_iter_110000.ckpt',)}
DATASETS = {'pascal_voc': ('voc_2007_trainval',), 'pascal_voc_0712': ('voc_2007_trainval+voc_2012_trainval',)}
def vis_detections(im, class_name, dets, thresh=0.5):
"""Draw detected bounding boxes."""
inds = np.where(dets[:, -1] >= thresh)[0]
if len(inds) == 0:
return
im = im[:, :, (2, 1, 0)]
fig, ax = plt.subplots(figsize=(12, 12))
ax.imshow(im, aspect='equal')
sco=[]
for i in inds:
score = dets[i, -1]
sco.append(score)
maxscore=max(sco)
# print(maxscore)成績最大值
for i in inds:
# print(i)
score = dets[i, -1]
if score==maxscore:
bbox = dets[i, :4]
# print(bbox)#目標框的4個坐標
img = cv2.imread("data/demo/"+filename)
# img = cv2.imread('data/demo/000002.jpg')
sp=img.shape
width = sp[1]
if bbox[0]>20 and bbox[2]+20<width:
cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0]-20):int(bbox[2])+20] # 裁剪坐標為[y0:y1, x0:x1]
if bbox[0]<20 and bbox[2]+20<width:
cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0]):int(bbox[2])+20] # 裁剪坐標為[y0:y1, x0:x1]
if bbox[0] > 20 and bbox[2] + 20 > width:
cropped = img[int(bbox[1]):int(bbox[3]), int(bbox[0] - 20):int(bbox[2])] # 裁剪坐標為[y0:y1, x0:x1]
path = 'cut1/'
# 重定義圖片的大小
res = cv2.resize(cropped, (1000, 100), interpolation=cv2.INTER_CUBIC) # dsize=(2*width,2*height)
cv2.imwrite(path+str(i)+filename, res)
ax.add_patch(plt.Rectangle((bbox[0], bbox[1]),
bbox[2] - bbox[0],
bbox[3] - bbox[1], fill=False,
edgecolor='red', linewidth=3.5)
)
ax.text(bbox[0], bbox[1] - 2,
'{:s} {:.3f}'.format(class_name, score),
bbox=dict(facecolor='blue', alpha=0.5),
fontsize=14, color='white')
ax.set_title(('{} detections with '
'p({} | box) >= {:.1f}').format(class_name, class_name,thresh),
fontsize=14)
plt.axis('off')
plt.tight_layout()
plt.draw()
def demo(sess, net, image_name):
"""Detect object classes in an image using pre-computed object proposals."""
# Load the demo image
im_file = os.path.join(cfg.FLAGS2["data_dir"], 'demo', image_name)
im = cv2.imread(im_file)
# Detect all object classes and regress object bounds
timer = Timer()
timer.tic()
scores, boxes = im_detect(sess, net, im)
timer.toc()
print('Detection took {:.3f}s for {:d} object proposals'.format(timer.total_time, boxes.shape[0]))
# Visualize detections for each class
CONF_THRESH = 0.1
NMS_THRESH = 0.1
for cls_ind, cls in enumerate(CLASSES[1:]):
cls_ind += 1 # because we skipped background
cls_boxes = boxes[:, 4 * cls_ind:4 * (cls_ind + 1)]
cls_scores = scores[:, cls_ind]
# print(cls_scores)#一個300個數(shù)的數(shù)組
#np.newaxis增加維度 np.hstack將數(shù)組拼接在一起
dets = np.hstack((cls_boxes,cls_scores[:, np.newaxis])).astype(np.float32)
keep = nms(dets, NMS_THRESH)
dets = dets[keep, :]
vis_detections(im, cls, dets, thresh=CONF_THRESH)
def parse_args():
"""Parse input arguments."""
parser = argparse.ArgumentParser(description='Tensorflow Faster R-CNN demo')
parser.add_argument('--net', dest='demo_net', help='Network to use [vgg16 res101]',
choices=NETS.keys(), default='vgg16')
parser.add_argument('--dataset', dest='dataset', help='Trained dataset [pascal_voc pascal_voc_0712]',
choices=DATASETS.keys(), default='pascal_voc')
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
# model path
demonet = args.demo_net
dataset = args.dataset
#tfmodel = os.path.join('output', demonet, DATASETS[dataset][0], 'default', NETS[demonet][0])
tfmodel = r'./default/voc_2007_trainval/cut1/vgg16_faster_rcnn_iter_8000.ckpt'
# 路徑異常提醒
if not os.path.isfile(tfmodel + '.meta'):
print(tfmodel)
raise IOError(('{:s} not found.\nDid you download the proper networks from '
'our server and place them properly?').format(tfmodel + '.meta'))
# set config
tfconfig = tf.ConfigProto(allow_soft_placement=True)
tfconfig.gpu_options.allow_growth = True
# init session
sess = tf.Session(config=tfconfig)
# load network
if demonet == 'vgg16':
net = vgg16(batch_size=1)
# elif demonet == 'res101':
# net = resnetv1(batch_size=1, num_layers=101)
else:
raise NotImplementedError
net.create_architecture(sess, "TEST", 2,
tag='default', anchor_scales=[8, 16, 32])
saver = tf.train.Saver()
saver.restore(sess, tfmodel)
print('Loaded network {:s}'.format(tfmodel))
# # 文件夾下所有圖片進行識別
# for filename in os.listdir(r'data/demo/'):
# im_names = [filename]
# for im_name in im_names:
# print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
# print('Demo for data/demo/{}'.format(im_name))
# demo(sess, net, im_name)
#
# plt.show()
# 單一圖片進行識別
filename = '0001.jpg'
im_names = [filename]
for im_name in im_names:
print('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~')
print('Demo for data/demo/{}'.format(im_name))
demo(sess, net, im_name)
plt.show()
效果如下:
4 字符分割
將切分出來的圖片進行保存,然后就是將其進行切分:
主程序的代碼和上面第一步的步驟原理是相同的,不同的就是訓練集的不同設(shè)置
效果圖如下:
5 銀行卡數(shù)字識別
僅部分代碼:
# author:丹成學長 Q746976041
import os
import tensorflow as tf
from PIL import Image
from nets2 import nets_factory
import numpy as np
import matplotlib.pyplot as plt
# 不同字符數(shù)量
CHAR_SET_LEN = 10
# 圖片高度
IMAGE_HEIGHT = 60
# 圖片寬度
IMAGE_WIDTH = 160
# 批次
BATCH_SIZE = 1
# tfrecord文件存放路徑
TFRECORD_FILE = r"C:\workspace\Python\Bank_Card_OCR\demo\test_result\tfrecords/1.tfrecords"
# placeholder
x = tf.placeholder(tf.float32, [None, 224, 224])
os.environ["CUDA_VISIBLE_DEVICES"] = '0' #指定第一塊GPU可用
config = tf.ConfigProto()
config.gpu_options.per_process_gpu_memory_fraction = 0.5 # 程序最多只能占用指定gpu50%的顯存
config.gpu_options.allow_growth = True #程序按需申請內(nèi)存
sess = tf.Session(config = config)
# 從tfrecord讀出數(shù)據(jù)
def read_and_decode(filename):
# 根據(jù)文件名生成一個隊列
filename_queue = tf.train.string_input_producer([filename])
reader = tf.TFRecordReader()
# 返回文件名和文件
_, serialized_example = reader.read(filename_queue)
features = tf.parse_single_example(serialized_example,
features={
'image' : tf.FixedLenFeature([], tf.string),
'label0': tf.FixedLenFeature([], tf.int64),
})
# 獲取圖片數(shù)據(jù)
image = tf.decode_raw(features['image'], tf.uint8)
# 沒有經(jīng)過預處理的灰度圖
image_raw = tf.reshape(image, [224, 224])
# tf.train.shuffle_batch必須確定shape
image = tf.reshape(image, [224, 224])
# 圖片預處理
image = tf.cast(image, tf.float32) / 255.0
image = tf.subtract(image, 0.5)
image = tf.multiply(image, 2.0)
# 獲取label
label0 = tf.cast(features['label0'], tf.int32)
return image, image_raw, label0
# 獲取圖片數(shù)據(jù)和標簽
image, image_raw, label0 = read_and_decode(TFRECORD_FILE)
# 使用shuffle_batch可以隨機打亂
image_batch, image_raw_batch, label_batch0 = tf.train.shuffle_batch(
[image, image_raw, label0], batch_size=BATCH_SIZE,
capacity=50000, min_after_dequeue=10000, num_threads=1)
# 定義網(wǎng)絡(luò)結(jié)構(gòu)
train_network_fn = nets_factory.get_network_fn(
'alexnet_v2',
num_classes=CHAR_SET_LEN * 1,
weight_decay=0.0005,
is_training=False)
with tf.Session() as sess:
# inputs: a tensor of size [batch_size, height, width, channels]
X = tf.reshape(x, [BATCH_SIZE, 224, 224, 1])
# 數(shù)據(jù)輸入網(wǎng)絡(luò)得到輸出值
logits, end_points = train_network_fn(X)
# 預測值
logits0 = tf.slice(logits, [0, 0], [-1, 10])
predict0 = tf.argmax(logits0, 1)
# 初始化
sess.run(tf.global_variables_initializer())
# 載入訓練好的模型
saver = tf.train.Saver()
saver.restore(sess, '../Cmodels/model/crack_captcha1.model-6000')
# saver.restore(sess, '../1/crack_captcha1.model-2500')
# 創(chuàng)建一個協(xié)調(diào)器,管理線程
coord = tf.train.Coordinator()
# 啟動QueueRunner, 此時文件名隊列已經(jīng)進隊
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
for i in range(6):
# 獲取一個批次的數(shù)據(jù)和標簽
b_image, b_image_raw, b_label0 = sess.run([image_batch,image_raw_batch,label_batch0])
# 顯示圖片
img = Image.fromarray(b_image_raw[0], 'L')
plt.imshow(img)
plt.axis('off')
plt.show()
# 打印標簽
print('label:', b_label0)
# 預測
label0 = sess.run([predict0], feed_dict={x: b_image})
# 打印預測值
print('predict:', label0[0])
# 通知其他線程關(guān)閉
coord.request_stop()
# 其他所有線程關(guān)閉之后,這一函數(shù)才能返回
coord.join(threads)
最終實現(xiàn)效果:
簡化流程
模板預處理
輪廓檢測
最后
項目運行效果:
畢業(yè)設(shè)計 機器視覺opencv銀行卡識別系統(tǒng)
項目獲?。?/strong>
https://gitee.com/sinonfin/algorithm-sharing
到了這里,關(guān)于畢設(shè)開題分享 基于opencv的銀行卡識別的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!