深度学习图像识别:车牌识别系统

编程入门 行业动态 更新时间:2024-10-19 16:36:26

深度学习图像识别:<a href=https://www.elefans.com/category/jswz/34/1761824.html style=车牌识别系统"/>

深度学习图像识别:车牌识别系统

项目概述

       图像识别是人工智能的一个重要领域,是指利用计算机对图像进行处理、分析和理解,以识别各种不同模式的目标和对像的技术,并对质量不佳的图像进行一系列的增强与重建技术手段,从而有效改善图像质量。

        图像识别技术可能是以图像的主要特征为基础的。每个图像都有它的特征,如字母A有个尖,P有个圈、而Y的中心有个锐角等。对图像识别时眼动的研究表明,视线总是集中在图像的主要特征上,也就是集中在图像轮廓曲度最大或轮廓方向突然改变的地方,这些地方的信息量最大。而且眼睛的扫描路线也总是依次从一个特征转到另一个特征上。由此可见,在图像识别过程中,知觉机制必须排除输入的多余信息,抽出关键的信息。同时,在大脑里必定有一个负责整合信息的机制,它能把分阶段获得的信息整理成一个完整的知觉映象。车牌检测与识别
       目前,智能交通系统中集成运用计算机视觉、物联网、人工智能等多种技术成为未来发展方向。其中,车牌识别(License Plate Recognition,LPR)技术作为一项重要技术,从获取的图像中提取目标车辆的车牌信息,成为完善智能交通管理运行的基础。

        由于本文介绍的是中文车牌,所以可以简单了解一下国内汽车拍照的特点:字符数为七个,包括汉字、字母和数字。车牌颜色组合中,其中最常见的组合为普通小型汽车蓝底白字和新能源汽车的渐变绿底黑字。

 车牌识别原理和流程

       车牌识别是基于图像分割和图像识别理论,对含有车辆号牌的图像进行分析处理,从而确定牌照在图像中的位置,并进一步提取和识别出文本字符。

        一个典型的车牌识别处理过程包括:图像采集、图像预处理、车牌定位、字符分割、字符识别及结果输出等处理过程。各个处理过程相辅相成,每个处理过程均须保证其高效和较高的抗干扰能力,只有这样才能保证识别功能达到满意的功能品质。

        车牌识别系统的实现方式主要分两种,一种为静态图像识别,另一种为动态视频流识别。静态图像识别受限于图像质量、车牌污损度、车牌倾斜度等因素。动态视频流识别则需要更快的识别速度,受限于处理器的性能指标,特别是在移动终端实现车牌实时识别需要更多性能优化。

虽然车牌识别包含6大处理过程,但核心算法主要位于车牌定位、字符分割及字符识别这三个模块中。

数据集介绍

  • 数据集(来源于飞桨)文件名为characterData.zip,其中有65个文件夹

  • 大小:characterData.zip(17.16M),包含0-9,A-Z,以及各省简称(阿拉伯数字、字母及中文多种字体约4000条数据)

  • 规格:图片为1x20x20的灰度图像

  • 本次实验中,取其中的10%作为测试集,90%作为训练集

本次实践是一个多分类任务,需要将照片中的每个字符分别进行识别,完成车牌的识别

测试平台:飞桨

挂载数据

# 查看当前挂载的数据集目录, 该目录下的变更重启环境后会自动还原
# View dataset directory. This directory will be recovered automatically after resetting environment. 
!ls /home/aistudio/data

导入运行所用到的包

#导入需要的包
import os
import zipfile
import random
import json
import cv2
import numpy as np
from PIL import Image
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph import Linear,Conv2D,Pool2D
import matplotlib.pyplot as plt

数据准备

配置原始数据集,训练次数设置为1,批次大小设置为32,超参学习率设为0.1%,解压到原始目录下。


