字符识别

在前面两篇博客中分别介绍了车牌定位以及字符分割的代码实现,那么在这篇文章中,我主要想介绍一下车牌识别系统的最后一个模块:字符识别。字符识别可以说是整个系统的核心部分了,这一部分可以有很多中实现方法:模板匹配、SVM以及人工神经网络等方法。本系统采用的是卷积神经网络算法(LeNet-5模型)来进行字符的识别。部分代码参考自下面这位博主(非常感谢博主的分享):
链接: TensorFlow车牌识别完整版.

车牌字符数据集

在进行CNN网络模型训练前,需要收集大量的数据集,并对其进行一些预处理。在对网上进行了大量的收刮之后,总共收集到了约4万张数据样本,车牌数据集的下载链接:
链接: 百度云.(提取码4b2a)
如果链接有问题,请在下方留言,以便及时修改。

模型的训练

本系统主要采用两个模型分别进行省份和数字/字母的识别,首先是省份的训练代码:

# coding=gbk
"""
汉字训练代码模块(LeNet-5)
__author__ = 'kuang'
2019.5.4     7号宿舍楼
"""
#载入模块
import sys
import os
import time
import random
import numpy as np
import tensorflow as tf
import cv2 as cv
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'       #设置警告等级#设置基本参数
SIZE = 1024
WIDTH = 32
HEIGHT = 32
NUM_CLASSES = 31    #总共是31个省份
iterations = 1000#设置存储模型的地址
SAVER_DIR = "XXX"  #自己的路径
PROVINCES = ("川","鄂","赣","甘","贵","桂","黑","沪","冀","津","京","吉","辽","鲁","蒙","闽","宁","青","琼","陕","苏","晋","皖","湘","新","豫","渝","粤","云","藏","浙")
nProvinceIndex = 0
time_begin = time.time()#定义输入节点,对应于图像像素值矩阵集合和图像标签(即所代表的数字)
x = tf.placeholder(tf.float32,shape=[None,SIZE])             #None表示batch size的大小
y_ = tf.placeholder(tf.float32,shape=[None,NUM_CLASSES])     #输出标签的占位
x_image = tf.reshape(x,[-1,WIDTH,HEIGHT,1])                  #生成一个四维的数组#定义卷积函数
def conv_layer(inputs,W,b,conv_strides,kernel_size,pool_strides,padding):L1_conv = tf.nn.conv2d(inputs,W,strides=conv_strides,padding=padding)                          #卷积操作L1_relu = tf.nn.relu(L1_conv + b)                                                                #激活函数RELUreturn tf.nn.max_pool(L1_relu,ksize=kernel_size,strides=pool_strides,padding='SAME')#定义全连接函数
def full_connect(inputs,W,b):return tf.nn.relu(tf.matmul(inputs,W)+b)def average(seq):return float(sum(seq)) / len(seq)#训练模型
if __name__ == "__main__":#第一次遍历图片目录是为了获取图片总数input_count = 0for i in range(0,31):dir = 'XXX\\train\\%s\\' % i    #自己的路径for root,dirs,files in os.walk(dir):for filename in files:input_count = input_count + 1#定义对应维数和各维长度的数组input_images = np.array([[0]*SIZE for i in range(input_count)])      #生成一个input_count行,SIZE列的全零二维数组input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])    #生成一个input_count行,NUM_CLASSES列的全零二维数组#第二次遍历图片目录是为了生成图片数据和标签index = 0for i in range(0,31):dir = 'XXX\\train\\%s\\' % ia = 0for root,dirs,files in os.walk(dir):for filename in files:filename = dir + filenameimg = cv.imread(filename,0)print(filename)print(a)#cv.imshow('threshold',img)#cv.waitKey(0)height = img.shape[0]       #行数width = img.shape[1]        #列数a = a + 1for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:input_images[index][w+h*width] = 1else:input_images[index][w+h*width] = 0input_labels[index][i] = 1index = index + 1#第一次遍历图片目录是为了获得图片总数val_count = 0for i in range(0,31):dir = 'XXX\\train\\%s\\' % ifor root,dirs,files in os.walk(dir):for filename in files:val_count = val_count + 1#定义对应维数和各维长度的数组val_images = np.array([[0]*SIZE for i in range(val_count)])      #生成一个input_count行,SIZE列的全零二维数组val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])    #生成一个input_count行,NUM_CLASSES列的全零二维数组#第二次遍历图片目录是为了生成图片数据和标签index = 0for i in range(0,31):dir = 'XXX\\train\\%s\\' % ifor root,dirs,files in os.walk(dir):for filename in files:filename = dir + filenameimg = cv.imread(filename,0)height = img.shape[0]       #行数width = img.shape[1]        #列数for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:val_images[index][w+h*width] = 1else:val_images[index][w+h*width] = 0val_labels[index][i] = 1index = index + 1with tf.Session() as sess:#第一个卷积层W_conv1 = tf.Variable(tf.truncated_normal([5,5,1,12],stddev=0.1),name="W_conv1")    b_conv1 = tf.Variable(tf.constant(0.1,shape=[12]),name="b_conv1")                    #生成偏置项,并初始化conv_strides = [1,1,1,1]                         #行,列的卷积步长均为1kernel_size = [1,2,2,1]                          #池化层卷积核的尺寸为2*2pool_strides = [1,2,2,1]                         #池化行,列步长为2L1_pool = conv_layer(x_image,W_conv1,b_conv1,conv_strides,kernel_size,pool_strides,padding='SAME')   #第一层卷积池化的输出 ,x_image为输入(后文代码中输入)#第二个卷积层W_conv2 = tf.Variable(tf.truncated_normal([5,5,12,24],stddev=0.1),name="W_conv2")b_conv2 = tf.Variable(tf.constant(0.1,shape=[24]),name="b_conv2")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]                          pool_strides = [1,2,2,1]L2_pool = conv_layer(L1_pool,W_conv2,b_conv2,conv_strides,kernel_size,pool_strides,padding="SAME")#全连接层W_fc1 = tf.Variable(tf.truncated_normal([8*8*24,512],stddev=0.1),name="W_fc1")   b_fc1 = tf.Variable(tf.constant(0.1,shape=[512]),name="b_fc1")h_pool2_flat = tf.reshape(L2_pool,[-1,8*8*24])                 #将第二次池化的二维特征图排列成一维的一个数组 全连接相当于一维的数组h_fc1 = full_connect(h_pool2_flat,W_fc1,b_fc1)                   #进行全连接操作#dropoutkeep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)#readout层W_fc2 = tf.Variable(tf.truncated_normal([512,NUM_CLASSES],stddev=0.1),name="W_fc2")b_fc2 = tf.Variable(tf.constant(0.1,shape=[NUM_CLASSES]),name="b_fc2")#定义优化器和训练OPy_conv = tf.matmul(h_fc1_drop,W_fc2) + b_fc2      #最后的输出层,因为是全连接,相当于每个神经元与权重相乘再加偏移cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y_conv))           #交叉熵损失函数train_step = tf.train.AdamOptimizer((1e-5)).minimize(cross_entropy)                                      correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))                                         accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))                                   #初始化saversaver = tf.train.Saver()sess.run(tf.global_variables_initializer())                                 #初始化所有变量time_elapsed = time.time() - time_begin                                     #运行时间print("读取图片文件耗费时间:%d秒" % time_elapsed)time_begin = time.time()print("一共读取了%s个训练图像,%s个标签"%(input_count,input_count))#设置每次训练操作的输入个数和迭代次数,这里为了支持任意图片总数,定义了一个余数remainder,譬如,如果每次训练训练操作的输入个数为60,图片总数为150张,则前面两次各输入60张,最后一次输入30张(余数30)batch_size = 64      #每次训练的图片数iterations = iterations     #迭代次数batches_count = int(input_count/batch_size)remainder = input_count % batch_sizeprint("训练数据集分成%s批,前面每批%s个数据,最后一批%s个数据" %(batches_count+1,batch_size,remainder))#执行训练迭代for it in range(iterations):#这里的关键是要把输入数组转为np.arraysum_loss = []for n in range(batches_count):loss, out = sess.run([cross_entropy, train_step], feed_dict = {x:input_images[n*batch_size:(n+1)*batch_size],y_:input_labels[n*batch_size:(n+1)*batch_size],keep_prob:0.5})   #feed_dict相当于一次喂进去的数据,x表示输入,前面已经将输入的图片转化为input_image数组形式了sum_loss.append(loss)if remainder > 0:start_index = batches_count * batch_sizeloss, out  =sess.run([cross_entropy, train_step], feed_dict = {x:input_images[start_index:input_count-1],y_:input_labels[start_index:input_count-1],keep_prob:0.5})sum_loss.append(loss)avg_loss = average(sum_loss)#每完成5次迭代,判断准确度是否已达到100%,达到则退出迭代循环iterate_accuracy = 0if it % 5 == 0:loss1 , iterate_accuracy = sess.run([cross_entropy,accuracy],feed_dict = {x : val_images,y_ : val_labels,keep_prob : 1.0})print('第%d次训练迭代:准确率 %0.5f%% ' % (it,iterate_accuracy*100) + '    损失值为:%s' % loss + '    测试损失值:%s' % loss1)if iterate_accuracy >= 0.9999999:break#完成训练,并输出训练时间print('完成训练')time_elapsed = time.time() - time_beginprint("训练耗费时间:%d秒" % time_elapsed)time_begin = time.time()#保存训练结果if not os.path.exists(SAVER_DIR) :print('不存在训练数据保存目录,现在创建保存目录')os.makedirs(SAVER_DIR)saver_path = saver.save(sess,"%smodel.ckpt"%(SAVER_DIR))print("保存路径为:",saver_path)

