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的Twisted框架中使用Deferred对象来管理回调函数
May 25 Python
python实现FTP服务器服务的方法
Apr 11 Python
Python实现判断一个整数是否为回文数算法示例
Mar 02 Python
详解Python3中setuptools、Pip安装教程
Jun 18 Python
django的auth认证,authenticate和装饰器功能详解
Jul 25 Python
python实现车牌识别的示例代码
Aug 05 Python
用python3 urllib破解有道翻译反爬虫机制详解
Aug 14 Python
python next()和iter()函数原理解析
Feb 07 Python
python批量修改xml属性的实现方式
Mar 05 Python
Python 利用OpenCV给照片换底色的示例代码
Aug 03 Python
python基于tkinter制作m3u8视频下载工具
Apr 24 Python
解决Tkinter中button按钮未按却主动执行command函数的问题
May 23 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文件服务实现虚拟挂载其他目录示例
2014/04/17 PHP
PHP批量去除BOM头代码分享
2015/06/26 PHP
php利用imagemagick实现复古老照片效果实例
2017/02/16 PHP
php中Redis的应用--消息传递
2017/03/28 PHP
php微信公众号开发之现金红包
2018/04/16 PHP
JS画线(实例代码)
2013/11/20 Javascript
jquery浏览器滚动加载技术实现方案
2014/06/03 Javascript
javascript使用正则表达式检测IP地址
2014/12/03 Javascript
jquery实现textarea 高度自适应
2015/03/11 Javascript
jQuery Form 表单提交插件之formSerialize,fieldSerialize,fieldValue,resetForm,clearForm,clearFields的应用
2016/01/23 Javascript
JavaScript实现设计模式中的单例模式的一些技巧总结
2016/05/17 Javascript
Javascript打印局部页面实例
2016/06/21 Javascript
jQuery简单自定义图片轮播插件及用法示例
2016/11/21 Javascript
非常实用的vue导航钩子
2017/03/20 Javascript
JavaScript数据结构中栈的应用之表达式求值问题详解
2017/04/11 Javascript
详解vue-router基本使用
2017/04/18 Javascript
vue2 router 动态传参,多个参数的实例
2017/11/10 Javascript
layui框架中layer父子页面交互的方法分析
2017/11/15 Javascript
JavaScript+H5实现微信摇一摇功能
2018/05/23 Javascript
详解vue的数据劫持以及操作数组的坑
2019/04/18 Javascript
理解JavaScript中的Proxy 与 Reflection API
2020/09/21 Javascript
[01:11]辉夜杯战队访谈宣传片—CDEC.Y
2015/12/26 DOTA
[44:33]EG vs Liquid 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
通过python下载FTP上的文件夹的实现代码
2013/02/10 Python
Python实现模拟浏览器请求及会话保持操作示例
2018/07/30 Python
代码实例讲解python3的编码问题
2019/07/08 Python
英国最受欢迎的价格比较网站之一:MoneySuperMarket
2018/12/19 全球购物
W Hamond官网:始于1979年的钻石专家
2020/07/20 全球购物
yy生日主持词
2014/03/20 职场文书
三孔导游词
2015/02/05 职场文书
劳资员岗位职责
2015/02/13 职场文书
施工单位工程部经理岗位职责
2015/04/09 职场文书
三八妇女节主持词
2015/07/04 职场文书
2016学校先进集体事迹材料
2016/02/29 职场文书
只需要100行Python代码就可以实现的贪吃蛇小游戏
2021/05/27 Python
排查并解决MySQL生产库内存使用率高的报警
2022/04/11 MySQL