TensorFlow车牌识别完整版代码(含车牌数据集)


Posted in Python onAugust 05, 2019

在之前发布的一篇博文《MNIST数据集实现车牌识别--初步演示版》中,我们演示了如何使用TensorFlow进行车牌识别,但是,当时采用的数据集是MNIST数字手写体,只能分类0-9共10个数字,无法分类省份简称和字母,局限性较大,无实际意义。

经过图像定位分割处理,博主收集了相关省份简称和26个字母的图片数据集,结合前述博文中贴出的python+TensorFlow代码,实现了完整的车牌识别功能。本着分享精神,在此送上全部代码和车牌数据集。

车牌数据集下载地址(约4000张图片):tf_car_license_dataset_3water.rar

省份简称训练+识别代码(保存文件名为train-license-province.py)(拷贝代码请务必注意python文本缩进,只要有一处缩进错误,就无法得到正确结果,或者出现异常):

#!/usr/bin/python3.5
# -*- coding: utf-8 -*- 
 
import sys
import os
import time
import random
 
import numpy as np
import tensorflow as tf
 
from PIL import Image
 
 
SIZE = 1280
WIDTH = 32
HEIGHT = 40
NUM_CLASSES = 6
iterations = 300
 
SAVER_DIR = "train-saver/province/"
 
PROVINCES = ("京","闽","粤","苏","沪","浙")
nProvinceIndex = 0
 
time_begin = time.time()
 
 
# 定义输入节点,对应于图片像素值矩阵集合和图片标签(即所代表的数字)
x = tf.placeholder(tf.float32, shape=[None, SIZE])
y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])
 
x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])
 
 
# 定义卷积函数
def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):
  L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)
  L1_relu = tf.nn.relu(L1_conv + b)
  return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')
 
# 定义全连接层函数
def full_connect(inputs, W, b):
  return tf.nn.relu(tf.matmul(inputs, W) + b)
 
 
if __name__ =='__main__' and sys.argv[1]=='train':
  # 第一次遍历图片目录是为了获取图片总数
  input_count = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/training-set/chinese-characters/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        input_count += 1
 
  # 定义对应维数和各维长度的数组
  input_images = np.array([[0]*SIZE for i in range(input_count)])
  input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/training-set/chinese-characters/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              input_images[index][w+h*width] = 0
            else:
              input_images[index][w+h*width] = 1
        input_labels[index][i] = 1
        index += 1
 
  # 第一次遍历图片目录是为了获取图片总数
  val_count = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/validation-set/chinese-characters/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        val_count += 1
 
  # 定义对应维数和各维长度的数组
  val_images = np.array([[0]*SIZE for i in range(val_count)])
  val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/validation-set/chinese-characters/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              val_images[index][w+h*width] = 0
            else:
              val_images[index][w+h*width] = 1
        val_labels[index][i] = 1
        index += 1
  
  with tf.Session() as sess:
    # 第一个卷积层
    W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")
    b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")
    b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")
    b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")
    b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")
 
    # 定义优化器和训练op
    y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)
 
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
 
    # 初始化saver
    saver = tf.train.Saver()
 
    sess.run(tf.global_variables_initializer())
 
    time_elapsed = time.time() - time_begin
    print("读取图片文件耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    print ("一共读取了 %s 个训练图像, %s 个标签" % (input_count, input_count))
 
    # 设置每次训练op的输入个数和迭代次数,这里为了支持任意图片总数,定义了一个余数remainder,譬如,如果每次训练op的输入个数为60,图片总数为150张,则前面两次各输入60张,最后一次输入30张(余数30)
    batch_size = 60
    iterations = iterations
    batches_count = int(input_count / batch_size)
    remainder = input_count % batch_size
    print ("训练数据集分成 %s 批, 前面每批 %s 个数据,最后一批 %s 个数据" % (batches_count+1, batch_size, remainder))
 
    # 执行训练迭代
    for it in range(iterations):
      # 这里的关键是要把输入数组转为np.array
      for n in range(batches_count):
        train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})
      if remainder > 0:
        start_index = batches_count * batch_size;
        train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})
 
      # 每完成五次迭代,判断准确度是否已达到100%,达到则退出迭代循环
      iterate_accuracy = 0
      if it%5 == 0:
        iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})
        print ('第 %d 次训练迭代: 准确率 %0.5f%%' % (it, iterate_accuracy*100))
        if iterate_accuracy >= 0.9999 and it >= 150:
          break;
 
    print ('完成训练!')
    time_elapsed = time.time() - time_begin
    print ("训练耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    # 保存训练结果
    if not os.path.exists(SAVER_DIR):
      print ('不存在训练数据保存目录,现在创建保存目录')
      os.makedirs(SAVER_DIR)
    saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))
 
 
 
