Python+Tensorflow+CNN实现车牌识别
前言:文章如有錯(cuò)誤之處,敬請告知,謝謝~
文章目錄
- 一、項(xiàng)目概述
- 二、生成車牌數(shù)據(jù)集
- 三、數(shù)據(jù)導(dǎo)入
- 四、CNN模型構(gòu)建
- 五、模型訓(xùn)練
- 六、訓(xùn)練結(jié)果展示
- 七、預(yù)測單張車牌
- 八、總結(jié)
一、項(xiàng)目概述
本次項(xiàng)目目標(biāo)是實(shí)現(xiàn)對自動(dòng)生成的帶有各種噪聲的車牌識別。在噪聲干擾情況下,車牌字符分割較困難,此次車牌識別是將車牌7個(gè)字符同時(shí)訓(xùn)練,字符包括31個(gè)省份簡稱、10個(gè)阿拉伯?dāng)?shù)字、24個(gè)英文字母('O’和’I’除外),共有65個(gè)類別,7個(gè)字符使用單獨(dú)的loss函數(shù)進(jìn)行訓(xùn)練。
(運(yùn)行環(huán)境:tensorflow1.14.0-GPU版)
二、生成車牌數(shù)據(jù)集
import os import cv2 as cv import numpy as np from math import * from PIL import ImageFont from PIL import Image from PIL import ImageDrawindex = {"京": 0, "滬": 1, "津": 2, "渝": 3, "冀": 4, "晉": 5, "蒙": 6, "遼": 7, "吉": 8, "黑": 9,"蘇": 10, "浙": 11, "皖": 12, "閩": 13, "贛": 14, "魯": 15, "豫": 16, "鄂": 17, "湘": 18, "粵": 19,"桂": 20, "瓊": 21, "川": 22, "貴": 23, "云": 24, "藏": 25, "陜": 26, "甘": 27, "青": 28, "寧": 29,"新": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39,"9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49,"K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59,"V": 60, "W": 61, "X": 62, "Y": 63, "Z": 64}chars = ["京", "滬", "津", "渝", "冀", "晉", "蒙", "遼", "吉", "黑","蘇", "浙", "皖", "閩", "贛", "魯", "豫", "鄂", "湘", "粵","桂", "瓊", "川", "貴", "云", "藏", "陜", "甘", "青", "寧","新", "0", "1", "2", "3", "4", "5", "6", "7", "8","9", "A", "B", "C", "D", "E", "F", "G", "H", "J","K", "L", "M", "N", "P", "Q", "R", "S", "T", "U","V", "W", "X", "Y", "Z"]def AddSmudginess(img, Smu):"""模糊處理:param img: 輸入圖像:param Smu: 模糊圖像:return: 添加模糊后的圖像"""rows = r(Smu.shape[0] - 50)cols = r(Smu.shape[1] - 50)adder = Smu[rows:rows + 50, cols:cols + 50]adder = cv.resize(adder, (50, 50))img = cv.resize(img,(50,50))img = cv.bitwise_not(img)img = cv.bitwise_and(adder, img)img = cv.bitwise_not(img)return imgdef rot(img, angel, shape, max_angel):"""添加透視畸變"""size_o = [shape[1], shape[0]]size = (shape[1]+ int(shape[0] * cos((float(max_angel ) / 180) * 3.14)), shape[0])interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])if angel > 0:pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0] - interval, size_o[1]]])else:pts2 = np.float32([[0, 0], [interval, size[1]], [size[0] - interval, 0], [size[0], size_o[1]]])M = cv.getPerspectiveTransform(pts1, pts2)dst = cv.warpPerspective(img, M, size)return dstdef rotRandrom(img, factor, size):"""添加放射畸變:param img: 輸入圖像:param factor: 畸變的參數(shù):param size: 圖片目標(biāo)尺寸:return: 放射畸變后的圖像"""shape = sizepts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])pts2 = np.float32([[r(factor), r(factor)], [r(factor), shape[0] - r(factor)], [shape[1] - r(factor), r(factor)],[shape[1] - r(factor), shape[0] - r(factor)]])M = cv.getPerspectiveTransform(pts1, pts2)dst = cv.warpPerspective(img, M, size)return dstdef tfactor(img):"""添加飽和度光照的噪聲"""hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)hsv[:, :, 0] = hsv[:, :, 0] * (0.8 + np.random.random() * 0.2)hsv[:, :, 1] = hsv[:, :, 1] * (0.3 + np.random.random() * 0.7)hsv[:, :, 2] = hsv[:, :, 2] * (0.2 + np.random.random() * 0.8)img = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)return imgdef random_envirment(img, noplate_bg):"""添加自然環(huán)境的噪聲, noplate_bg為不含車牌的背景圖"""bg_index = r(len(noplate_bg))env = cv.imread(noplate_bg[bg_index])env = cv.resize(env, (img.shape[1], img.shape[0]))bak = (img == 0)bak = bak.astype(np.uint8) * 255inv = cv.bitwise_and(bak, env)img = cv.bitwise_or(inv, img)return imgdef GenCh(f, val):"""生成中文字符"""img = Image.new("RGB", (45, 70), (255, 255, 255))draw = ImageDraw.Draw(img)draw.text((0, 3), val, (0, 0, 0), font=f)img = img.resize((23, 70))A = np.array(img)return Adef GenCh1(f, val):"""生成英文字符"""img =Image.new("RGB", (23, 70), (255, 255, 255))draw = ImageDraw.Draw(img)draw.text((0, 2), val, (0, 0, 0), font=f) # val.decode('utf-8')A = np.array(img)return Adef AddGauss(img, level):"""添加高斯模糊""" return cv.blur(img, (level * 2 + 1, level * 2 + 1))def r(val):return int(np.random.random() * val)def AddNoiseSingleChannel(single):"""添加高斯噪聲"""diff = 255 - single.max()noise = np.random.normal(0, 1 + r(6), single.shape)noise = (noise - noise.min()) / (noise.max() - noise.min())noise *= diff# noise= noise.astype(np.uint8)dst = single + noisereturn dstdef addNoise(img): # sdev = 0.5,avg=10img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])return imgclass GenPlate:def __init__(self, fontCh, fontEng, NoPlates):self.fontC = ImageFont.truetype(fontCh, 43, 0)self.fontE = ImageFont.truetype(fontEng, 60, 0)self.img = np.array(Image.new("RGB", (226, 70),(255, 255, 255)))self.bg = cv.resize(cv.imread("data\\images\\template.bmp"), (226, 70)) # template.bmp:車牌背景圖self.smu = cv.imread("data\\images\\smu2.jpg") # smu2.jpg:模糊圖像self.noplates_path = []for parent, parent_folder, filenames in os.walk(NoPlates):for filename in filenames:path = parent + "\\" + filenameself.noplates_path.append(path)def draw(self, val):offset = 2self.img[0:70, offset+8:offset+8+23] = GenCh(self.fontC, val[0])self.img[0:70, offset+8+23+6:offset+8+23+6+23] = GenCh1(self.fontE, val[1])for i in range(5):base = offset + 8 + 23 + 6 + 23 + 17 + i * 23 + i * 6self.img[0:70, base:base+23] = GenCh1(self.fontE, val[i+2])return self.imgdef generate(self, text):if len(text) == 7:fg = self.draw(text) # decode(encoding="utf-8")fg = cv.bitwise_not(fg)com = cv.bitwise_or(fg, self.bg)com = rot(com, r(60)-30, com.shape,30)com = rotRandrom(com, 10, (com.shape[1], com.shape[0]))com = tfactor(com)com = random_envirment(com, self.noplates_path)com = AddGauss(com, 1+r(4))com = addNoise(com)return com@staticmethoddef genPlateString(pos, val):"""生成車牌string,存為圖片生成車牌list,存為label"""plateStr = ""plateList=[]box = [0, 0, 0, 0, 0, 0, 0]if pos != -1:box[pos] = 1for unit, cpos in zip(box, range(len(box))):if unit == 1:plateStr += valplateList.append(val)else:if cpos == 0:plateStr += chars[r(31)]plateList.append(plateStr)elif cpos == 1:plateStr += chars[41 + r(24)]plateList.append(plateStr)else:plateStr += chars[31 + r(34)]plateList.append(plateStr)plate = [plateList[0]]b = [plateList[i][-1] for i in range(len(plateList))]plate.extend(b[1:7])return plateStr, plate@staticmethoddef genBatch(batchsize, outputPath, size):"""將生成的車牌圖片寫入文件夾,對應(yīng)的label寫入label.txt:param batchsize: 批次大小:param outputPath: 輸出圖像的保存路徑:param size: 輸出圖像的尺寸:return: None"""if not os.path.exists(outputPath):os.mkdir(outputPath)outfile = open('data\\plate\\label.txt', 'w', encoding='utf-8')for i in range(batchsize):plateStr, plate = G.genPlateString(-1, -1)# print(plateStr, plate)img = G.generate(plateStr)img = cv.resize(img, size)cv.imwrite(outputPath + "\\" + str(i).zfill(2) + ".jpg", img)outfile.write(str(plate) + "\n")if __name__ == '__main__':G = GenPlate("data\\font\\platech.ttf", 'data\\font\\platechar.ttf', "data\\NoPlates")G.genBatch(101, 'data\\plate', (272, 72))生成的車牌圖像尺寸盡量不要超過300,本次尺寸選取:272 * 72
生成車牌所需文件:
- 字體文件:中文‘platech.ttf’,英文及數(shù)字‘platechar.ttf’
- 背景圖:來源于不含車牌的車輛裁剪圖片
- 車牌(藍(lán)底):template.bmp
- 噪聲圖像:smu2.jpg
文件百度云盤下載鏈接 密碼:h67g
車牌生成后保存至plate文件夾,示例如下:
三、數(shù)據(jù)導(dǎo)入
from genplate import * import matplotlib.pyplot as plt# 產(chǎn)生用于訓(xùn)練的數(shù)據(jù) class OCRIter:def __init__(self, batch_size, width, height):super(OCRIter, self).__init__()self.genplate = GenPlate("data\\font\\platech.ttf", 'data\\font\\platechar.ttf', "data\\NoPlates")self.batch_size = batch_sizeself.height = heightself.width = widthdef iter(self):data = []label = []for i in range(self.batch_size):img, num = self.gen_sample(self.genplate, self.width, self.height)data.append(img)label.append(num)return np.array(data), np.array(label)@staticmethoddef rand_range(lo, hi):return lo + r(hi - lo)def gen_rand(self):name = ""label = list([])label.append(self.rand_range(0, 31)) #產(chǎn)生車牌開頭32個(gè)省的標(biāo)簽label.append(self.rand_range(41, 65)) #產(chǎn)生車牌第二個(gè)字母的標(biāo)簽for i in range(5):label.append(self.rand_range(31, 65)) #產(chǎn)生車牌后續(xù)5個(gè)字母的標(biāo)簽name += chars[label[0]]name += chars[label[1]]for i in range(5):name += chars[label[i+2]]return name, labeldef gen_sample(self, genplate, width, height):num, label = self.gen_rand()img = genplate.generate(num)img = cv.resize(img, (height, width))img = np.multiply(img, 1/255.0)return img, label #返回的label為標(biāo)簽,img為車牌圖像''' # 測試代碼 O = OCRIter(2, 272, 72) img, lbl = O.iter() for im in img:plt.imshow(im, cmap='gray')plt.show() print(img.shape) print(lbl) '''四、CNN模型構(gòu)建
import tensorflow as tfdef cnn_inference(images, keep_prob):W_conv = {'conv1': tf.Variable(tf.random.truncated_normal([3, 3, 3, 32],stddev=0.1)),'conv2': tf.Variable(tf.random.truncated_normal([3, 3, 32, 32],stddev=0.1)),'conv3': tf.Variable(tf.random.truncated_normal([3, 3, 32, 64],stddev=0.1)),'conv4': tf.Variable(tf.random.truncated_normal([3, 3, 64, 64],stddev=0.1)),'conv5': tf.Variable(tf.random.truncated_normal([3, 3, 64, 128],stddev=0.1)),'conv6': tf.Variable(tf.random.truncated_normal([3, 3, 128, 128],stddev=0.1)),'fc1_1': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_2': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_3': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_4': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_5': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_6': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),'fc1_7': tf.Variable(tf.random.truncated_normal([5*30*128, 65],stddev=0.01)),} b_conv = { 'conv1': tf.Variable(tf.constant(0.1, dtype=tf.float32, shape=[32])),'conv2': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[32])),'conv3': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[64])),'conv4': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[64])),'conv5': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[128])),'conv6': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[128])),'fc1_1': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_2': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_3': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_4': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_5': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_6': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),'fc1_7': tf.Variable(tf.constant(0.1, dtype=tf.float32,shape=[65])),} # 第1層卷積層conv1 = tf.nn.conv2d(images, W_conv['conv1'], strides=[1,1,1,1], padding='VALID')conv1 = tf.nn.bias_add(conv1, b_conv['conv1'])conv1 = tf.nn.relu(conv1)# 第2層卷積層conv2 = tf.nn.conv2d(conv1, W_conv['conv2'], strides=[1,1,1,1], padding='VALID')conv2 = tf.nn.bias_add(conv2, b_conv['conv2'])conv2 = tf.nn.relu(conv2)# 第1層池化層pool1 = tf.nn.max_pool2d(conv2, ksize=[1,2,2,1], strides=[1,2,2,1], padding='VALID')# 第3層卷積層conv3 = tf.nn.conv2d(pool1, W_conv['conv3'], strides=[1,1,1,1], padding='VALID')conv3 = tf.nn.bias_add(conv3, b_conv['conv3'])conv3 = tf.nn.relu(conv3)# 第4層卷積層conv4 = tf.nn.conv2d(conv3, W_conv['conv4'], strides=[1,1,1,1], padding='VALID')conv4 = tf.nn.bias_add(conv4, b_conv['conv4'])conv4 = tf.nn.relu(conv4)# 第2層池化層pool2 = tf.nn.max_pool2d(conv4, ksize=[1,2,2,1], strides=[1,2,2,1], padding='VALID')# 第5層卷積層conv5 = tf.nn.conv2d(pool2, W_conv['conv5'], strides=[1,1,1,1], padding='VALID')conv5 = tf.nn.bias_add(conv5, b_conv['conv5'])conv5 = tf.nn.relu(conv5)# 第4層卷積層conv6 = tf.nn.conv2d(conv5, W_conv['conv6'], strides=[1,1,1,1], padding='VALID')conv6 = tf.nn.bias_add(conv6, b_conv['conv6'])conv6 = tf.nn.relu(conv6)# 第3層池化層pool3 = tf.nn.max_pool2d(conv6, ksize=[1,2,2,1], strides=[1,2,2,1], padding='VALID')#第1_1層全連接層# print(pool3.shape)reshape = tf.reshape(pool3, [-1, 5 * 30 * 128])fc1 = tf.nn.dropout(reshape, keep_prob)fc1_1 = tf.add(tf.matmul(fc1, W_conv['fc1_1']), b_conv['fc1_1'])#第1_2層全連接層fc1_2 = tf.add(tf.matmul(fc1, W_conv['fc1_2']), b_conv['fc1_2'])#第1_3層全連接層fc1_3 = tf.add(tf.matmul(fc1, W_conv['fc1_3']), b_conv['fc1_3'])#第1_4層全連接層fc1_4 = tf.add(tf.matmul(fc1, W_conv['fc1_4']), b_conv['fc1_4'])#第1_5層全連接層fc1_5 = tf.add(tf.matmul(fc1, W_conv['fc1_5']), b_conv['fc1_5'])#第1_6層全連接層fc1_6 = tf.add(tf.matmul(fc1, W_conv['fc1_6']), b_conv['fc1_6'])#第1_7層全連接層fc1_7 = tf.add(tf.matmul(fc1, W_conv['fc1_7']), b_conv['fc1_7'])return fc1_1, fc1_2, fc1_3, fc1_4, fc1_5, fc1_6, fc1_7def calc_loss(logit1, logit2, logit3, logit4, logit5, logit6, logit7, labels):labels = tf.convert_to_tensor(labels, tf.int32)loss1 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit1, labels=labels[:, 0]))tf.compat.v1.summary.scalar('loss1', loss1)loss2 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit2, labels=labels[:, 1]))tf.compat.v1.summary.scalar('loss2', loss2)loss3 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit3, labels=labels[:, 2]))tf.compat.v1.summary.scalar('loss3', loss3)loss4 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit4, labels=labels[:, 3]))tf.compat.v1.summary.scalar('loss4', loss4)loss5 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit5, labels=labels[:, 4]))tf.compat.v1.summary.scalar('loss5', loss5)loss6 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit6, labels=labels[:, 5]))tf.compat.v1.summary.scalar('loss6', loss6)loss7 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logit7, labels=labels[:, 6]))tf.compat.v1.summary.scalar('loss7', loss7)return loss1, loss2, loss3, loss4, loss5, loss6, loss7def train_step(loss1, loss2, loss3, loss4, loss5, loss6, loss7, learning_rate):optimizer1 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op1 = optimizer1.minimize(loss1)optimizer2 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op2 = optimizer2.minimize(loss2)optimizer3 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op3 = optimizer3.minimize(loss3)optimizer4 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op4 = optimizer4.minimize(loss4)optimizer5 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op5 = optimizer5.minimize(loss5)optimizer6 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op6 = optimizer6.minimize(loss6)optimizer7 = tf.compat.v1.train.AdamOptimizer(learning_rate=learning_rate)train_op7 = optimizer7.minimize(loss7)return train_op1, train_op2, train_op3, train_op4, train_op5, train_op6, train_op7def pred_model(logit1, logit2, logit3, logit4, logit5, logit6, logit7, labels):labels = tf.convert_to_tensor(labels, tf.int32)labels = tf.reshape(tf.transpose(labels), [-1])logits = tf.concat([logit1, logit2, logit3, logit4, logit5, logit6, logit7], 0)prediction = tf.nn.in_top_k(logits, labels, 1)accuracy = tf.reduce_mean(tf.cast(prediction, tf.float32))tf.compat.v1.summary.scalar('accuracy', accuracy)return accuracy五、模型訓(xùn)練
import os import time import datetime import numpy as np import tensorflow as tf from input_data import OCRIter import modelos.environ["TF_CPP_MIN_LOG_LEVEL"] = '3'img_h = 72 img_w = 272 num_label = 7 batch_size = 32 epoch = 10000 learning_rate = 0.0001logs_path = 'logs\\1005' model_path = 'saved_model\\1005'image_holder = tf.compat.v1.placeholder(tf.float32, [batch_size, img_h, img_w, 3]) label_holder = tf.compat.v1.placeholder(tf.int32, [batch_size, 7]) keep_prob = tf.compat.v1.placeholder(tf.float32)def get_batch():data_batch = OCRIter(batch_size, img_h, img_w)image_batch, label_batch = data_batch.iter()return np.array(image_batch), np.array(label_batch)logit1, logit2, logit3, logit4, logit5, logit6, logit7 = model.cnn_inference(image_holder, keep_prob)loss1, loss2, loss3, loss4, loss5, loss6, loss7 = model.calc_loss(logit1, logit2, logit3, logit4, logit5, logit6, logit7, label_holder)train_op1, train_op2, train_op3, train_op4, train_op5, train_op6, train_op7 = model.train_step(loss1, loss2, loss3, loss4, loss5, loss6, loss7, learning_rate)accuracy = model.pred_model(logit1, logit2, logit3, logit4, logit5, logit6, logit7, label_holder)input_image=tf.compat.v1.summary.image('input', image_holder)summary_op = tf.compat.v1.summary.merge(tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.SUMMARIES))init_op = tf.compat.v1.global_variables_initializer()with tf.compat.v1.Session() as sess:sess.run(init_op)train_writer = tf.compat.v1.summary.FileWriter(logs_path, sess.graph)saver = tf.compat.v1.train.Saver()start_time1 = time.time()for step in range(epoch):# 生成車牌圖像以及標(biāo)簽數(shù)據(jù)img_batch, lbl_batch = get_batch()start_time2 = time.time()time_str = datetime.datetime.now().isoformat()feed_dict = {image_holder:img_batch, label_holder:lbl_batch, keep_prob:0.6}_1, _2, _3, _4, _5, _6, _7, ls1, ls2, ls3, ls4, ls5, ls6, ls7, acc = sess.run([train_op1, train_op2, train_op3, train_op4, train_op5, train_op6, train_op7, loss1, loss2, loss3, loss4, loss5, loss6, loss7, accuracy], feed_dict)summary_str = sess.run(summary_op, feed_dict)train_writer.add_summary(summary_str,step)duration = time.time() - start_time2loss_total = ls1 + ls2 + ls3 + ls4 + ls5 + ls6 + ls7if step % 10 == 0:sec_per_batch = float(duration)print('%s: Step %d, loss_total = %.2f, acc = %.2f%%, sec/batch = %.2f' %(time_str, step, loss_total, acc * 100, sec_per_batch))if step % 5000 == 0 or (step + 1) == epoch:checkpoint_path = os.path.join(model_path,'model.ckpt')saver.save(sess, checkpoint_path, global_step=step)end_time = time.time()print("Training over. It costs {:.2f} minutes".format((end_time - start_time1) / 60))六、訓(xùn)練結(jié)果展示
訓(xùn)練參數(shù):
batch_size = 32
epoch = 10000
learning_rate = 0.0001
在tensorboard中查看訓(xùn)練過程
accuracy :
accuracy曲線在epoch = 10000左右時(shí)達(dá)到收斂,最終精確度在94%左右
loss :
以上三張分別是loss1,loss2, loss7的曲線圖像,一號位字符是省份簡稱,識別相對字母數(shù)字較難,loss1=0.08左右,二號位字符是字母,loss2穩(wěn)定在0.001左右,但是隨著字符往后,loss值也將越來越大,7號位字符loss7穩(wěn)定在0.6左右。
七、預(yù)測單張車牌
import os import cv2 as cv import numpy as np import tensorflow as tf import matplotlib.pyplot as plt from PIL import Image import modelos.environ["TF_CPP_MIN_LOG_LEVEL"] = '3' # 只顯示 Errorindex = {"京": 0, "滬": 1, "津": 2, "渝": 3, "冀": 4, "晉": 5, "蒙": 6, "遼": 7, "吉": 8, "黑": 9,"蘇": 10, "浙": 11, "皖": 12, "閩": 13, "贛": 14, "魯": 15, "豫": 16, "鄂": 17, "湘": 18, "粵": 19,"桂": 20, "瓊": 21, "川": 22, "貴": 23, "云": 24, "藏": 25, "陜": 26, "甘": 27, "青": 28, "寧": 29,"新": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39,"9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49,"K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59,"V": 60, "W": 61, "X": 62, "Y": 63, "Z": 64}chars = ["京", "滬", "津", "渝", "冀", "晉", "蒙", "遼", "吉", "黑","蘇", "浙", "皖", "閩", "贛", "魯", "豫", "鄂", "湘", "粵","桂", "瓊", "川", "貴", "云", "藏", "陜", "甘", "青", "寧","新", "0", "1", "2", "3", "4", "5", "6", "7", "8","9", "A", "B", "C", "D", "E", "F", "G", "H", "J","K", "L", "M", "N", "P", "Q", "R", "S", "T", "U","V", "W", "X", "Y", "Z"]def get_one_image(test):""" 隨機(jī)獲取單張車牌圖像 """n = len(test)rand_num =np.random.randint(0,n)img_dir = test[rand_num]image_show = Image.open(img_dir)plt.imshow(image_show) # 顯示車牌圖片image = cv.imread(img_dir)image = image.reshape(72, 272, 3)image = np.multiply(image, 1 / 255.0)return imagebatch_size = 1 x = tf.compat.v1.placeholder(tf.float32, [batch_size, 72, 272, 3]) keep_prob = tf.compat.v1.placeholder(tf.float32)test_dir = 'data\\plate\\' test_image = [] for file in os.listdir(test_dir):test_image.append(test_dir + file) test_image = list(test_image)image_array = get_one_image(test_image)logit1, logit2, logit3, logit4, logit5, logit6, logit7 = model.cnn_inference(x, keep_prob)model_path = 'saved_model\\1005'saver = tf.compat.v1.train.Saver()with tf.compat.v1.Session() as sess:print ("Reading checkpoint...")ckpt = tf.train.get_checkpoint_state(model_path)if ckpt and ckpt.model_checkpoint_path:global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1]saver.restore(sess, ckpt.model_checkpoint_path)print('Loading success, global_step is %s' % global_step)else:print('No checkpoint file found')pre1, pre2, pre3, pre4, pre5, pre6, pre7 = sess.run([logit1, logit2, logit3, logit4, logit5, logit6, logit7],feed_dict={x:image_array, keep_prob:1.0})prediction = np.reshape(np.array([pre1, pre2, pre3, pre4, pre5, pre6, pre7]), [-1, 65])max_index = np.argmax(prediction, axis=1)print(max_index)line = ''result = np.array([])for i in range(prediction.shape[0]):if i == 0:result = np.argmax(prediction[i][0:31])if i == 1:result = np.argmax(prediction[i][41:65]) + 41if i > 1:result = np.argmax(prediction[i][31:65]) + 31line += chars[result]+" "print ('predicted: ' + line) plt.show()隨機(jī)測試20張車牌,18張預(yù)測正確,2張預(yù)測錯(cuò)誤,從最后兩幅預(yù)測錯(cuò)誤的圖片可以看出,模型對相似字符以及遮擋字符識別成功率仍有待提高。測試結(jié)果部分展示如下:
八、總結(jié)
本次構(gòu)建的CNN模型較為簡單,只有6卷積層+3池化層+1全連接層,可以通過增加模型深度以及每層之間的神經(jīng)元數(shù)量來優(yōu)化模型,提高識別的準(zhǔn)確率。此次訓(xùn)練數(shù)據(jù)集來源于自動(dòng)生成的車牌,由于真實(shí)的車牌圖像與生成的車牌圖像在噪聲干擾上有所區(qū)分,所以識別率上會(huì)有所出入。如果使用真實(shí)的車牌數(shù)據(jù)集,需要對車牌進(jìn)行濾波、均衡化、腐蝕、矢量量化等預(yù)處理方法。
總結(jié)
以上是生活随笔為你收集整理的Python+Tensorflow+CNN实现车牌识别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ERP软件研究
- 下一篇: Python仿黑客帝国代码雨