'''
参数配置
'''
train_parameters = {"input_size": [1, 20, 20],                           #输入图片的shape"class_dim": -1,                                     #分类数"src_path":"data/data23617/characterData.zip",       #原始数据集路径"target_path":"/home/aistudio/data/dataset",        #要解压的路径 "train_list_path": "./train_data.txt",              #train_data.txt路径"eval_list_path": "./val_data.txt",                  #eval_data.txt路径"label_dict":{},                                    #标签字典"readme_path": "/home/aistudio/data/readme.json",   #readme.json路径"num_epochs": 1,                                    #训练轮数"train_batch_size": 32,                             #批次的大小"learning_strategy": {                              #优化函数相关的配置"lr": 0.001                                     #超参数学习率} 
}
def unzip_data(src_path,target_path):'''解压原始数据集,将src_path路径下的zip包解压至data/dataset目录下'''if(not os.path.isdir(target_path)):    z = zipfile.ZipFile(src_path, 'r')z.extractall(path=target_path)z.close()else:print("文件已解压")

 读取、存放所有类别图像的信息,获取所有图片用每10张图片取一个作为验证数据,说明文件中的测试集与训练集数目并生成数据列表。

def get_data_list(target_path,train_list_path,eval_list_path):'''生成数据列表'''#存放所有类别的信息class_detail = []#获取所有类别保存的文件夹名称data_list_path=target_pathclass_dirs = os.listdir(data_list_path)if '__MACOSX' in class_dirs:class_dirs.remove('__MACOSX')# #总的图像数量all_class_images = 0# #存放类别标签class_label=0# #存放类别数目class_dim = 0# #存储要写进eval.txt和train.txt中的内容trainer_list=[]eval_list=[]#读取每个类别for class_dir in class_dirs:if class_dir != ".DS_Store":class_dim += 1#每个类别的信息class_detail_list = {}eval_sum = 0trainer_sum = 0#统计每个类别有多少张图片class_sum = 0#获取类别路径 path = os.path.join(data_list_path,class_dir)# print(path)# 获取所有图片img_paths = os.listdir(path)for img_path in img_paths:                                  # 遍历文件夹下的每个图片if img_path =='.DS_Store':continuename_path = os.path.join(path,img_path)                       # 每张图片的路径if class_sum % 10 == 0:                                 # 每10张图片取一个做验证数据eval_sum += 1                                       # eval_sum为测试数据的数目eval_list.append(name_path + "\t%d" % class_label + "\n")else:trainer_sum += 1 trainer_list.append(name_path + "\t%d" % class_label + "\n")#trainer_sum测试数据的数目class_sum += 1                                          #每类图片的数目all_class_images += 1                                   #所有类图片的数目# 说明的json文件的class_detail数据class_detail_list['class_name'] = class_dir             #类别名称class_detail_list['class_label'] = class_label          #类别标签class_detail_list['class_eval_images'] = eval_sum       #该类数据的测试集数目class_detail_list['class_trainer_images'] = trainer_sum #该类数据的训练集数目class_detail.append(class_detail_list)  #初始化标签列表train_parameters['label_dict'][str(class_label)] = class_dirclass_label += 1#初始化分类数train_parameters['class_dim'] = class_dimprint(train_parameters)#乱序  random.shuffle(eval_list)with open(eval_list_path, 'a') as f:for eval_image in eval_list:f.write(eval_image) #乱序        random.shuffle(trainer_list) with open(train_list_path, 'a') as f2:for train_image in trainer_list:f2.write(train_image) # 说明的json文件信息readjson = {}readjson['all_class_name'] = data_list_path                  #文件父目录readjson['all_class_images'] = all_class_imagesreadjson['class_detail'] = class_detailjsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))with open(train_parameters['readme_path'],'w') as f:f.write(jsons)print ('生成数据列表完成!')

初始化参数生成数据列表,解压原始数据。

'''
参数初始化
'''
src_path=train_parameters['src_path']
target_path=train_parameters['target_path']
train_list_path=train_parameters['train_list_path']
eval_list_path=train_parameters['eval_list_path']
batch_size=train_parameters['train_batch_size']
'''
解压原始数据到指定路径
'''
unzip_data(src_path,target_path)#每次生成数据列表前,首先清空train.txt和eval.txt
with open(train_list_path, 'w') as f: f.seek(0)f.truncate() 
with open(eval_list_path, 'w') as f: f.seek(0)f.truncate() #生成数据列表   
get_data_list(target_path,train_list_path,eval_list_path)'''
构造数据提供器
'''
train_reader = paddle.batch(data_reader(train_list_path),batch_size=batch_size,drop_last=True)
eval_reader = paddle.batch(data_reader(eval_list_path),batch_size=batch_size,drop_last=True)

 定义模型