if __name__ =='__main__' and sys.argv[1]=='predict':
  saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))
  with tf.Session() as sess:
    model_file=tf.train.latest_checkpoint(SAVER_DIR)
    saver.restore(sess, model_file)
 
    # 第一个卷积层
    W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")
    b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")
    b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")
    b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")
    b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")
 
    # 定义优化器和训练op
    conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
 
    for n in range(1,2):
      path = "test_images/%s.bmp" % (n)
      img = Image.open(path)
      width = img.size[0]
      height = img.size[1]
 
      img_data = [[0]*SIZE for i in range(1)]
      for h in range(0, height):
        for w in range(0, width):
          if img.getpixel((w, h)) < 190:
            img_data[0][w+h*width] = 1
          else:
            img_data[0][w+h*width] = 0
      
      result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})
      max1 = 0
      max2 = 0
      max3 = 0
      max1_index = 0
      max2_index = 0
      max3_index = 0
      for j in range(NUM_CLASSES):
        if result[0][j] > max1:
          max1 = result[0][j]
          max1_index = j
          continue
        if (result[0][j]>max2) and (result[0][j]<=max1):
          max2 = result[0][j]
          max2_index = j
          continue
        if (result[0][j]>max3) and (result[0][j]<=max2):
          max3 = result[0][j]
          max3_index = j
          continue
      
      nProvinceIndex = max1_index
      print ("概率: [%s %0.2f%%]  [%s %0.2f%%]  [%s %0.2f%%]" % (PROVINCES[max1_index],max1*100, PROVINCES[max2_index],max2*100, PROVINCES[max3_index],max3*100))
      
    print ("省份简称是: %s" % PROVINCES[nProvinceIndex])

城市代号训练+识别代码(保存文件名为train-license-letters.py):

#!/usr/bin/python3.5
# -*- coding: utf-8 -*- 
 
import sys
import os
import time
import random
 
import numpy as np
import tensorflow as tf
 
from PIL import Image
 
 
SIZE = 1280
WIDTH = 32
HEIGHT = 40
NUM_CLASSES = 26
iterations = 500
 
SAVER_DIR = "train-saver/letters/"
 
LETTERS_DIGITS = ("A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","I","O")
license_num = ""
 
time_begin = time.time()
 
 
# 定义输入节点,对应于图片像素值矩阵集合和图片标签(即所代表的数字)
x = tf.placeholder(tf.float32, shape=[None, SIZE])
y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])
 
x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])
 
 
# 定义卷积函数
def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):
  L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)
  L1_relu = tf.nn.relu(L1_conv + b)
  return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')
 
# 定义全连接层函数
def full_connect(inputs, W, b):
  return tf.nn.relu(tf.matmul(inputs, W) + b)
 
 