然后是数字/字母的训练代码:

# coding=gbk
"""
数字/字母训练代码模块(LeNet-5)
__author__ = 'kuang'
2019.5.4     7号宿舍楼
"""
#载入模块
import sys
import os
import time
import random
import numpy as np
import tensorflow as tf
import cv2 as cv
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'       #设置警告等级#设置基本参数
SIZE = 1024
WIDTH = 32
HEIGHT = 32
NUM_CLASSES = 34    #总共是34个数字字母
iterations = 1000#设置保存的路径
SAVER_DIR = XXX\\train_saver\\numbers\\"
LETTERS_DIGITS = ("A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9")
#license_num = []
time_begin = time.time()#定义输入节点,对应于图像像素值矩阵集合和图像标签(即所代表的数字)
x = tf.placeholder(tf.float32,shape=[None,SIZE])    #None表示batch size的大小
y_ = tf.placeholder(tf.float32,shape=[None,NUM_CLASSES])     #输出标签的占位
x_image = tf.reshape(x,[-1,WIDTH,HEIGHT,1])                  #对图像重新定义尺寸#定义卷积函数
def conv_layer(inputs,W,b,conv_strides,kernel_size,pool_strides,padding):L1_conv = tf.nn.conv2d(inputs,W,strides=conv_strides,padding=padding)                          #卷积操作L1_relu = tf.nn.relu(L1_conv + b)                                                                #激活函数RELUreturn tf.nn.max_pool(L1_relu,ksize=kernel_size,strides=pool_strides,padding='SAME')#定义全连接函数
def full_connect(inputs,W,b):return tf.nn.relu(tf.matmul(inputs,W)+b)def average(seq):return float(sum(seq)) / len(seq)#训练模型
if __name__ == "__main__":#第一次遍历图片目录是为了获取图片总数input_count = 0for i in range(31,65):dir = 'XXX\\train\\%s\\' % ifor root,dirs,files in os.walk(dir):for filename in files:input_count = input_count + 1#定义对应维数和各维长度的数组input_images = np.array([[0]*SIZE for i in range(input_count)])      #生成一个input_count行,SIZE列的全零二维数组input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])    #生成一个input_count行,NUM_CLASSES列的全零二维数组#第二次遍历图片目录是为了生成图片数据和标签index = 0for i in range(31,65):dir = 'XXX\\train\\%s\\' % ia = 0for root,dirs,files in os.walk(dir):for filename in files:filename = dir + filenameimg = cv.imread(filename,0)print(filename)print(a)#cv.imshow('threshold',img)#cv.waitKey(0)height = img.shape[0]       #行数width = img.shape[1]        #列数a = a + 1for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:input_images[index][w+h*width] = 1else:input_images[index][w+h*width] = 0input_labels[index][i-31] = 1index = index + 1#第一次遍历图片目录是为了获得图片总数val_count = 0for i in range(31,65):dir = 'XXX\\train\\%s\\' % ifor root,dirs,files in os.walk(dir):for filename in files:val_count = val_count + 1#定义对应维数和各维长度的数组val_images = np.array([[0]*SIZE for i in range(val_count)])      #生成一个input_count行,SIZE列的全零二维数组val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])    #生成一个input_count行,NUM_CLASSES列的全零二维数组#第二次遍历图片目录是为了生成图片数据和标签index = 0for i in range(31,65):dir = 'XXX\\train\\%s\\' % ifor root,dirs,files in os.walk(dir):for filename in files:filename = dir + filenameimg = cv.imread(filename,0)height = img.shape[0]       #行数width = img.shape[1]        #列数for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:val_images[index][w+h*width] = 1else:val_images[index][w+h*width] = 0val_labels[index][i-31] = 1index = index + 1with tf.Session() as sess:#第一个卷积层W_conv1 = tf.Variable(tf.truncated_normal([5,5,1,12],stddev=0.1),name="W_conv1")    b_conv1 = tf.Variable(tf.constant(0.1,shape=[12]),name="b_conv1")                    #生成偏置项,并初始化conv_strides = [1,1,1,1]                         #行,列的卷积步长均为1kernel_size = [1,2,2,1]                          #池化层卷积核的尺寸为2*2pool_strides = [1,2,2,1]                         #池化行,列步长为2L1_pool = conv_layer(x_image,W_conv1,b_conv1,conv_strides,kernel_size,pool_strides,padding='SAME')   #第一层卷积池化的输出 ,x_image为输入(后文代码中输入)#第二个卷积层W_conv2 = tf.Variable(tf.truncated_normal([5,5,12,24],stddev=0.1),name="W_conv2")b_conv2 = tf.Variable(tf.constant(0.1,shape=[24]),name="b_conv2")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]                          pool_strides = [1,2,2,1]L2_pool = conv_layer(L1_pool,W_conv2,b_conv2,conv_strides,kernel_size,pool_strides,padding="SAME")#全连接层W_fc1 = tf.Variable(tf.truncated_normal([8*8*24,512],stddev=0.1),name="W_fc1")   b_fc1 = tf.Variable(tf.constant(0.1,shape=[512]),name="b_fc1")h_pool2_flat = tf.reshape(L2_pool,[-1,8*8*24])                 #将第二次池化的二维特征图排列成一维的一个数组 全连接相当于一维的数组h_fc1 = full_connect(h_pool2_flat,W_fc1,b_fc1)                   #进行全连接操作#dropoutkeep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)#readout层W_fc2 = tf.Variable(tf.truncated_normal([512,NUM_CLASSES],stddev=0.1),name="W_fc2")b_fc2 = tf.Variable(tf.constant(0.1,shape=[NUM_CLASSES]),name="b_fc2")#定义优化器和训练OPy_conv = tf.matmul(h_fc1_drop,W_fc2) + b_fc2      #最后的输出层,因为是全连接,相当于每个神经元与权重相乘再加偏移#global_step = tf.Variable(0,trainable=False)#learing_rate = tf.train.exponential_decay(0.1,global_step,100,0.96,staircase=False)cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_,logits=y_conv))           train_step = tf.train.AdamOptimizer((1e-5)).minimize(cross_entropy)                                        correct_prediction = tf.equal(tf.argmax(y_conv,1),tf.argmax(y_,1))                                         accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))                                          #初始化saversaver = tf.train.Saver()sess.run(tf.global_variables_initializer())                                 #初始化所有变量time_elapsed = time.time() - time_begin                                     #运行时间print("读取图片文件耗费时间:%d秒" % time_elapsed)time_begin = time.time()print("一共读取了%s个训练图像,%s个标签"%(input_count,input_count))#设置每次训练操作的输入个数和迭代次数,这里为了支持任意图片总数,定义了一个余数remainder,譬如,如果每次训练训练操作的输入个数为60,图片总数为150张,则前面两次各输入60张,最后一次输入30张(余数30)batch_size = 64      #每次训练的图片数iterations = iterations     #迭代次数batches_count = int(input_count/batch_size)remainder = input_count % batch_sizeprint("训练数据集分成%s批,前面每批%s个数据,最后一批%s个数据" %(batches_count+1,batch_size,remainder))#执行训练迭代for it in range(iterations):#这里的关键是要把输入数组转为np.arraysum_loss = []for n in range(batches_count):loss, out = sess.run([cross_entropy, train_step], feed_dict = {x:input_images[n*batch_size:(n+1)*batch_size],y_:input_labels[n*batch_size:(n+1)*batch_size],keep_prob:0.5})   #feed_dict相当于一次喂进去的数据,x表示输入,前面已经将输入的图片转化为input_image数组形式了sum_loss.append(loss)if remainder > 0:start_index = batches_count * batch_sizeloss, out  =sess.run([cross_entropy, train_step], feed_dict = {x:input_images[start_index:input_count-1],y_:input_labels[start_index:input_count-1],keep_prob:0.5})sum_loss.append(loss)avg_loss = average(sum_loss)#每完成5次迭代,判断准确度是否已达到100%,达到则退出迭代循环iterate_accuracy = 0if it % 5 == 0:loss1 , iterate_accuracy = sess.run([cross_entropy,accuracy], feed_dict = {x : val_images,y_ : val_labels,keep_prob : 1.0})print('第%d次训练迭代:准确率 %0.5f%% ' % (it,iterate_accuracy*100) + '    损失值为:%s' % avg_loss + '    测试损失值:%s' % loss1)if iterate_accuracy >= 0.9999999:break#完成训练,并输出训练时间print('完成训练')time_elapsed = time.time() - time_beginprint("训练耗费时间:%d秒" % time_elapsed)time_begin = time.time()#保存训练结果if not os.path.exists(SAVER_DIR) :print('不存在训练数据保存目录,现在创建保存目录')os.makedirs(SAVER_DIR)saver_path = saver.save(sess,"%smodel.ckpt"%(SAVER_DIR))print("保存路径为:",saver_path)

