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


Posted in Python onOctober 11, 2019

一、项目概述

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

二、生成车牌数据集

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: 畸变的参数
  :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 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):
  """
  添加自然环境的噪声, 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 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:车牌背景图
    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文件夹,示例如下:

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

三、数据导入

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

五、模型训练

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):
    # 生成车牌图像以及标签数据
    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 :

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

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

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

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

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

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

七、预测单张车牌

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):
  """ 随机获取单张车牌图像 """
  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 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()

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

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

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

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

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

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

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

八、总结

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

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

Python 相关文章推荐
python写xml文件的操作实例
Oct 05 Python
python定时利用QQ邮件发送天气预报的实例
Nov 17 Python
python文本数据相似度的度量
Mar 12 Python
PyQt5每天必学之组合框
Apr 20 Python
python中int与str互转方法
Jul 02 Python
详解python 模拟豆瓣登录(豆瓣6.0)
Apr 18 Python
pyqt 实现QlineEdit 输入密码显示成圆点的方法
Jun 24 Python
python实现连连看游戏
Feb 14 Python
python简单的三元一次方程求解实例
Apr 02 Python
python 实现任务管理清单案例
Apr 25 Python
Django models文件模型变更错误解决
May 11 Python
Python读取pdf表格写入excel的方法
Jan 22 Python
命令行运行Python脚本时传入参数的三种方式详解
Oct 11 #Python
python中对_init_的理解及实例解析
Oct 11 #Python
pandas数据处理进阶详解
Oct 11 #Python
结合OpenCV与TensorFlow进行人脸识别的实现
Oct 10 #Python
树莓派安装OpenCV3完整过程的实现
Oct 10 #Python
树莓派极简安装OpenCv的方法步骤
Oct 10 #Python
python 利用jinja2模板生成html代码实例
Oct 10 #Python
You might like
PHP与SQL注入攻击[二]
2007/04/17 PHP
PHP实现异步调用方法研究与分享
2011/10/27 PHP
php通过array_merge()函数合并关联和非关联数组的方法
2015/03/18 PHP
PHP实现简易图形计算器
2020/08/28 PHP
一端时间轮换的广告
2006/06/26 Javascript
JavaScript高级程序设计(第3版)学习笔记4 js运算符和操作符
2012/10/11 Javascript
javaScript中两个等于号和三个等于号之间的区别介绍
2014/06/27 Javascript
jquery validate.js表单验证入门实例(附源码)
2015/11/10 Javascript
JavaScript 实现的checkbox经典实例分享
2016/10/16 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
2016/12/02 Javascript
JS获得多个同name 的input输入框的值的实现方法
2017/01/09 Javascript
详解微信小程序开发之城市选择器 城市切换
2017/01/17 Javascript
javascript将url解析为json格式的两种方法
2017/08/18 Javascript
解决VUE中document.body.scrollTop为0的问题
2018/09/15 Javascript
利用hasOwnProperty给数组去重的面试题分享
2018/11/05 Javascript
NestJs 静态目录配置详解
2019/03/12 Javascript
Vue + Elementui实现多标签页共存的方法
2019/06/12 Javascript
jquery实现聊天机器人
2020/02/08 jQuery
JS实现iframe中子父页面跨域通讯的方法分析
2020/03/10 Javascript
vue跳转页面的几种方法(推荐)
2020/03/26 Javascript
[02:15]你好,这就是DOTA!
2015/08/05 DOTA
Python中str.format()详解
2017/03/12 Python
Python实现的删除重复文件或图片功能示例【去重】
2019/04/23 Python
python异常处理、自定义异常、断言原理与用法分析
2020/03/23 Python
基于Python绘制美观动态圆环图、饼图
2020/06/03 Python
HTML5对手机页面长按会粘贴复制禁用的解决方法
2016/07/19 HTML / CSS
美国花布包包品牌:Vera Bradley
2017/08/11 全球购物
梵蒂冈和罗马卡:Omnia Card Pass
2018/02/10 全球购物
Nordgreen手表德国官方网站:丹麦极简主义手表
2019/10/31 全球购物
C语言编程练习
2012/04/02 面试题
春节超市活动方案
2014/08/14 职场文书
2014年数学教师工作总结
2014/12/03 职场文书
2016春季运动会通讯稿
2015/07/18 职场文书
Netty结合Protobuf进行编解码的方法
2021/06/26 Java/Android
idea下配置tomcat避坑详解
2022/04/12 Servers
Win11使用CAD卡顿或者致命错误怎么办?Win11无法正常使用CAD的解决方法
2022/07/23 数码科技