if __name__ =='__main__' and sys.argv[1]=='train':
  # 第一次遍历图片目录是为了获取图片总数
  input_count = 0
  for i in range(0+10,NUM_CLASSES+10):
    dir = './train_images/training-set/letters/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        input_count += 1
 
  # 定义对应维数和各维长度的数组
  input_images = np.array([[0]*SIZE for i in range(input_count)])
  input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0+10,NUM_CLASSES+10):
    dir = './train_images/training-set/letters/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              input_images[index][w+h*width] = 0
            else:
              input_images[index][w+h*width] = 1
        #print ("i=%d, index=%d" % (i, index))
        input_labels[index][i-10] = 1
        index += 1
 
  # 第一次遍历图片目录是为了获取图片总数
  val_count = 0
  for i in range(0+10,NUM_CLASSES+10):
    dir = './train_images/validation-set/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        val_count += 1
 
  # 定义对应维数和各维长度的数组
  val_images = np.array([[0]*SIZE for i in range(val_count)])
  val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0+10,NUM_CLASSES+10):
    dir = './train_images/validation-set/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              val_images[index][w+h*width] = 0
            else:
              val_images[index][w+h*width] = 1
        val_labels[index][i-10] = 1
        index += 1
  
  with tf.Session() as sess:
    # 第一个卷积层
    W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")
    b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")
    b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")
    b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")
    b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")
 
    # 定义优化器和训练op
    y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)
 
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
 
    sess.run(tf.global_variables_initializer())
 
    time_elapsed = time.time() - time_begin
    print("读取图片文件耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    print ("一共读取了 %s 个训练图像, %s 个标签" % (input_count, input_count))
 
    # 设置每次训练op的输入个数和迭代次数,这里为了支持任意图片总数,定义了一个余数remainder,譬如,如果每次训练op的输入个数为60,图片总数为150张,则前面两次各输入60张,最后一次输入30张(余数30)
    batch_size = 60
    iterations = iterations
    batches_count = int(input_count / batch_size)
    remainder = input_count % batch_size
    print ("训练数据集分成 %s 批, 前面每批 %s 个数据,最后一批 %s 个数据" % (batches_count+1, batch_size, remainder))
 
    # 执行训练迭代
    for it in range(iterations):
      # 这里的关键是要把输入数组转为np.array
      for n in range(batches_count):
        train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})
      if remainder > 0:
        start_index = batches_count * batch_size;
        train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})
 
      # 每完成五次迭代,判断准确度是否已达到100%,达到则退出迭代循环
      iterate_accuracy = 0
      if it%5 == 0:
        iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})
        print ('第 %d 次训练迭代: 准确率 %0.5f%%' % (it, iterate_accuracy*100))
        if iterate_accuracy >= 0.9999 and it >= iterations:
          break;
 
    print ('完成训练!')
    time_elapsed = time.time() - time_begin
    print ("训练耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    # 保存训练结果
    if not os.path.exists(SAVER_DIR):
      print ('不存在训练数据保存目录,现在创建保存目录')
      os.makedirs(SAVER_DIR)
    # 初始化saver
    saver = tf.train.Saver()      
    saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))
 
 
 
if __name__ =='__main__' and sys.argv[1]=='predict':
  saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))
  with tf.Session() as sess:
    model_file=tf.train.latest_checkpoint(SAVER_DIR)
    saver.restore(sess, model_file)
 
    # 第一个卷积层
    W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")
    b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")
    b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")
    b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")
    b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")
 
    # 定义优化器和训练op
    conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
 
    for n in range(2,3):
      path = "test_images/%s.bmp" % (n)
      img = Image.open(path)
      width = img.size[0]
      height = img.size[1]
 
      img_data = [[0]*SIZE for i in range(1)]
      for h in range(0, height):
        for w in range(0, width):
          if img.getpixel((w, h)) < 190:
            img_data[0][w+h*width] = 1
          else:
            img_data[0][w+h*width] = 0
      
      result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})
      
      max1 = 0
      max2 = 0
      max3 = 0
      max1_index = 0
      max2_index = 0
      max3_index = 0
      for j in range(NUM_CLASSES):
        if result[0][j] > max1:
          max1 = result[0][j]
          max1_index = j
          continue
        if (result[0][j]>max2) and (result[0][j]<=max1):
          max2 = result[0][j]
          max2_index = j
          continue
        if (result[0][j]>max3) and (result[0][j]<=max2):
          max3 = result[0][j]
          max3_index = j
          continue
      
      if n == 3:
        license_num += "-"
      license_num = license_num + LETTERS_DIGITS[max1_index]
      print ("概率: [%s %0.2f%%]  [%s %0.2f%%]  [%s %0.2f%%]" % (LETTERS_DIGITS[max1_index],max1*100, LETTERS_DIGITS[max2_index],max2*100, LETTERS_DIGITS[max3_index],max3*100))
      
    print ("城市代号是: 【%s】" % license_num)