设置通道数、卷积核个数、卷积核大小

class MyLeNet(fluid.dygraph.Layer):def __init__(self):super(MyLeNet,self).__init__()self.hidden1_1 = Conv2D(1,28,5,1) #通道数、卷积核个数、卷积核大小self.hidden1_2 = Pool2D(pool_size=2,pool_type='max',pool_stride=1)self.hidden2_1 = Conv2D(28,32,3,1)self.hidden2_2 = Pool2D(pool_size=2,pool_type='max',pool_stride=1)self.hidden3 = Conv2D(32,32,3,1)self.hidden4 = Linear(32*10*10,65,act='softmax')def forward(self,input):#print(input.shape)x = self.hidden1_1(input)#print(x.shape)x = self.hidden1_2(x)#print(x.shape)x = self.hidden2_1(x)#print(x.shape)x = self.hidden2_2(x)#print(x.shape)x = self.hidden3(x)#print(x.shape)x = fluid.layers.reshape(x, shape=[-1, 32*10*10])y = self.hidden4(x)return y

训练模型

with fluid.dygraph.guard():model=MyLeNet() #模型实例化model.train() #训练模式opt=fluid.optimizer.SGDOptimizer(learning_rate=train_parameters['learning_strategy']['lr'], parameter_list=model.parameters())#优化器选用SGD随机梯度下降,学习率为0.001.epochs_num=train_parameters['num_epochs'] #迭代次数for pass_num in range(epochs_num):for batch_id,data in enumerate(train_reader()):images=np.array([x[0].reshape(1,20,20) for x in data],np.float32)labels = np.array([x[1] for x in data]).astype('int64')labels = labels[:, np.newaxis]image=fluid.dygraph.to_variable(images)label=fluid.dygraph.to_variable(labels)predict=model(image) #数据传入modelloss=fluid.layers.cross_entropy(predict,label)avg_loss=fluid.layers.mean(loss)#获取loss值acc=fluid.layers.accuracy(predict,label)#计算精度if batch_id!=0 and batch_id%50==0:Batch = Batch+50 Batchs.append(Batch)all_train_loss.append(avg_loss.numpy()[0])all_train_accs.append(acc.numpy()[0])print("train_pass:{},batch_id:{},train_loss:{},train_acc:{}".format(pass_num,batch_id,avg_loss.numpy(),acc.numpy()))avg_loss.backward()       opt.minimize(avg_loss)    #优化器对象的minimize方法对参数进行更新 model.clear_gradients()   #model.clear_gradients()来重置梯度fluid.save_dygraph(model.state_dict(),'MyLeNet')#保存模型draw_train_acc(Batchs,all_train_accs)
draw_train_loss(Batchs,all_train_loss)

准确率(accs)与缺失值(loss)的训练结果:

 模型评估

#模型评估
with fluid.dygraph.guard():accs = []model_dict, _ = fluid.load_dygraph('MyLeNet')model = MyLeNet()model.load_dict(model_dict) #加载模型参数model.eval() #训练模式for batch_id,data in enumerate(eval_reader()):#测试集images=np.array([x[0].reshape(1,20,20) for x in data],np.float32)labels = np.array([x[1] for x in data]).astype('int64')labels = labels[:, np.newaxis]image=fluid.dygraph.to_variable(images)label=fluid.dygraph.to_variable(labels)       predict=model(image)       acc=fluid.layers.accuracy(predict,label)accs.append(acc.numpy()[0])avg_acc = np.mean(accs)print(avg_acc)

 运行结果最后得到的准确率是约等于80%

 

使用模型

对车牌图像进行预处理

对每个字符逐一分割并保存,记录字符准确位置

       字符分割的任务是把多列或多行字符图像中的每个字符从整个图像中切割出来成为单个字符图像。传统字符分割算法可以归纳为以下两类类:直接分割法、基于图像形态学的分割法。直接分割法简单,基于一些先验知识,如车牌字符分布情况等,同时辅助一些基本投影算法实现分割;基于形态学的分割方法使用边缘检测、膨胀腐蚀等处理来确定字符图像位置。传统的字符分割算法同样对外界干扰敏感,如车牌倾斜度、字符污损粘连等。车牌字符的正确分割对字符的识别是很关键的,在分割正确的情况下,才能保证识别的准确率。而随着神经网络理论的不断发展,端到端的图片分类识别技术也有很大突破,因此很多OCR软件逐步摆脱传统字符分割处理,由识别网络对多字符进行直接识别。

