侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python+Tensorflow+CNN实现车牌识别的示例代码

Python  /  管理员 发布于 7年前   256

一、项目概述

本次项目目标是实现对自动生成的带有各种噪声的车牌识别。在噪声干扰情况下,车牌字符分割较困难,此次车牌识别是将车牌7个字符同时训练,字符包括31个省份简称、10个阿拉伯数字、24个英文字母('O'和'I'除外),共有65个类别,7个字符使用单独的loss函数进行训练。
(运行环境:tensorflow1.14.0-GPU版)

二、生成车牌数据集

import osimport cv2 as cvimport numpy as npfrom math import *from PIL import ImageFontfrom PIL import Imagefrom 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: 畸变的参数  :param size: 图片目标尺寸  :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 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):  """  添加自然环境的噪声, 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) * 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 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 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 dstdef 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:车牌背景图    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):    """  生成车牌string,存为图片    生成车牌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):    """    将生成的车牌图片写入文件夹,对应的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',英文及数字‘platechar.ttf'
  • 背景图:来源于不含车牌的车辆裁剪图片
  • 车牌(蓝底):template.bmp
  • 噪声图像:smu2.jpg

车牌生成后保存至plate文件夹,示例如下:

三、数据导入

from genplate import *import matplotlib.pyplot as plt# 产生用于训练的数据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))  #产生车牌开头32个省的标签    label.append(self.rand_range(41, 65))  #产生车牌第二个字母的标签    for i in range(5):      label.append(self.rand_range(31, 65))  #产生车牌后续5个字母的标签    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为标签,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模型构建

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_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

五、模型训练

import osimport timeimport datetimeimport numpy as npimport tensorflow as tffrom input_data import OCRIterimport modelos.environ["TF_CPP_MIN_LOG_LEVEL"] = '3'img_h = 72img_w = 272num_label = 7batch_size = 32epoch = 10000learning_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):    # 生成车牌图像以及标签数据    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))

六、训练结果展示

训练参数:
batch_size = 32
epoch = 10000
learning_rate = 0.0001
在tensorboard中查看训练过程
accuracy :

accuracy

曲线在epoch = 10000左右时达到收敛,最终精确度在94%左右

loss :

以上三张分别是loss1,loss2, loss7的曲线图像,一号位字符是省份简称,识别相对字母数字较难,loss1=0.08左右,二号位字符是字母,loss2稳定在0.001左右,但是随着字符往后,loss值也将越来越大,7号位字符loss7稳定在0.6左右。

七、预测单张车牌

import osimport cv2 as cvimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltfrom PIL import Imageimport 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):  """ 随机获取单张车牌图像 """  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 = 1x = 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()

随机测试20张车牌,18张预测正确,2张预测错误,从最后两幅预测错误的图片可以看出,模型对相似字符以及遮挡字符识别成功率仍有待提高。测试结果部分展示如下:

八、总结

本次构建的CNN模型较为简单,只有6卷积层+3池化层+1全连接层,可以通过增加模型深度以及每层之间的神经元数量来优化模型,提高识别的准确率。此次训练数据集来源于自动生成的车牌,由于真实的车牌图像与生成的车牌图像在噪声干扰上有所区分,所以识别率上会有所出入。如果使用真实的车牌数据集,需要对车牌进行滤波、均衡化、腐蚀、矢量量化等预处理方法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


  • 上一条:
    Python3.7 pyodbc完美配置访问access数据库
    下一条:
    命令行运行Python脚本时传入参数的三种方式详解
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客