车牌编号训练+识别代码(保存文件名为train-license-digits.py):

#!/usr/bin/python3.5
# -*- coding: utf-8 -*- 
 
import sys
import os
import time
import random
 
import numpy as np
import tensorflow as tf
 
from PIL import Image
 
 
SIZE = 1280
WIDTH = 32
HEIGHT = 40
NUM_CLASSES = 34
iterations = 1000
 
SAVER_DIR = "train-saver/digits/"
 
LETTERS_DIGITS = ("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")
license_num = ""
 
time_begin = time.time()
 
 
# 定义输入节点,对应于图片像素值矩阵集合和图片标签(即所代表的数字)
x = tf.placeholder(tf.float32, shape=[None, SIZE])
y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])
 
x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])
 
 
# 定义卷积函数
def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):
  L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)
  L1_relu = tf.nn.relu(L1_conv + b)
  return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')
 
# 定义全连接层函数
def full_connect(inputs, W, b):
  return tf.nn.relu(tf.matmul(inputs, W) + b)
 
 
if __name__ =='__main__' and sys.argv[1]=='train':
  # 第一次遍历图片目录是为了获取图片总数
  input_count = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/training-set/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        input_count += 1
 
  # 定义对应维数和各维长度的数组
  input_images = np.array([[0]*SIZE for i in range(input_count)])
  input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/training-set/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              input_images[index][w+h*width] = 0
            else:
              input_images[index][w+h*width] = 1
        input_labels[index][i] = 1
        index += 1
 
  # 第一次遍历图片目录是为了获取图片总数
  val_count = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/validation-set/%s/' % i      # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        val_count += 1
 
  # 定义对应维数和各维长度的数组
  val_images = np.array([[0]*SIZE for i in range(val_count)])
  val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])
 
  # 第二次遍历图片目录是为了生成图片数据和标签
  index = 0
  for i in range(0,NUM_CLASSES):
    dir = './train_images/validation-set/%s/' % i     # 这里可以改成你自己的图片目录,i为分类标签
    for rt, dirs, files in os.walk(dir):
      for filename in files:
        filename = dir + filename
        img = Image.open(filename)
        width = img.size[0]
        height = img.size[1]
        for h in range(0, height):
          for w in range(0, width):
            # 通过这样的处理,使数字的线条变细,有利于提高识别准确率
            if img.getpixel((w, h)) > 230:
              val_images[index][w+h*width] = 0
            else:
              val_images[index][w+h*width] = 1
        val_labels[index][i] = 1
        index += 1
  
  with tf.Session() as sess:
    # 第一个卷积层
    W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")
    b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")
    b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")
    b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")
    b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")
 
    # 定义优化器和训练op
    y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
    cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)
 
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
 
    sess.run(tf.global_variables_initializer())
 
    time_elapsed = time.time() - time_begin
    print("读取图片文件耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    print ("一共读取了 %s 个训练图像, %s 个标签" % (input_count, input_count))
 
    # 设置每次训练op的输入个数和迭代次数,这里为了支持任意图片总数,定义了一个余数remainder,譬如,如果每次训练op的输入个数为60,图片总数为150张,则前面两次各输入60张,最后一次输入30张(余数30)
    batch_size = 60
    iterations = iterations
    batches_count = int(input_count / batch_size)
    remainder = input_count % batch_size
    print ("训练数据集分成 %s 批, 前面每批 %s 个数据,最后一批 %s 个数据" % (batches_count+1, batch_size, remainder))
 
    # 执行训练迭代
    for it in range(iterations):
      # 这里的关键是要把输入数组转为np.array
      for n in range(batches_count):
        train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})
      if remainder > 0:
        start_index = batches_count * batch_size;
        train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})
 
      # 每完成五次迭代,判断准确度是否已达到100%,达到则退出迭代循环
      iterate_accuracy = 0
      if it%5 == 0:
        iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})
        print ('第 %d 次训练迭代: 准确率 %0.5f%%' % (it, iterate_accuracy*100))
        if iterate_accuracy >= 0.9999 and it >= iterations:
          break;
 
    print ('完成训练!')
    time_elapsed = time.time() - time_begin
    print ("训练耗费时间:%d秒" % time_elapsed)
    time_begin = time.time()
 
    # 保存训练结果
    if not os.path.exists(SAVER_DIR):
      print ('不存在训练数据保存目录,现在创建保存目录')
      os.makedirs(SAVER_DIR)
    # 初始化saver
    saver = tf.train.Saver()      
    saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))
 
 
 
