1. 程式人生 > 程式設計 >Python+Tensorflow+CNN實現車牌識別的示例程式碼

Python+Tensorflow+CNN實現車牌識別的示例程式碼

一、專案概述

本次專案目標是實現對自動生成的帶有各種噪聲的車牌識別。在噪聲干擾情況下,車牌字元分割較困難,此次車牌識別是將車牌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))
  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],size_o[1]]])
  if angel > 0:
    pts2 = np.float32([[interval,size[1]],[size[0],[size[0] - interval,size_o[1]]])
  else:
    pts2 = np.float32([[0,[interval,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,shape[0]],[shape[1],shape[0]]])
  pts2 = np.float32([[r(factor),r(factor)],[r(factor),shape[0] - r(factor)],[shape[1] - r(factor),shape[0] - r(factor)]])
  M = cv.getPerspectiveTransform(pts1,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),font=f)
  img = img.resize((23,70))
  A = np.array(img)
  return A


def GenCh1(f,val):
  """
  生成英文字元
  """
  img =Image.new("RGB",(23,2),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,255)))
    self.bg = cv.resize(cv.imread("data\\images\\template.bmp"),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]
    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\\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,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,32],stddev=0.1)),'conv2': tf.Variable(tf.random.truncated_normal([3,32,'conv3': tf.Variable(tf.random.truncated_normal([3,64],'conv4': tf.Variable(tf.random.truncated_normal([3,64,'conv5': tf.Variable(tf.random.truncated_normal([3,128],'conv6': tf.Variable(tf.random.truncated_normal([3,128,'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,'fc1_3': tf.Variable(tf.random.truncated_normal([5*30*128,'fc1_4': tf.Variable(tf.random.truncated_normal([5*30*128,'fc1_5': tf.Variable(tf.random.truncated_normal([5*30*128,'fc1_6': tf.Variable(tf.random.truncated_normal([5*30*128,'fc1_7': tf.Variable(tf.random.truncated_normal([5*30*128,} 

  b_conv = { 
    'conv1': tf.Variable(tf.constant(0.1,dtype=tf.float32,shape=[32])),'conv2': tf.Variable(tf.constant(0.1,'conv3': tf.Variable(tf.constant(0.1,shape=[64])),'conv4': tf.Variable(tf.constant(0.1,'conv5': tf.Variable(tf.constant(0.1,shape=[128])),'conv6': tf.Variable(tf.constant(0.1,'fc1_1': tf.Variable(tf.constant(0.1,shape=[65])),'fc1_2': tf.Variable(tf.constant(0.1,'fc1_3': tf.Variable(tf.constant(0.1,'fc1_4': tf.Variable(tf.constant(0.1,'fc1_5': tf.Variable(tf.constant(0.1,'fc1_6': tf.Variable(tf.constant(0.1,'fc1_7': tf.Variable(tf.constant(0.1,} 


  # 第1層卷積層
  conv1 = tf.nn.conv2d(images,W_conv['conv1'],strides=[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'],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,padding='VALID')
 
  # 第3層卷積層
  conv3 = tf.nn.conv2d(pool1,W_conv['conv3'],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'],padding='VALID')
  conv4 = tf.nn.bias_add(conv4,b_conv['conv4'])
  conv4 = tf.nn.relu(conv4)
  # 第2層池化層
  pool2 = tf.nn.max_pool2d(conv4,padding='VALID')

  # 第5層卷積層
  conv5 = tf.nn.conv2d(pool2,W_conv['conv5'],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'],padding='VALID')
  conv6 = tf.nn.bias_add(conv6,b_conv['conv6'])
  conv6 = tf.nn.relu(conv6)
  # 第3層池化層
  pool3 = tf.nn.max_pool2d(conv6,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,1]))
  tf.compat.v1.summary.scalar('loss2',loss2)

  loss3 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logit3,2]))
  tf.compat.v1.summary.scalar('loss3',loss3)

  loss4 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logit4,3]))
  tf.compat.v1.summary.scalar('loss4',loss4)

  loss5 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logit5,4]))
  tf.compat.v1.summary.scalar('loss5',loss5)

  loss6 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logit6,5]))
  tf.compat.v1.summary.scalar('loss6',loss6)

  loss7 = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(
    logits=logit7,6]))
  tf.compat.v1.summary.scalar('loss7',loss7)

  return loss1,loss2,loss3,loss4,loss5,loss6,loss7


def train_step(loss1,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,tf.int32)
  labels = tf.reshape(tf.transpose(labels),[-1])
  logits = tf.concat([logit1,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,7])
keep_prob = tf.compat.v1.placeholder(tf.float32)


def get_batch():
  data_batch = OCRIter(batch_size,img_w)
  image_batch,label_batch = data_batch.iter()
  return np.array(image_batch),np.array(label_batch)


logit1,logit7 = model.cnn_inference(
  image_holder,keep_prob)

loss1,loss7 = model.calc_loss(
  logit1,label_holder)

train_op1,train_op7 = model.train_step(
  loss1,learning_rate)

accuracy = model.pred_model(logit1,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_op7,loss1,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 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,"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,3)
  image = np.multiply(image,1 / 255.0)
  return image

batch_size = 1
x = tf.compat.v1.placeholder(tf.float32,72,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,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,feed_dict={x:image_array,keep_prob:1.0})
  prediction = np.reshape(np.array([pre1,pre7]),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全連線層,可以通過增加模型深度以及每層之間的神經元數量來優化模型,提高識別的準確率。此次訓練資料集來源於自動生成的車牌,由於真實的車牌影象與生成的車牌影象在噪聲干擾上有所區分,所以識別率上會有所出入。如果使用真實的車牌資料集,需要對車牌進行濾波、均衡化、腐蝕、向量量化等預處理方法。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。