两个模型的调用

训练完成后即可进行测试,多模型加载代码如下:

# coding=gbk
"""
多模型恢复模块(测试)
__author__ = 'kuang'
2019.5.10     7号宿舍楼
"""
import tensorflow as tf
import numpy as np
import cv2 as cv
import sys
import os
import random
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'       #设置警告等级#定义卷积函数
def conv_layer(inputs,W,b,conv_strides,kernel_size,pool_strides,padding):L1_conv = tf.nn.conv2d(inputs,W,strides=conv_strides,padding=padding)                          #卷积操作L1_relu = tf.nn.relu(L1_conv + b)                                                                #激活函数RELUreturn tf.nn.max_pool(L1_relu,ksize=kernel_size,strides=pool_strides,padding='SAME')#定义全连接函数
def full_connect(inputs,W,b):return tf.nn.relu(tf.matmul(inputs,W)+b)#定义第一个预测函数
def predicts():PROVINCES = ("川","鄂","赣","甘","贵","桂","黑","沪","冀","津","京","吉","辽","鲁","蒙","闽","宁","青","琼","陕","苏","晋","皖","湘","新","豫","渝","粤","云","藏","浙")nProvinceIndex = 0SAVER_DIR = "XXX\\train_saver\\province\\"#新建一个图g1 = tf.Graph()with g1.as_default():x = tf.placeholder(tf.float32,shape=[None,1024])             #None表示batch size的大小,这里可以是任何数,因为不知道待训练的图片数,SIZE指图片的大小y_ = tf.placeholder(tf.float32,shape=[None,31])     #输出标签的占位x_image = tf.reshape(x,[-1,32,32,1])                  #生成一个四维的数组sess1 = tf.Session(graph=g1)saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))#model_file = "%smodel.ckpt"%(SAVER_DIR)model_file = tf.train.latest_checkpoint(SAVER_DIR)      #找出所有模型中最新的模型saver.restore(sess1,model_file)                          #恢复模型,相当于加载模型#第一个卷积层W_conv1 = sess1.graph.get_tensor_by_name("W_conv1:0")b_conv1 = sess1.graph.get_tensor_by_name("b_conv1:0")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]pool_strides = [1,2,2,1]L1_pool = conv_layer(x_image,W_conv1,b_conv1,conv_strides,kernel_size,pool_strides,padding='SAME')print("第一个卷积层")#第二个卷积层W_conv2 = sess1.graph.get_tensor_by_name("W_conv2:0")b_conv2 = sess1.graph.get_tensor_by_name("b_conv2:0")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]pool_strides = [1,2,2,1]L2_pool = conv_layer(L1_pool,W_conv2,b_conv2,conv_strides,kernel_size,pool_strides,padding='SAME')#全连接层W_fc1 = sess1.graph.get_tensor_by_name("W_fc1:0")b_fc1 = sess1.graph.get_tensor_by_name("b_fc1:0")h_pool2_flat = tf.reshape(L2_pool,[-1,8*8*24])h_fc1 = full_connect(h_pool2_flat,W_fc1,b_fc1)#dropoutkeep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)#readout层W_fc2 = sess1.graph.get_tensor_by_name("W_fc2:0")b_fc2 = sess1.graph.get_tensor_by_name("b_fc2:0")#定义优化器和训练opconv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)for n in range(1,2):path = "XXX\\test\\%s.bmp" % (n)        #测试图的路径img = cv.imread(path,0)#cv.imshow('threshold',img)#cv.waitKey(0)height = img.shape[0]      #行数width = img.shape[1]       #列数img_data = [[0]*1024 for i in range(1)]          #创建一个数组,用于将输入的图片转换成数组形式for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:img_data[0][w+h*width] = 1else:img_data[0][w+h*width] = 0result = sess1.run(conv,feed_dict = {x:np.array(img_data),keep_prob:1.0})#用于输出概率最大的3类max1 = 0max2 = 0max3 = 0max1_index = 0max2_index = 0max3_index = 0for j in range(31):if result[0][j] > max1:max1 = result[0][j]max1_index = jcontinueif (result[0][j]>max2) and (result[0][j]<=max1):max2 = result[0][j]max2_index = jcontinueif (result[0][j]>max3) and (result[0][j]<=max2):max3 = result[0][j]max3_index = jcontinuenProvinceIndex = max1_index      #最大概率的类print("概率:[%s %0.2f%%]    [%s %0.2f%%]    [%s %0.2f%%]" % (PROVINCES[max1_index],max1*100,PROVINCES[max2_index],max2*100,PROVINCES[max3_index],max3*100))print("省份简称是:%s" % PROVINCES[nProvinceIndex])return PROVINCES[nProvinceIndex],nProvinceIndexsess1.close()#定义第二个预测函数
def predictn():LETTERS_DIGITS = ("A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9")license_num = ""SAVER_DIR = "XXX\\train_saver\\numbers\\"print("进入调用")g2 = tf.Graph()with g2.as_default():x = tf.placeholder(tf.float32,shape=[None,1024])             #None表示batch size的大小,这里可以是任何数,因为不知道待训练的图片数,SIZE指图片的大小y_ = tf.placeholder(tf.float32,shape=[None,34])     #输出标签的占位x_image = tf.reshape(x,[-1,32,32,1])                  #生成一个四维的数组sess2 = tf.Session(graph=g2)saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))model_file = tf.train.latest_checkpoint(SAVER_DIR)      #找出所有模型中最新的模型saver.restore(sess2,model_file)#第一个卷积层W_conv1 = sess2.graph.get_tensor_by_name("W_conv1:0")b_conv1 = sess2.graph.get_tensor_by_name("b_conv1:0")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]pool_strides = [1,2,2,1]L1_pool = conv_layer(x_image,W_conv1,b_conv1,conv_strides,kernel_size,pool_strides,padding='SAME')#第二个卷积层W_conv2 = sess2.graph.get_tensor_by_name("W_conv2:0")b_conv2 = sess2.graph.get_tensor_by_name("b_conv2:0")conv_strides = [1,1,1,1]kernel_size = [1,2,2,1]pool_strides = [1,2,2,1]L2_pool = conv_layer(L1_pool,W_conv2,b_conv2,conv_strides,kernel_size,pool_strides,padding='SAME')#全连接层W_fc1 = sess2.graph.get_tensor_by_name("W_fc1:0")b_fc1 = sess2.graph.get_tensor_by_name("b_fc1:0")h_pool2_flat = tf.reshape(L2_pool,[-1,8*8*24])h_fc1 = full_connect(h_pool2_flat,W_fc1,b_fc1)#dropoutkeep_prob = tf.placeholder(tf.float32)h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob)#readout层W_fc2 = sess2.graph.get_tensor_by_name("W_fc2:0")b_fc2 = sess2.graph.get_tensor_by_name("b_fc2:0")#定义优化器和训练opconv = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2) + b_fc2)#想尝试将城市代码和车牌后五位一起识别,因此可以将3-8改为2-8for n in range(2,8):path = "XXX\\test\\%s.bmp" % (n)img = cv.imread(path,0)height = img.shape[0]width = img.shape[1]img_data = [[0]*1024 for i in range(1)]for h in range(0,height):for w in range(0,width):m = img[h][w]if m > 150:img_data[0][w+h*width] = 1else:img_data[0][w+h*width] = 0result = sess2.run(conv,feed_dict = {x:np.array(img_data),keep_prob:1.0})max1 = 0max2 = 0max3 = 0max1_index = 0max2_index = 0max3_index = 0for j in range(34):if result[0][j] > max1:max1 = result[0][j]max1_index = jcontinueif (result[0][j]>max2) and (result[0][j]<=max1):max2 = result[0][j]max2_index = jcontinueif (result[0][j]>max3) and (result[0][j]<=max2):max3 = result[0][j]max3_index = jcontinuelicense_num = license_num + LETTERS_DIGITS[max1_index]print("概率:[%s %0.2f%%]    [%s %0.2f%%]    [%s %0.2f%%]" % (LETTERS_DIGITS[max1_index],max1*100,LETTERS_DIGITS[max2_index],max2*100,LETTERS_DIGITS[max3_index],max3*100))print("车牌编号是:%s" % license_num)return license_numsess2.close()if __name__ == "__main__":a,b = predicts()c = predictn()print("车牌号为:" + a + c[0]+ "·" + c[1:6] )

