任务描述:

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

数据集介绍

  • 数据集文件名为characterData.zip,其中有65个文件夹

  • 包含0-9,A-Z,以及各省简称

  • 图片为1*20*20的灰度图像

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

    #导入需要的包
    import os
    import zipfile #Python zipfile模块用来做zip格式编码的压缩和解压缩的
    import random #生成随机数
    import json
    import cv2  #使用cv2来表示调用的是 C++ 开发的 opencv 的接口
    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、数据准备


'''
参数配置
'''
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') #打开一个ZIP文件。返回的也是一个类似文件的ZipFile对象,可以读写。z.extractall(path=target_path)  #批量解压文件。默认是全部解压。z.close()else:print("文件已解压")

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)  #shuffle() 方法将序列的所有元素随机排序。shuffle()是不能直接访问的,需要导入 random 模块.with open(eval_list_path, 'a') as f:for eval_image in eval_list:f.write(eval_image) #write()方法可将任何字符串写入一个打开的文件,
#write()方法不会在字符串的结尾添加换行符('\n')#乱序        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=(',', ': '))##将Python对象编码成json字符串with open(train_parameters['readme_path'],'w') as f: #建立一个json文件f.write(jsons)print ('生成数据列表完成!')

补充:json.dumps()

  • indent:应该是一个非负的整型,如果是0,或者为空,则一行显示数据,否则会换行且按照indent的数量显示前面的空白,这样打印出来的json数据也叫pretty-printed json
  • separators:分隔符,实际上是(item_separator, dict_separator)的一个元组,默认的就是(‘,’,’:’);这表示dictionary内keys之间用“,”隔开,而KEY和value之间用“:”隔开。
  • encoding:默认是UTF-8,设置json数据的编码方式。
  • sort_keys:将数据根据keys的值进行排序。
def data_reader(file_list):'''自定义data_reader'''def reader():with open(file_list, 'r') as f:lines = [line.strip() for line in f]for line in lines:img_path, lab = line.strip().split('\t')img = cv2.imread(img_path)img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)img = np.array(img).astype('float32')img = img/255.0yield img, int(lab) return reader

补充:

1、cv2.imread()接口读图像,读进来直接是BGR 格式数据格式在 0~255
需要特别注意的是图片读出来的格式是BGR,不是我们最常见的RGB格式,颜色肯定有区别。
2、cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
cv2.COLOR_BGR2RGB 将BGR格式转换成RGB格式
cv2.COLOR_BGR2GRAY 将BGR格式转换成灰度图片

'''
参数初始化
'''
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)

补充:

  1. truncate() 方法用于截断文件,如果指定了可选参数 size,则表示截断文件为 size 个字符。 如果没有指定 size,则从当前位置起截断;截断之后 size 后面的所有字符被删除。 
  2. file.seek()方法标准格式是:seek(offset,whence=0)offset:开始的偏移量,也就是代表需要移动偏移的字节数whence:给offset参数一个定义,表示要从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起。默认为0

    whence 的默认参数是0。

  3. drop_last告诉如何处理"数据集长度"除于batch_size余下的数据。True就抛弃,否则保留.

Batch=0
Batchs=[]
all_train_accs=[]
def draw_train_acc(Batchs, train_accs):title="training accs"plt.title(title, fontsize=24)plt.xlabel("batch", fontsize=14)plt.ylabel("acc", fontsize=14)plt.plot(Batchs, train_accs, color='green', label='training accs') #label为线条的标签plt.legend() #显示标签plt.grid()plt.show()all_train_loss=[]
def draw_train_loss(Batchs, train_loss):title="training loss"plt.title(title, fontsize=24)plt.xlabel("batch", fontsize=14)plt.ylabel("loss", fontsize=14)plt.plot(Batchs, train_loss, color='red', label='training loss')plt.legend()plt.grid()plt.show() #可以将多条线画在同一张图上

2、定义模型

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)    #32*1*20*20x = self.hidden1_1(input)#print(x.shape)        #32*28*16*16x = self.hidden1_2(x)#print(x.shape)        #32*28*15*15x = self.hidden2_1(x)#print(x.shape)        #32*32*13*13x = self.hidden2_2(x)   #print(x.shape)        #32*32*12*12x = self.hidden3(x)#print(x.shape)        #32*32*10*10x = fluid.layers.reshape(x, shape=[-1, 32*10*10]) #32*3200y = self.hidden4(x)    #32*65return y#定义DNN网络
'''
class MyDNN(fluid.dygraph.Layer):def __init__(self):super(MyDNN,self).__init__()self.hidden1 = Linear(20*20,200,act='relu')self.hidden2 = Linear(200,100,act='relu')self.hidden3 = Linear(100,100,act='relu')self.out = Linear(100,65,act='softmax')def forward(self,input):        # forward 定义执行实际运行时网络的执行逻辑x = fluid.layers.reshape(input, shape=[-1,20*20]) #-1 表示这个维度的值是从x的元素总数和剩余维度推断出来的,有且只能有一个维度设置为-1# print(x.shape)x = self.hidden1(x)# print('1', x.shape)x = self.hidden2(x)# print('2',x.shape)x = self.hidden3(x)# print('3',x.shape)y = self.out(x)# print('4',y.shape)return y
'''

3、训练模型

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) #32*1*20*20labels = 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)

4、模型评估


#模型评估
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)

5、使用模型

5.1对车牌图像进行预处理

# 对车牌图片进行处理,分割出车牌中的每一个字符并保存
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:根据阈值处理后的图像数据
#这里把阈值设置成了175,对于BINARY方法,当图像中的灰度值大于175的重置像素值为255. 0是黑,255是白
# 按列统计像素分布
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) #读取图像,返回的是numpy数组img = img.astype('float32')img = img[np.newaxis, ] / 255.0return img

