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

文件百度云盘下载链接 密码:h67g

车牌生成后保存至plate文件夹,示例如下:在这里插入图片描述

三、数据导入

from genplate import *
import matplotlib.pyplot as plt

# 产生用于训练的数据
class OCRIter:
    def __init__(self, batch_size, width, height):
        super(OCRIter, self).__init__()
        self.genplate = GenPlate("data\\font\\platech.ttf", 'data\\font\\platechar.ttf', "data\\NoPlates")
        self.batch_size = batch_size
        self.height = height
        self.width = width

    def iter(self):
        data = []
        label = []
        for i in range(self.batch_size):
            img, num = self.gen_sample(self.genplate, self.width, self.height)
            data.append(img)
            label.append(num)
        return np.array(data), np.array(label)

    @staticmethod
    def rand_range(lo, hi):
        return lo + r(hi - lo)

    def gen_rand(self):
        name = ""
        label = list([])
        label.append(self.rand_range(0, 31))    #产生车牌开头32个省的标签
        label.append(self.rand_range(41, 65))    #产生车牌第二个字母的标签
        for i in range(5):
            label.append(self.rand_range(31, 65))    #产生车牌后续5个字母的标签
        name += chars[label[0]]
        name += chars[label[1]]
        for i in range(5):
            name += chars[label[i+2]]
        return name, label

    def gen_sample(self, genplate, width, height):
        num, label = self.gen_rand()
        img = genplate.generate(num)
        img = cv.resize(img, (height, width))
        img = np.multiply(img, 1/255.0)
        return img, label        #返回的label为标签,img为车牌图像

'''
# 测试代码
O = OCRIter(2, 272, 72)
img, lbl = O.iter()
for im in img:
    plt.imshow(im, cmap='gray')
    plt.show()
print(img.shape)
print(lbl)
'''

四、CNN模型构建

import tensorflow as 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 :
在这里插入图片描述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, "沪": 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张预测错误,从最后两幅预测错误的图片可以看出,模型对相似字符以及遮挡字符识别成功率仍有待提高。测试结果部分展示如下:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

八、总结

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

  • 38
    点赞
  • 335
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 61
    评论
以下是一个基于Python+Tensorflow+CNN车牌识别系统的示例代码,供您参考: 首先,需要安装必要的库: ```python pip install tensorflow-gpu==1.14.0 pip install opencv-python pip install imutils ``` 然后,我们需要下载数据集。此处我们使用的是CCPD数据集,可以从以下链接下载:https://github.com/detectRecog/CCPD。 接下来,我们定义一个函数`load_data`,用于加载数据集: ```python import os import cv2 import numpy as np def load_data(data_dir): data = [] labels = [] for root, dirs, files in os.walk(data_dir): for file in files: if file.endswith('.jpg'): image_path = os.path.join(root, file) image = cv2.imread(image_path) image = cv2.resize(image, (120, 32)) data.append(image) label = os.path.basename(root) labels.append(label) data = np.array(data, dtype='float32') data /= 255.0 labels = np.array(labels) return data, labels ``` 接下来,我们定义一个函数`build_model`,用于构建模型: ```python from keras.models import Sequential from keras.layers.convolutional import Conv2D, MaxPooling2D from keras.layers.core import Dense, Dropout, Activation, Flatten def build_model(input_shape, num_classes): model = Sequential() model.add(Conv2D(32, (3, 3), padding='same', input_shape=input_shape)) model.add(Activation('relu')) model.add(Conv2D(32, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Conv2D(64, (3, 3), padding='same')) model.add(Activation('relu')) model.add(Conv2D(64, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(num_classes)) model.add(Activation('softmax')) return model ``` 接下来,我们加载数据集并构建模型: ```python data_dir = '/path/to/dataset' input_shape = (32, 120, 3) num_classes = 65 data, labels = load_data(data_dir) model = build_model(input_shape, num_classes) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) ``` 接下来,我们训练模型: ```python from keras.utils import to_categorical num_epochs = 10 batch_size = 32 labels = to_categorical(labels, num_classes=num_classes) model.fit(data, labels, batch_size=batch_size, epochs=num_epochs, validation_split=0.1) ``` 最后,我们可以使用训练好的模型对车牌进行识别: ```python import imutils from keras.preprocessing.image import img_to_array def recognize_plate(image, model): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (7, 7), 0) edged = cv2.Canny(gray, 50, 200) cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) cnts = sorted(cnts, key=cv2.contourArea, reverse=True) for c in cnts: x, y, w, h = cv2.boundingRect(c) if w / h > 4 and w > 100: roi = gray[y:y + h, x:x + w] thresh = cv2.threshold(roi, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1] thresh = cv2.resize(thresh, (120, 32)) thresh = thresh.astype("float") / 255.0 thresh = img_to_array(thresh) thresh = np.expand_dims(thresh, axis=0) preds = model.predict(thresh) label = chr(preds.argmax(axis=1)[0] + 48) cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(image, label, (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 0), 2) break return image ``` 使用示例: ```python image = cv2.imread('/path/to/image') image = recognize_plate(image, model) cv2.imshow('Image', image) cv2.waitKey(0) ``` 以上就是一个基于Python+Tensorflow+CNN车牌识别系统的示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 61
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阁不鸽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值