测试结果


测试结果为:

总结

在训练之后对模型进行测试,模型基本能够识别出部分省份、字母等。但是在一些数字、字母(相近的特征)上,模型并不能很好的进行识别,其目前存在的问题主要有:
1、模型的识别率不高,并且其与前面两个模块的进行有直接的联系,三个模块误差累积,导致最后识别效果不佳。
2、由于汉字和字母、数字一起识别其识别率过低,因此本文采用了的两个模型,但是两个模型的模型恢复时长过长,并不适合于实时检测的要求。
以上就是本小白的一些拙见,希望各位大佬能够给小弟提出一些建议与意见来进行改进。

车牌识别系统三:python+tensorflow实现车牌字符的识别相关推荐

  1. 基于机器学习的车牌识别系统(Python实现基于SVM支持向量机的车牌分类)

    基于机器学习的车牌识别系统(Python实现基于SVM支持向量机的车牌分类) 一.数据集说明 训练样本来自于github上的EasyPR的c++版本,包含一万三千多张数字及大写字母的图片以及三千多张中 ...

  2. 汽车车牌识别系统实现(二)--车牌定位+代码实现

    汽车车牌识别系统实现(二)-- 车牌定位 之前对这部分内容解释的不够详细,自己都看不下去了,因此重新编辑一下. 一.前言 车牌定位是汽车车牌识别能否取得成功的关键过程,车牌定位是否准确直接影响到后续的 ...

  3. 汽车车牌识别系统实现(一)–车牌识别的步骤

    汽车车牌识别系统实现(一)–车牌识别的大致步骤 一.前戏 大学期间,一次偶然的机会接触到了数字图像处理.班主任当时讲的特别兴奋,而我也慢慢喜欢上了它.毕业设计不知天高地厚的.茫然的选择了汽车车牌识别系 ...

  4. java车牌识别系统_基于jsp的车牌识别系统-JavaEE实现车牌识别系统 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的车牌识别系统, 该项目可用各类java课程设计大作业中, 车牌识别系统的系统架构分为前后台两部分, 最终实现在线上 ...

  5. matlab车牌识别系统论文,基于Matlab的车牌识别系统.doc

    摘 要: 车牌识别系统研究在这个信息化的时代是一项重大发展,也是在智能交通范围的一项课题.车牌识别可以获取交通信息,在交通车辆管理.交通车辆监控.交通车流量检测等方面应用普遍,受到相关学者的高度关注. ...

  6. 使用Python+Tensorflow的CNN技术快速识别验证码

    北京 上海巡回站 | NVIDIA DLI深度学习培训 2018年1月26/1月12日 NVIDIA 深度学习学院 带你快速进入火热的DL领域 阅读全文                        ...

  7. 毕业设计 树莓派人脸识别系统(Python)

    文章目录 0 项目说明 1 简介 2 运行要求 3 安装要求 4 项目源码 5 最后 0 项目说明 基于树莓派的人脸识别系统(Python) 提示:适合用于课程设计或毕业设计,工作量达标,源码开放 1 ...

  8. python车牌识别系统开源代码_北京百万庄车牌识别的软件人气火爆

    北京车牌识别厂家直销 北京百万庄车牌识别的软件人气火爆 与字符数据库模板中的标准字符表达形式进行匹配判别,结果输出:将车牌识别的结果以文本格式输出,以上就是车牌识别系统的工作原理,希望能够帮助大家更好 ...

  9. python车牌识别系统抬杆_小区车牌识别自动抬杆系统安装要求

    小区车牌识别自动抬杆系统安装要求 一.识别要求: 小车牌识别(90-120像素),大车牌识别(120-160像素).全分辨率抓拍触发地感的现场车辆照,车牌宽度大小(通过图画软件框定车牌可显示像素大小) ...

