Day03 车牌识别

任务描述:

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

数据集介绍

  • 数据集文件名为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_path
    class_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 = 0
            trainer_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':
                    continue
                name_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_dir
            class_label += 1
            
    #初始化分类数
    train_parameters['class_dim'] = class_dim
    print(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_images
    readjson['class_detail'] = class_detail
    jsons = 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.0
                yield 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*20
        x = self.hidden1_1(input)
        #print(x.shape)        #32*28*16*16
        x = self.hidden1_2(x)
        #print(x.shape)        #32*28*15*15
        x = self.hidden2_1(x)
        #print(x.shape)        #32*32*13*13
        x = self.hidden2_2(x)   
        #print(x.shape)        #32*32*12*12
        x = self.hidden3(x)
        #print(x.shape)        #32*32*10*10
        x = fluid.layers.reshape(x, shape=[-1, 32*10*10]) #32*3200
        y = self.hidden4(x)    #32*65

        return 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*20
            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) #数据传入model
            
            loss=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 += 1
    else:
        index = i + 1
        while result[index] != 0:
            index += 1
        character_dict[num] = [i, index-1]
        num += 1
        i = index
# print(character_dict)        
#将每个字符填充,并存储
characters = []
for i in range(8):
    if i==2:
        continue
    padding = (170 - (character_dict[i][1] - character_dict[i][0])) / 2
    #将单个字符图像填充为170*170
    ndarray = 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.0
    return 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:
            continue
        infer_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='')

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
day03-手动组建fabric网络.pdf》是一份指导手册,用于教授如何手动组建Fabric网络。Fabric网络是一个分布式账本技术,可用于构建区块链解决方案。这个手册详细介绍了在组建Fabric网络时的步骤和注意事项。 首先,手动组建一个Fabric网络需要一些技术基础。手册在开始部分介绍了一些必备的知识,例如区块链和分布式账本的基本概念。学习者需要理解这些基础概念,以便更好地理解后续的内容。 手册还提供了一些实践操作的步骤。首先,需要准备网络中的各个组件,例如Peer节点、Orderer节点和Channel。手册详细介绍了如何配置这些组件,并给出了一些示例。 接下来,手册重点介绍了如何连接这些组件,以构建一个完整的Fabric网络。手册详细介绍了如何配置节点之间的通信,如何创建和加入Channel,以及如何运行智能合约。这些步骤是手动组建Fabric网络的核心内容,学习者需要仔细理解和熟悉。 除了步骤之外,手册还提供了一些注意事项和最佳实践。例如,手册强调了网络的安全性和可扩展性,提供了一些建议和建议,帮助学习者更好地设计和管理Fabric网络。 总之,《day03-手动组建fabric网络.pdf》是一份非常实用的手册,适合想要了解如何手动组建Fabric网络的人士。通过学习这个手册,学习者可以获得丰富的知识和实践经验,从而能够独立地组建和管理自己的Fabric网络。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值