if __name__ =='__main__' and sys.argv[1]=='predict':
  saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))
  with tf.Session() as sess:
    model_file=tf.train.latest_checkpoint(SAVER_DIR)
    saver.restore(sess, model_file)
 
    # 第一个卷积层
    W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")
    b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 2, 2, 1]
    pool_strides = [1, 2, 2, 1]
    L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')
 
    # 第二个卷积层
    W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")
    b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")
    conv_strides = [1, 1, 1, 1]
    kernel_size = [1, 1, 1, 1]
    pool_strides = [1, 1, 1, 1]
    L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')
 
 
    # 全连接层
    W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")
    b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")
    h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])
    h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)
 
 
    # dropout
    keep_prob = tf.placeholder(tf.float32)
 
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 
 
    # readout层
    W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")
    b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")
 
    # 定义优化器和训练op
    conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
 
    for n in range(3,8):
      path = "test_images/%s.bmp" % (n)
      img = Image.open(path)
      width = img.size[0]
      height = img.size[1]
 
      img_data = [[0]*SIZE for i in range(1)]
      for h in range(0, height):
        for w in range(0, width):
          if img.getpixel((w, h)) < 190:
            img_data[0][w+h*width] = 1
          else:
            img_data[0][w+h*width] = 0
      
      result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})
      
      max1 = 0
      max2 = 0
      max3 = 0
      max1_index = 0
      max2_index = 0
      max3_index = 0
      for j in range(NUM_CLASSES):
        if result[0][j] > max1:
          max1 = result[0][j]
          max1_index = j
          continue
        if (result[0][j]>max2) and (result[0][j]<=max1):
          max2 = result[0][j]
          max2_index = j
          continue
        if (result[0][j]>max3) and (result[0][j]<=max2):
          max3 = result[0][j]
          max3_index = j
          continue
      
      license_num = license_num + LETTERS_DIGITS[max1_index]
      print ("概率: [%s %0.2f%%]  [%s %0.2f%%]  [%s %0.2f%%]" % (LETTERS_DIGITS[max1_index],max1*100, LETTERS_DIGITS[max2_index],max2*100, LETTERS_DIGITS[max3_index],max3*100))
      
    print ("车牌编号是: 【%s】" % license_num)

保存好上面三个python脚本后,我们首先进行省份简称训练。在运行代码之前,需要先把数据集解压到训练脚本所在目录。然后,在命令行中进入脚本所在目录,输入执行如下命令:

python train-license-province.py train

训练结果如下:

TensorFlow车牌识别完整版代码(含车牌数据集)

然后进行省份简称识别,在命令行输入执行如下命令:

python train-license-province.py predict

TensorFlow车牌识别完整版代码(含车牌数据集)

执行城市代号训练(相当于训练26个字母):

python train-license-letters.py train

TensorFlow车牌识别完整版代码(含车牌数据集)

识别城市代号:

python train-license-letters.py predict

TensorFlow车牌识别完整版代码(含车牌数据集)

执行车牌编号训练(相当于训练24个字母+10个数字,我国交通法规规定车牌编号中不包含字母I和O):

python train-license-digits.py train

TensorFlow车牌识别完整版代码(含车牌数据集)

识别车牌编号:

python train-license-digits.py predict

TensorFlow车牌识别完整版代码(含车牌数据集)

可以看到,在测试图片上,识别准确率很高。识别结果是闽O-1672Q。

下图是测试图片的车牌原图:

TensorFlow车牌识别完整版代码(含车牌数据集)

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

Python 相关文章推荐
Python原始字符串(raw strings)用法实例
Oct 13 Python
python使用PythonMagick将jpg图片转换成ico图片的方法
Mar 26 Python
用Python解析XML的几种常见方法的介绍
Apr 09 Python
Python中异常重试的解决方案详解
May 05 Python
python登录并爬取淘宝信息代码示例
Dec 09 Python
Python创建普通菜单示例【基于win32ui模块】
May 09 Python
python 使用 requests 模块发送http请求 的方法
Dec 09 Python
python 图像平移和旋转的实例
Jan 10 Python
python django下载大的csv文件实现方法分析
Jul 19 Python
python 工具 字符串转numpy浮点数组的实现
Mar 14 Python
Python利用PyPDF2库获取PDF文件总页码实例
Apr 03 Python
PIP和conda 更换国内安装源的方法步骤
Sep 21 Python
TensorFlow基于MNIST数据集实现车牌识别(初步演示版)
Aug 05 #Python
Django应用程序入口WSGIHandler源码解析
Aug 05 #Python
详解如何用TensorFlow训练和识别/分类自定义图片
Aug 05 #Python
详解如何从TensorFlow的mnist数据集导出手写体数字图片
Aug 05 #Python
Python获取时间范围内日期列表和周列表的函数
Aug 05 #Python
Django ORM 查询管理器源码解析
Aug 05 #Python
python实现车牌识别的示例代码
Aug 05 #Python
You might like
Zend的MVC机制使用分析(二)
2013/05/02 PHP
php判断页面是否是微信打开的示例(微信打开网页)
2014/04/25 PHP
php语言的7种基本的排序方法
2020/12/28 PHP
php使用gd2绘制基本图形示例(直线、圆、正方形)
2017/02/15 PHP
PHP中类的自动加载的方法
2017/03/17 PHP
PHP 进度条函数的简单实例
2017/09/19 PHP
PHP+Oracle本地开发环境搭建方法详解
2019/04/01 PHP
浅析PHP 中move_uploaded_file 上传中文文件名失败
2019/04/17 PHP
IE 条件注释详解总结(附实例代码)
2009/08/29 Javascript
JS获取页面窗口大小的代码解读
2011/12/01 Javascript
JavaScript获取表单内所有元素值的方法
2015/04/02 Javascript
简介JavaScript中的setDate()方法的使用
2015/06/11 Javascript
基于javascript实现漂亮的页面过渡动画效果附源码下载
2015/10/26 Javascript
JavaScript_ECMA5数组新特性详解
2016/06/12 Javascript
jquery动态遍历Json对象的属性和值的方法
2016/07/27 Javascript
Angular.JS实现无限级的联动菜单(使用demo)
2017/02/08 Javascript
node.js学习之事件模块Events的使用示例
2017/09/28 Javascript
使用Vue-Router 2实现路由功能实例详解
2017/11/14 Javascript
Vue侧滑菜单组件——DrawerLayout
2017/12/18 Javascript
Angular ElementRef简介及其使用
2018/10/01 Javascript
javascript递归函数定义和用法示例分析
2020/07/22 Javascript
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
解决pip install的时候报错timed out的问题
2018/06/12 Python
python实现海螺图片的方法示例
2019/05/12 Python
python实现视频分帧效果
2019/05/31 Python
Python实现简单的列表冒泡排序和反转列表操作示例
2019/07/10 Python
Python Selenium 之数据驱动测试的实现
2019/08/01 Python
python实现的爬取电影下载链接功能示例
2019/08/26 Python
python实现代码统计器
2019/09/19 Python
Python开发入门——迭代的基本使用
2020/09/03 Python
亚马逊海外购:亚马逊美国、英国、日本、德国直邮
2021/03/18 全球购物
马云的职业生涯规划之路
2014/01/01 职场文书
大学同学会活动方案
2014/08/20 职场文书
销售2014年度工作总结
2014/12/08 职场文书
公司租车协议书
2015/01/29 职场文书
小学见习报告
2015/06/23 职场文书