补充:

  1. cv2.threshold()这个函数有四个参数,第一个原图像,第二个进行分类的阈值,第三个是高于(低于)阈值时赋予的新值,第四个是一个方法选择参数,常用的cv2.THRESH_BINARY(黑白二值)。该函数有两个返回值,第一个retVal(得到的阈值值(在后面一个方法中会用到)),第二个就是阈值化后的图像。
  2. 缩放到原来的二分之一,输出尺寸格式为(宽,高) img_test1 = cv2.resize(img, (int(y / 2), int(x / 2)))

5.2 对标签进行转换

#将标签进行转换
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)

5.3 使用模型进行预测

#构建预测动态图过程
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='')

 

Day03 车牌识别相关推荐

  1. robo3t 连接服务器数据库_车牌识别软件连接各种数据库方法大全

    软件连接各种数据库方法大全 1:软件连接免安装数据库. 免安装数据库使用方便,不受操作系统版本影响,不用安装,解压打开运行即可,所以免安装数据库不要放在桌面上,也不要解压打开多个. 打开车牌识别软件, ...

  2. LabVIEW OCR 实现车牌识别(实战篇—3)

    目录 1.字符数据集训练 2.识别与验证 在学习本章之前,推荐先学习系列专栏文章:LabVIEW目标对象分类识别(理论篇-5) OCR(光学字符识别)是指机器自动从图像中识别文本字符的过程,OCR机器 ...

  3. OpenCV(项目)车牌识别4 -- 总结篇

    目录 一.效果 1.成功案例 2.经典失败案例(单字符识别成类似字符) 3.其他失败案例 二.总结 三.车牌识别总代码 一.效果 1.成功案例 2.经典失败案例(单字符识别成类似字符) 3.其他失败案 ...

  4. OpenCV(项目)车牌识别2 -- 车牌字符分割(直方图)

    目录 试错 1.没有膨胀/膨胀过小:无法连接上单个字符. 2.膨胀过大:错误连接相邻字符. 一.直方图处理原理 1.横向分割 2.纵向分割 过程: 一.中值滤波.灰度化 二.二值化(统一黑底白字) 三 ...

  5. OpenCV(项目)车牌识别1 -- 车牌提取(形态学)

    目录 一.形态学车牌提取(简单:单情景) 1.读取图片,转灰度图 2.提取轮廓(Sobel算子提取y方向边缘) 3.自适应二值化 4.闭运算处理,把图像闭合.揉团,使图像区域化 5.腐蚀/膨胀去噪得到 ...

  6. 寻找连通域算法_【车牌识别算法】

    车牌识别技术要求能够将运动中的汽车牌照从复杂背景中提取并识别出来,通过车牌提取.图像预处理.特征提取.车牌字符识别等技术,识别车辆牌号.颜色等信息. 目前车牌识别技术主要分为端到端识别与车牌分割识别两 ...

  7. 免费直播:1小时带你体验Python车牌识别实战

    Python基础学会了,实战又是爬虫?太枯燥? 别无聊,CSDN学院邀请章秀淞老师开设技术直播课:1小时带你体验车牌识别实战.让你从众多车中,能用Python技术找到夏树上的那辆叔叔的奔驰车牌(玩笑) ...

  8. 基于OpenCV 的车牌识别

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 车牌识别是一种图像处理技术,用于识别不同车辆.这项技术被广泛用于各 ...

  9. 你不知道的车牌识别系统

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 有小伙伴后台和小白说,能不能推荐几个适合入门的开源视觉项目,因为根 ...

  10. 实战:车牌识别之车牌定位

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 本文转自|3D视觉工坊 从本节起,我们开始尝试做一下车牌识别中的算 ...

最新文章

  1. tomcat配置文件server.xml具体解释
  2. 数组、ArrayList、链表、LinkedList
  3. 深入解读f-散度和f-GAN训练的相关数学性质
  4. RabbitMQ 相关概念
  5. group by with cube
  6. java学习(六)多线程 下
  7. 【Matlab数学建模】层次分析法
  8. python实现银行ATM系统
  9. 韩国NF(耐福)数字音频功放芯片系列大全
  10. 除了装去广告软件,你还能通过「禁止APP联网」来屏蔽广告
  11. android手机如何截屏快捷键,手机截屏怎么弄,手把手教你手机截图方法
  12. Type-C PD充电简介
  13. 机器学习之朴素贝叶斯分类算法
  14. python-DRF_限流Throttling_自定义频率类_内置频率类使用_过滤排序功能
  15. 2022最新面试题(含css、html、js、es6、Vue)
  16. java 过滤bom头_去除bom头.java
  17. 完整版 Chrome 浏览器将登陆 Fuchsia OS
  18. 【循序渐进学Python】Python中的循环(二)——while循环与嵌套
  19. 计算机英语说明文,英语说明文
  20. 工程学导论的学习感悟

热门文章

  1. xshell过期/安装教程
  2. CUDA驱动版本与运行版本不匹配问题详解
  3. 决策树结果可视化中文乱码问题解决方案
  4. 基于MATLAB产生式系统(植物识别系统)
  5. Linux tar命令无法执行,Linux执行tar解压报错tar: Error is not recoverable: exiting now
  6. 免费分享佳能ir c3320 c3330 c3325彩色复印机中文维修手册
  7. c语言课程设计,学生信息管理系统
  8. 2019通信工程师的职业发展前景和方向
  9. 苹果id无法登陆_英雄联盟手游苹果id怎么绑定拳头账号?绑定教程介绍[图]-攻略...
  10. 电脑知识 如何提取图片中的文字