Python+Tensorflow+CNN實(shí)現(xiàn)車(chē)牌識(shí)別的示例代碼
一、項(xiàng)目概述
本次項(xiàng)目目標(biāo)是實(shí)現(xiàn)對(duì)自動(dòng)生成的帶有各種噪聲的車(chē)牌識(shí)別。在噪聲干擾情況下,車(chē)牌字符分割較困難,此次車(chē)牌識(shí)別是將車(chē)牌7個(gè)字符同時(shí)訓(xùn)練,字符包括31個(gè)省份簡(jiǎn)稱(chēng)、10個(gè)阿拉伯?dāng)?shù)字、24個(gè)英文字母('O'和'I'除外),共有65個(gè)類(lèi)別,7個(gè)字符使用單獨(dú)的loss函數(shù)進(jìn)行訓(xùn)練。
(運(yùn)行環(huán)境:tensorflow1.14.0-GPU版)
二、生成車(chē)牌數(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 ImageDraw index = {"京": 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 img def 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 dst def rotRandrom(img, factor, size): """ 添加放射畸變 :param img: 輸入圖像 :param factor: 畸變的參數(shù) :param size: 圖片目標(biāo)尺寸 :return: 放射畸變后的圖像 """ shape = size pts1 = 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 dst def 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 img def random_envirment(img, noplate_bg): """ 添加自然環(huán)境的噪聲, noplate_bg為不含車(chē)牌的背景圖 """ 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) * 255 inv = cv.bitwise_and(bak, env) img = cv.bitwise_or(inv, img) return img def 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 A def 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 A def 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 + noise return dst def addNoise(img): # sdev = 0.5,avg=10 img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0]) img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1]) img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2]) return img class 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:車(chē)牌背景圖 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 + "\\" + filename self.noplates_path.append(path) def draw(self, val): offset = 2 self.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 * 6 self.img[0:70, base:base+23] = GenCh1(self.fontE, val[i+2]) return self.img def 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 @staticmethod def genPlateString(pos, val): """ 生成車(chē)牌string,存為圖片 生成車(chē)牌list,存為label """ plateStr = "" plateList=[] box = [0, 0, 0, 0, 0, 0, 0] if pos != -1: box[pos] = 1 for unit, cpos in zip(box, range(len(box))): if unit == 1: plateStr += val plateList.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 @staticmethod def genBatch(batchsize, outputPath, size): """ 將生成的車(chē)牌圖片寫(xiě)入文件夾,對(duì)應(yīng)的label寫(xiě)入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))
生成的車(chē)牌圖像尺寸盡量不要超過(guò)300,本次尺寸選取:272 * 72
生成車(chē)牌所需文件:
- 字體文件:中文‘platech.ttf',英文及數(shù)字‘platechar.ttf'
- 背景圖:來(lái)源于不含車(chē)牌的車(chē)輛裁剪圖片
- 車(chē)牌(藍(lán)底):template.bmp
- 噪聲圖像:smu2.jpg
車(chē)牌生成后保存至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_size self.height = height self.width = width def 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) @staticmethod def 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)生車(chē)牌開(kāi)頭32個(gè)省的標(biāo)簽 label.append(self.rand_range(41, 65)) #產(chǎn)生車(chē)牌第二個(gè)字母的標(biāo)簽 for i in range(5): label.append(self.rand_range(31, 65)) #產(chǎn)生車(chē)牌后續(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, label def 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為車(chē)牌圖像 ''' # 測(cè)試代碼 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 tf def 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_7 def 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, loss7 def 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_op7 def 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 model os.environ["TF_CPP_MIN_LOG_LEVEL"] = '3' img_h = 72 img_w = 272 num_label = 7 batch_size = 32 epoch = 10000 learning_rate = 0.0001 logs_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): # 生成車(chē)牌圖像以及標(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_time2 loss_total = ls1 + ls2 + ls3 + ls4 + ls5 + ls6 + ls7 if 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)練過(guò)程
accuracy :
accuracy
曲線在epoch = 10000左右時(shí)達(dá)到收斂,最終精確度在94%左右
loss :
以上三張分別是loss1,loss2, loss7的曲線圖像,一號(hào)位字符是省份簡(jiǎn)稱(chēng),識(shí)別相對(duì)字母數(shù)字較難,loss1=0.08左右,二號(hào)位字符是字母,loss2穩(wěn)定在0.001左右,但是隨著字符往后,loss值也將越來(lái)越大,7號(hào)位字符loss7穩(wěn)定在0.6左右。
七、預(yù)測(cè)單張車(chē)牌
import os import cv2 as cv import numpy as np import tensorflow as tf import matplotlib.pyplot as plt from PIL import Image import model os.environ["TF_CPP_MIN_LOG_LEVEL"] = '3' # 只顯示 Error index = {"京": 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ī)獲取單張車(chē)牌圖像 """ 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) # 顯示車(chē)牌圖片 image = cv.imread(img_dir) image = image.reshape(72, 272, 3) image = np.multiply(image, 1 / 255.0) return image batch_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]) + 41 if i > 1: result = np.argmax(prediction[i][31:65]) + 31 line += chars[result]+" " print ('predicted: ' + line) plt.show()
隨機(jī)測(cè)試20張車(chē)牌,18張預(yù)測(cè)正確,2張預(yù)測(cè)錯(cuò)誤,從最后兩幅預(yù)測(cè)錯(cuò)誤的圖片可以看出,模型對(duì)相似字符以及遮擋字符識(shí)別成功率仍有待提高。測(cè)試結(jié)果部分展示如下:
八、總結(jié)
本次構(gòu)建的CNN模型較為簡(jiǎn)單,只有6卷積層+3池化層+1全連接層,可以通過(guò)增加模型深度以及每層之間的神經(jīng)元數(shù)量來(lái)優(yōu)化模型,提高識(shí)別的準(zhǔn)確率。此次訓(xùn)練數(shù)據(jù)集來(lái)源于自動(dòng)生成的車(chē)牌,由于真實(shí)的車(chē)牌圖像與生成的車(chē)牌圖像在噪聲干擾上有所區(qū)分,所以識(shí)別率上會(huì)有所出入。如果使用真實(shí)的車(chē)牌數(shù)據(jù)集,需要對(duì)車(chē)牌進(jìn)行濾波、均衡化、腐蝕、矢量量化等預(yù)處理方法。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Python?CNN卷積神經(jīng)網(wǎng)絡(luò)實(shí)戰(zhàn)教程深入講解
- python人工智能tensorflow構(gòu)建卷積神經(jīng)網(wǎng)絡(luò)CNN
- Python人工智能深度學(xué)習(xí)CNN
- Python神經(jīng)網(wǎng)絡(luò)TensorFlow基于CNN卷積識(shí)別手寫(xiě)數(shù)字
- Python中人臉圖像特征提取方法(HOG、Dlib、CNN)簡(jiǎn)述
- Python搭建Keras CNN模型破解網(wǎng)站驗(yàn)證碼的實(shí)現(xiàn)
- Python實(shí)現(xiàn)CNN的多通道輸入實(shí)例
- 使用Python OpenCV為CNN增加圖像樣本的實(shí)現(xiàn)
- Python與CNN的碰撞詳解
相關(guān)文章
Python按條件刪除Excel表格數(shù)據(jù)的方法(示例詳解)
本文介紹基于Python語(yǔ)言,讀取Excel表格文件,基于我們給定的規(guī)則,對(duì)其中的數(shù)據(jù)加以篩選,將不在指定數(shù)據(jù)范圍內(nèi)的數(shù)據(jù)剔除,保留符合我們需要的數(shù)據(jù)的方法,感興趣的朋友跟隨小編一起看看吧2024-08-08Python將list保存到文件的3種方法實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Python將list保存到文件的3種方法,Python中提供了文件操作的功能,可以通過(guò)打開(kāi)和讀寫(xiě)文件實(shí)現(xiàn),文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-02-02Anaconda環(huán)境克隆、遷移的詳細(xì)步驟
最近需要在多臺(tái)計(jì)算機(jī)上工作,每次重新部署環(huán)境比較麻煩,所以學(xué)習(xí)一下anaconda環(huán)境遷移的方法,下面這篇文章主要給大家介紹了關(guān)于Anaconda環(huán)境克隆、遷移的詳細(xì)步驟,需要的朋友可以參考下2022-08-08Python的string模塊中的Template類(lèi)字符串模板用法
通過(guò)string.Template我們可以為Python定制字符串的替換標(biāo)準(zhǔn),這里我們就來(lái)通過(guò)示例解析Python的string模塊中的Template類(lèi)字符串模板用法:2016-06-06Python序列化與反序列化相關(guān)知識(shí)總結(jié)
今天給大家?guī)?lái)關(guān)于python的相關(guān)知識(shí),文章圍繞著Python序列化與反序列展開(kāi),文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06十個(gè)Python練手的實(shí)戰(zhàn)項(xiàng)目,學(xué)會(huì)這些Python就基本沒(méi)問(wèn)題了(推薦)
這篇文章主要介紹了Python實(shí)戰(zhàn)項(xiàng)目,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04python使用pandas處理大數(shù)據(jù)節(jié)省內(nèi)存技巧(推薦)
這篇文章主要介紹了python使用pandas處理大數(shù)據(jù)節(jié)省內(nèi)存技巧,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05解析pip安裝第三方庫(kù)但PyCharm中卻無(wú)法識(shí)別的問(wèn)題及PyCharm安裝第三方庫(kù)的方法教程
這篇文章主要介紹了解析pip安裝第三方庫(kù)但PyCharm中卻無(wú)法識(shí)別的問(wèn)題及PyCharm安裝第三方庫(kù)的方法教程,本文圖文并茂給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03