# 对车牌图片进行处理,分割出车牌中的每一个字符并保存
license_plate = cv2.imread('work/车牌.png')
gray_plate = cv2.cvtColor(license_plate, cv2.COLOR_RGB2GRAY) 
ret, binary_plate = cv2.threshold(gray_plate, 175, 255, cv2.THRESH_BINARY) #ret:阈值,binary_plate:根据阈值处理后的图像数据
# 按列统计像素分布
result = []
for col in range(binary_plate.shape[1]):result.append(0)for row in range(binary_plate.shape[0]):result[col] = result[col] + binary_plate[row][col]/255
# print(result)
#记录车牌中字符的位置
character_dict = {}
num = 0
i = 0
while i < len(result):if result[i] == 0:i += 1else:index = i + 1while result[index] != 0:index += 1character_dict[num] = [i, index-1]num += 1i = index
# print(character_dict)        
#将每个字符填充,并存储
characters = []
for i in range(8):if i==2:continuepadding = (170 - (character_dict[i][1] - character_dict[i][0])) / 2#将单个字符图像填充为170*170ndarray = np.pad(binary_plate[:,character_dict[i][0]:character_dict[i][1]], ((0,0), (int(padding), int(padding))), 'constant', constant_values=(0,0))ndarray = cv2.resize(ndarray, (20,20))cv2.imwrite('work/' + str(i) + '.png', ndarray)characters.append(ndarray)def load_image(path):img = paddle.dataset.image.load_image(file=path, is_color=False)img = img.astype('float32')img = img[np.newaxis, ] / 255.0return img

对标签进行转换

将文件中的拼音转化为中文字体

#将标签进行转换
print('Label:',train_parameters['label_dict'])
match = {'A':'A','B':'B','C':'C','D':'D','E':'E','F':'F','G':'G','H':'H','I':'I','J':'J','K':'K','L':'L','M':'M','N':'N','O':'O','P':'P','Q':'Q','R':'R','S':'S','T':'T','U':'U','V':'V','W':'W','X':'X','Y':'Y','Z':'Z','yun':'云','cuan':'川','hei':'黑','zhe':'浙','ning':'宁','jin':'津','gan':'赣','hu':'沪','liao':'辽','jl':'吉','qing':'青','zang':'藏','e1':'鄂','meng':'蒙','gan1':'甘','qiong':'琼','shan':'陕','min':'闽','su':'苏','xin':'新','wan':'皖','jing':'京','xiang':'湘','gui':'贵','yu1':'渝','yu':'豫','ji':'冀','yue':'粤','gui1':'桂','sx':'晋','lu':'鲁','0':'0','1':'1','2':'2','3':'3','4':'4','5':'5','6':'6','7':'7','8':'8','9':'9'}
L = 0
LABEL ={}
for V in train_parameters['label_dict'].values():LABEL[str(L)] = match[V]L += 1
print(LABEL)

使用模型进行预测

#构建预测动态图过程
with fluid.dygraph.guard():model=MyLeNet()#模型实例化model_dict,_=fluid.load_dygraph('MyLeNet')model.load_dict(model_dict)#加载模型参数model.eval()#评估模式lab=[]for i in range(8):if i==2:continueinfer_imgs = []infer_imgs.append(load_image('work/' + str(i) + '.png'))infer_imgs = np.array(infer_imgs)infer_imgs = fluid.dygraph.to_variable(infer_imgs)result=model(infer_imgs)lab.append(np.argmax(result.numpy()))
print(lab)
display(Image.open('work/车牌.png'))
for i in range(len(lab)):print(LABEL[str(lab[i])],end='')

 识别结果为:

更多推荐

深度学习图像识别:车牌识别系统

本文发布于:2024-02-25 19:55:53,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1700121.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:车牌   识别系统   深度   图像

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!