最新文章

  1. “强化学习之父”萨顿:预测学习马上要火,AI将帮我们理解人类意识
  2. jQuery,data()方法学习
  3. HAProxy的日志配置以及ACL规则实现负载均衡
  4. 8口PoE网口供电交换机适用环境介绍
  5. Mac OS X下查看CPU信息
  6. bert预训练模型解读_超越谷歌BERT!依图预训练语言理解模型入选NeurIPS
  7. FireFox中国管理者的脑袋被驴踢了。
  8. 快递送不上门,谁的锅?
  9. Java实现数组交换
  10. Python_列表2
  11. Javascript php 异常捕获
  12. WMI 错误 10的解决
  13. java 反解析cron_Java解析Cron表达式
  14. IDEA+Java+SSM+Mysql+Bootstrap+Maven实现网上书城系统
  15. java xml解析 jdom_Java XML解析工具 JDOM介绍及使用实例
  16. discuz tools.php,Discuz!论坛Tools工具箱功能详解 | 学步园
  17. vue-echarts渲染中国地图以及省份地图
  18. Scratch少儿编程
  19. Android入门之——百度地图开发(二)定位当前位置
  20. python按钮点击事件wx_wx.python事件的绑定

热门文章

  1. int在c语言中能输出负数么,为什么使用模数时C ++输出负数?
  2. linux光盘启动软件下载,KNOPPIX 8.5.0发布下载,光盘启动的GNU/Linux系统
  3. (二)永磁同步电机矢量控制(三电平)——三电平传统SVPWM调制算法原理
  4. 调制方法为SVPWM的三相T型三电平并网逆变器simulink模型
  5. 桌面软件大屏通用交叉输入功能设计实例
  6. jsp机票查询预订系统
  7. 一份超全的Python学习资料汇总
  8. 音箱IP65测试办理方法及要求
  9. 有关 numpy 的 35 个实战挑战
  10. 如何在Excel中轻松选择单元格块