【LeNet5】简单车牌识别

article/2025/7/3 8:12:11

文章目录

  • 1. 项目准备
    • 1.1. 问题导入
    • 1.2. 数据集简介
  • 2. LeNet5模型
    • 2.1. 卷积神经网络
    • 2.2. 模型介绍
  • 3. 实验步骤
    • 3.0. 前期准备
    • 3.1. 数据准备
    • 3.2. 网络配置
    • 3.3. 模型训练
    • 3.4. 模型评估
    • 3.5. 模型预测
  • 写在最后


1. 项目准备

1.1. 问题导入

本次实践是一个多分类任务,需要将照片中的每个字符分别进行识别,我们将借助CV2模块完成对车牌图像逐字符划分,然后训练卷积神经网络模型LeNet5完成对车牌的识别。

1.2. 数据集简介

实验数据集中有65个文件夹,包含数字 (0-9)、大写字母 (A-Z) 以及各省简称,每个文件夹存放一类图片,所有的图片均为20 * 20像素的灰度图像。

这是数据集的下载链接:车牌识别数据集 - AI Studio


2. LeNet5模型

2.1. 卷积神经网络

本实验使用的模型属于卷积神经网络模型(Convolutional Neural Network,CNN)。一个卷积神经网络通常包括输入层、输出层和多个隐藏层,而隐藏层通常包括卷积层、池化层和全连接层等。

在卷积运算和池化运算中,如果输入维度为 N N N,卷积核或池化核的大小为 K K K,运算步长为 S S S,填充长度为 P P P,那么输出维度为 ⌊ ( N − K + 2 P ) S ⌋ + 1 ⌊\frac{(N-K+2P)}{S}⌋ + 1 S(NK+2P)+1。并且在卷积运算中,卷积核的数量与输出通道数相等。

2.2. 模型介绍

LeNet-5源自Yann LeCun(1998)的论文Gradient-Based Learning Applied to Document Recognition,是一种用于手写体字符识别的、结构简单、非常高效的卷积神经网络。LeNet-5大体上由提取特征的三个卷积层和两个分类的全连接层组成,在卷积层之间均插入了最大池化层来缩小特征图,以便于后面的网络层提取更大尺度的特征,并且卷积层和全连接层均采用Sigmoid激活函数(现常用ReLU作为激活函数),其网络结构如下图所示。


3. 实验步骤

3.0. 前期准备

  • 导入模块

注意:本案例仅适用于PaddlePaddle 2.0+版本

import os
import cv2              # 在本项目中,它主要用来分割图像
import shutil
import random
import zipfile
import numpy as np
from PIL import Image
import matplotlib.pyplot as pltimport paddle
from paddle import nn
from paddle import metric as M
from paddle.io import DataLoader, Dataset
from paddle.nn import functional as F
from paddle.optimizer import Adam
from paddle.vision import transforms as T
  • 设置超参数
BATCH_SIZE = 128               # 每批次的样本数
INIT_LR = 1e-4                 # 初始学习率
EPOCHS = 25                    # 训练轮数
LOG_GAP = 100                  # 输出训练信息的间隔SRC_PATH = "./data/characterData.zip"     # 压缩包路径
DATA_PATH = "./data/dataset"              # 数据集路径
INFER_PATH = "./data/infer_license.png"   # 预测图片路径
TEMP_PATH = "./data/infer"                # 临时图片路径
MODEL_PATH = "LeNet5.pdparams"            # 模型参数保存路径

3.1. 数据准备

  • 解压数据集
    由于数据集中的数据是以压缩包的形式存放的,因此我们需要先解压数据压缩包。
if not os.path.isdir(DATA_PATH):z = zipfile.ZipFile(SRC_PATH, 'r')      # 打开Zip文件,创建Zip对象z.extractall(path=DATA_PATH)            # 解压Zip文件至DATA_PATHshutil.rmtree(DATA_PATH + "/__MACOSX")  # 删除无关文件z.close()
print("数据集解压完成!")
  • 划分数据集
    我们需要按1:9比例划分测试集和训练集,分别生成两个包含数据路径和标签映射关系的列表。
train_list, test_list = [], []           # 存放数据的路径及标签的映射关系
char_num, label_dict = 0, {}             # 方便字符在整型和字符型之间转换
file_folders = os.listdir(DATA_PATH)     # 统计数据集下的文件夹for folder in file_folders:label_dict[str(char_num)] = folder   # 记录标签和代号的对应关系imgs = os.listdir(os.path.join(DATA_PATH, folder))for idx, img in enumerate(imgs):img_path = os.path.join(DATA_PATH, folder, img)value = [img_path, char_num]if idx % 10 == 0:      # 按照1:9的比例划分数据集test_list.append(value)else:train_list.append(value)char_num += 1
  • 数据预处理
    我们需要先定义一个数据集类,接着对数据集图像进行缩放和归一化处理。
class MyDataset(Dataset):''' 自定义的数据集类 '''def __init__(self, label_list, transform=None):'''* `label_list`: 标签与文件路径的映射列表* `transform`:数据处理函数'''super(MyDataset, self).__init__()random.shuffle(label_list)      # 打乱映射列表self.label_list = label_listself.transform = transformdef __getitem__(self, index):''' 根据位序获取对应数据 '''img_path, label = self.label_list[index]image = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)image = image.astype('float32')if self.transform is not None:image = self.transform(image)image = paddle.to_tensor(image)return image, int(label)def __len__(self):''' 获取数据集样本总数 '''return len(self.label_list)
transform = T.Compose([T.Resize(size=(32, 32)),                                   # 缩放大小T.Normalize(mean=[127.5], std=[127.5], data_format='CHW')  # 归一化
])train_dataset = MyDataset(train_list, transform)  # 训练集
test_dataset = MyDataset(test_list, transform)    # 测试集
  • 定义数据提供器
    我们需要分别构建用于训练和测试的数据提供器,其中训练数据提供器是乱序、按批次提供数据的。
train_loader = DataLoader(train_dataset,            # 训练数据集batch_size=BATCH_SIZE,    # 每批读取的样本数num_workers=1,            # 加载数据的子进程个数shuffle=True,             # 打乱训练数据集drop_last=False)          # 不丢弃不完整的样本test_loader = DataLoader(test_dataset,              # 测试数据集batch_size=BATCH_SIZE,     # 每批读取的样本数num_workers=1,             # 加载数据的子进程个数shuffle=False,             # 不打乱测试数据集drop_last=False)           # 不丢弃不完整的样本

3.2. 网络配置

class LeNet5(nn.Layer):def __init__(self, in_channels=1, n_classes=10):'''* `in_channels`: 输入的通道数* `n_classes`: 输出分类数量'''super(LeNet5, self).__init__()# Conv2D(输入通道数,输出通道数,卷积核大小,卷积步长)# MaxPool2D(池化核大小,池化步长)self.conv1 = nn.Conv2D(in_channels, 6, 5, 1)self.pool1 = nn.MaxPool2D(2, 2)self.conv2 = nn.Conv2D(6, 16, 5, 1)self.pool2 = nn.MaxPool2D(2, 2)self.conv3 = nn.Conv2D(16, 120, 5, 1)self.fc1 = nn.Linear(120, 84)self.fc2 = nn.Linear(84, n_classes)def forward(self, x):x = F.relu(self.conv1(x))  # C1输出维度为28*28*6x = self.pool1(x)          # S2输出维度为14*14*6x = F.relu(self.conv2(x))  # C3输出维度为10*10*6x = self.pool2(x)          # S4输出维度为5*5*16x = F.relu(self.conv3(x))  # C5输出维度为120x = paddle.flatten(x, 1, -1)x = F.relu(self.fc1(x))    # F6输出维度为84x = F.dropout(x, p=0.25)y = self.fc2(x)            # F7输出维度为10return y
  • 模型实例化
model = LeNet5(in_channels=1, n_classes=char_num)

3.3. 模型训练

model.train()                # 开启训练模式
opt = Adam(learning_rate=INIT_LR,parameters=model.parameters())  # 定义Adam优化器
loss_arr, acc_arr = [], []   # 用于可视化for ep in range(EPOCHS):for batch_id, data in enumerate(train_loader()):x_data, y_data = datay_data = y_data[:, np.newaxis]          # 增加一维维度y_pred = model(x_data)                  # 预测结果acc = M.accuracy(y_pred, y_data)        # 计算准确率loss = F.cross_entropy(y_pred, y_data)  # 计算交叉熵if batch_id != 0 and batch_id % LOG_GAP == 0:   # 定期输出训练结果print("Epoch:%d,Batch:%3d,Loss:%.5f,Acc:%.5f"\% (ep, batch_id, loss, acc))acc_arr.append(acc.item())loss_arr.append(loss.item())opt.clear_grad()loss.backward()opt.step()paddle.save(model.state_dict(), MODEL_PATH)  # 保存训练好的模型

模型训练结果如下:

Epoch:0,Batch:100,Loss:0.93938,Acc:0.77344
Epoch:1,Batch:100,Loss:0.45156,Acc:0.88281
Epoch:2,Batch:100,Loss:0.37834,Acc:0.90625
Epoch:3,Batch:100,Loss:0.33006,Acc:0.92188
Epoch:4,Batch:100,Loss:0.28153,Acc:0.92969
Epoch:5,Batch:100,Loss:0.14206,Acc:0.96875
Epoch:6,Batch:100,Loss:0.17640,Acc:0.94531
Epoch:7,Batch:100,Loss:0.14656,Acc:0.97656
Epoch:8,Batch:100,Loss:0.17134,Acc:0.96875
Epoch:9,Batch:100,Loss:0.13322,Acc:0.95312
Epoch:10,Batch:100,Loss:0.10637,Acc:0.96875
Epoch:11,Batch:100,Loss:0.06380,Acc:0.97656
Epoch:12,Batch:100,Loss:0.06698,Acc:0.97656
Epoch:13,Batch:100,Loss:0.01808,Acc:1.00000
Epoch:14,Batch:100,Loss:0.03210,Acc:0.99219
Epoch:15,Batch:100,Loss:0.01909,Acc:1.00000
Epoch:16,Batch:100,Loss:0.06952,Acc:0.97656
Epoch:17,Batch:100,Loss:0.01585,Acc:1.00000
Epoch:18,Batch:100,Loss:0.03883,Acc:0.98438
Epoch:19,Batch:100,Loss:0.01998,Acc:1.00000
  • 可视化训练过程
fig = plt.figure(figsize=[10, 8])# 训练误差图像:
ax1 = fig.add_subplot(211, facecolor="#E8E8F8")
ax1.set_ylabel("Loss", fontsize=18)
plt.tick_params(labelsize=14)
ax1.plot(range(len(loss_arr)), loss_arr, color="orangered")
ax1.grid(linewidth=1.5, color="white")  # 显示网格# 训练准确率图像:
ax2 = fig.add_subplot(212, facecolor="#E8E8F8")
ax2.set_xlabel("Training Steps", fontsize=18)
ax2.set_ylabel("Accuracy", fontsize=18)
plt.tick_params(labelsize=14)
ax2.plot(range(len(acc_arr)), acc_arr, color="dodgerblue")
ax2.grid(linewidth=1.5, color="white")  # 显示网格fig.tight_layout()
plt.show()
plt.close()

3.4. 模型评估

model.eval()                 # 开启评估模式
test_costs, test_accs = [], []for batch_id, data in enumerate(test_loader()):x_data, y_data = datay_data = y_data[:, np.newaxis]          # 增加一维维度y_pred = model(x_data)                  # 预测结果acc = M.accuracy(y_pred, y_data)        # 计算准确率loss = F.cross_entropy(y_pred, y_data)  # 计算交叉熵test_accs.append(acc.item())test_costs.append(loss.item())
test_loss = np.mean(test_costs)    # 每轮测试的平均误差
test_acc = np.mean(test_accs)      # 每轮测试的平均准确率
print("Eval \t Loss:%.5f,Acc:%.5f" % (test_loss, test_acc))

模型评估结果如下:

Eval 	 Loss:0.16533,Acc:0.96663

3.5. 模型预测

  • 预测图片预处理
    由于车牌图片是由多个字符构成的RGB模式的图片,因此在进行预测之前需要将车牌图片转化为灰度图并按字符划分子图像,以便于模型逐字符进行预测。
def load_image(path):      # 图像整体预处理img = Image.open(path).convert("L")    # 将图像打开并转为灰度图img = img.resize((32, 32), Image.ANTIALIAS)img = np.array(img).reshape(1, 1, 32, 32)\.astype('float32')          # 把图像变成numpy数组img = img / 255.0 * 2.0 - 1.0   # 将数据归一化到[-1, 1]之间img = paddle.to_tensor(img)return imgdef divide_picture(path):      # 分割出车牌图像中的每一个字符并保存# (1) 图片灰度化处理:license = cv2.imread(path)gray_img = cv2.cvtColor(license, cv2.COLOR_RGB2GRAY)  # 将车牌转化为灰度图retval, bin_img = cv2.threshold(                      # 进行图像二值化操作gray_img, 100, 255, cv2.THRESH_BINARY  # 源图片、起始阈值、最大阈值、阈值类型)   # 函数返回值:retval是阈值;bin_img是根据阈值处理后的图像# (2) 按列统计像素分布:result = []for col in range(bin_img.shape[1]):result.append(0)for row in range(bin_img.shape[0]):result[col] += bin_img[row][col] / 255.0    # 统计归一化后的像素分布# (3) 记录车牌中的字符的位置:place_dict, num, i = {}, 0, 0while i < len(result):if result[i] == 0:i += 1else:index = i + 1while result[index] != 0:index += 1place_dict[num] = [i, index-1]num += 1i = index# (4) 将每个字符填充并存储:characters = []if not os.path.exists(TEMP_PATH):os.mkdir(TEMP_PATH)for i in range(8):if i == 2:   # 跳过蓝牌中的“•”号continuepadding = (170 - (place_dict[i][1] - place_dict[i][0])) / 2ndarray = np.pad(     # 将单个字符图像填充为170*170bin_img[:, place_dict[i][0]: place_dict[i][1]],((0, 0), (int(padding), int(padding))),"constant",constant_values=(0, 0))ndarray = cv2.resize(ndarray, (32, 32))cv2.imwrite(TEMP_PATH + "/%d.png" % i, ndarray)   # 保存划分后的单字符图片characters.append(ndarray)def match_labels(label_dict):   # 返回将标签与汉字的映射关系temp = {'yun': '云', 'cuan': '川', 'hei': '黑', 'zhe': '浙', 'ning': '宁', 'yu': '豫', 'ji': '冀', 'hu': '沪', 'jl': '吉', 'sx': '晋', 'lu': '鲁', 'qing': '青', 'zang': '藏', 'e1': '鄂', 'meng': '蒙', 'gan1': '甘','qiong': '琼', 'shan': '陕', 'min': '闽', 'su': '苏', 'xin': '新','wan': '皖', 'jing': '京', 'xiang': '湘', 'gui': '贵', 'yu1': '渝', 'jin': '津', 'gan': '赣', 'yue': '粤', 'gui1': '桂', 'liao': '辽'}name_dict = {}for key, val in label_dict.items():      # 本次转换的目的是转换字母和汉字name_dict[key] = temp.get(val, val)return name_dict
name_dict = match_labels(label_dict)    # 获取转换标签的字典
divide_picture(INFER_PATH)              # 按车牌字符划分图片
display(Image.open(INFER_PATH))         # 展示预测车牌

  • 载入模型并开始预测
model.eval()                 # 开启评估模式
model.set_state_dict(paddle.load(MODEL_PATH)
)   # 载入预训练模型参数infer_label = ""                         # 存储预测结果
for i in range(8):if i == 2:     # 跳过蓝牌中的“•”号infer_label += "•"continuechar_img = load_image(TEMP_PATH + "/%d.png" % i)result = model(char_img)            # 模型预测,返回一个概率数组lab = np.argmax(result.numpy())     # 返回数组result中的最大值的索引值infer_label += name_dict[str(lab)]  # 将字符的预测结果加入到总结果中
print("\n该车牌的预测结果为:", infer_label)     # 展示预测结果

模型预测结果如下:

该车牌的预测结果为: 苏A•UP678

写在最后

  • 如果您发现项目存在问题,或者如果您有更好的建议,欢迎在下方评论区中留言讨论~
  • 这是本项目的链接:实验项目 - AI Studio,点击fork可直接在AI Studio运行~
  • 这是我的个人主页:个人主页 - AI Studio,来AI Studio互粉吧,等你哦~
  • 【友链滴滴】欢迎大家随时访问我的个人博客~

http://chatgpt.dhexx.cn/article/NLUiOnL8.shtml

相关文章

LeNet5—论文及源码阅读

LeNet5实现图像分类 &#x1f42c; 目录: 一、概论二、论文选读三、源码精读 所用数据集介绍LeNet5模型网络构建LeNet5模型训练 测试代码 四、参考资料 一、概论 LeNet-5是一种经典的卷积神经网络结构&#xff0c;于1998年投入实际使用中。该网络最早应用于手写体字符识别应…

重学深度学习系列---LeNet5实现手写数字识别(TensorFlow2-mnist数据集)

重学深度学习系列—LeNet5实现手写数字识别(TensorFlow2-mnist数据集) 文章目录 重学深度学习系列---LeNet5实现手写数字识别(TensorFlow2-mnist数据集)我的环境&#xff1a;一、LeNet5简单介绍二、LeNet-5代码实现三、训练四、对图片进行预测五、训练过程截图&#xff1a;参考…

经典网络-LeNet-5

上图展示展示了 LeNet-5 模型的架构。 是Yann LeCun 教授于1998 年在论文Gradient-based learning applied to document recognition中提出的&#xff0c;它是第一个成功应用于数字识别问题的卷积神经网络。 LeNet-5 详细结构 在下面的篇幅中将详细介绍LeNet-5 模型每一层的…

深度学习---卷积神经网络之LeNet5(TensorFlow 代码实现)

一、前言 1.1 使用全连接神经网络对图像进行处理存在的问题 1、需要处理的数据量大&#xff0c;效率低 现在的图像都有着极高的像素&#xff0c;假设一张需要处理的图片像素是 1000 * 1000 * 3(彩色图片&#xff0c;具有 RGB 3 个通道)&#xff0c;使用具有 100 个隐藏单元的…

浅谈LeNet-5

浅谈LeNet-5 基于Tensorflow的实现欢迎查看我下一篇博客Tensorflow实战 LeNet-5神经网络进行手写体数字识别 一、LetNet是什么&#xff1f; LetNet是一种入门级的神经网络模型&#xff0c;是一个简单的卷积神经网络&#xff0c;可以用来做手写体识别。 下面我将以上图为例简…

卷积神经网络CNN与LeNet5详解(可训练参数量、计算量、连接数的计算+项目实战)

文章目录 神经网络CNN卷积神经网络CNN的由来局部感受野共享权重池化CNN的结构光栅化 LeNet5详解LeNet5-C1层LeNet5-S2层LeNet5-C3层LeNet5-S4层LeNet5-C5层LeNet5-F6层LeNet5-OUTPUT层计算公式 LeNet5实战定义网络模型初始化模型参数训练测试准确率预测结果 神经网络 神经网络可…

lenet5实现

Pytorch环境下搭建lenet5网络实现手写数字识别 &#xff08;文章采用cuda9.0cudnn7.4pytorch1.6环境&#xff09; 数据集选用EMNIST dataset中的手写数据集&#xff0c;参考链接如下&#xff1a; 数据集下载地址 代码部分参考S.E作者的pytorch实现手写英文字母识别&#xff0…

从零开始的神经网络构建历程(三,LeNet5复现)

前两篇博文主要介绍了torch如何构建全连接前馈神经网络&#xff0c;本篇博客主要针对经典卷积神经网络LeNet5进行复现。 卷积神经网络的基本结构 相信不少人都看过不少博客&#xff0c;也都对卷积神经网络的大致结构了解一点&#xff0c;这里本人站在神经元的角度来描述卷积神…

卷积神经网络LeNet5结构

LeNet5可以说是最早的卷积神经网络了&#xff0c;它发表于1998年&#xff0c;论文原文Gradient-Based Learning Applied to Doucment Recognition作者是Yann Le Cun等。下面对LeNet5网络架构进行简单的说明&#xff0c;有兴趣的同学可以去参考原文&#xff0c;论文原文地址http…

Lenet5(1998)

先读 https://blog.csdn.net/zhangjunhit/article/details/53536915 https://blog.csdn.net/qianqing13579/article/details/71076261 第三个卷积层&#xff0c;S2 6个1010的特征映射&#xff0c;C3是16个1010的特征映射&#xff0c;怎么做的呢&#xff1f; 关注C3层 Why not …

LeNet5的论文及理解

LeNet5网络的来源&#xff1a;Lcun Y, Bottou L, Bengio Y, et al. Gradient-based learning applied to document recognition[J]. Proceedings of the IEEE, 1998, 86(11):2278-2324. 1. 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;基…

Pytorch搭建LeNet5网络

本讲目标&#xff1a;   介绍Pytorch搭建LeNet5网络的流程。 Pytorch八股法搭建LeNet5网络 1.LeNet5网络介绍2.Pytorch搭建LeNet5网络2.1搭建LeNet网络2.2测试LeNet网络输出2.3下载数据集2.4加载并配置网络2.5训练并保存网络2.6测试图片 1.LeNet5网络介绍 借鉴点&#xff1a;…

HLS:卷积神经网络LeNet5的实现与测试

目录 一、引言二、LeNet5的学习三、数学知识补充四、HLS代码设计五、仿真综合与优化六、Zynq平台搭建测试七、一些注意点八、文献时间线与后续工作 一、引言 1、开发环境。 Windows10、Vivado2018.2、Vivado HLS与Xilinx SDK。 2、LeNet5概述。 1994年&#xff0c;CNN网络&…

卷积神经网络(一):LeNet5的基本结构

在机器视觉&#xff0c;图像处理领域&#xff0c;卷积神经网络取得了巨大的成功。本文将参考UFLDL和DEEPLEARNING.NET的教程&#xff0c;结合自己的理解&#xff0c;梳理一下卷积神经网络的构成以及其BP算法的求解。虽然利用theano可以方便的实现LeNet5&#xff0c;但是不利于学…

LeNet5网络结构详解

文章目录 1.论文地址&#xff1a;2.LeNet5网络结构&#xff1a;3.首先了解参数量和计算量之间的区别和计算&#xff1a;&#xff08;1&#xff09;参数量&#xff08;Params&#xff09;&#xff1a;&#xff08;2&#xff09;计算量&#xff08;FLOPS&#xff09;:&#xff08…

经典网络模型介绍系列——LeNet-5

从今天开始&#xff0c;带大家从LeNet5开始学习经典的网络模型。 一、LeNet-5 LeNet-5是LeNet系列的最终稳定版&#xff0c;它被美国银行用于手写数字识别&#xff0c;该网络有以下特点&#xff1a; 所有卷积核大小均为5*5&#xff0c;步长为1&#xff1b;所有池化方法为平均…

LeNet5的深入解析

论文&#xff1a;Gradient-based-learning-applied-to-document-recognition 参考&#xff1a;http://blog.csdn.net/strint/article/details/44163869 LeNet5 这个网络虽然很小&#xff0c;但是它包含了深度学习的基本模块&#xff1a;卷积层&#xff0c;池化层&#xff0c;…

LeNet5模型讲解

加粗样式LeNet5模型讲解 LeNet5模型总览 总共8层网络&#xff0c;分别为&#xff1a; 输入层&#xff08;INPUT&#xff09;、卷积层&#xff08;Convolutions,C1&#xff09;、池化层&#xff08;Subsampling&#xff0c;S2&#xff09;、卷积层&#xff08;C3&#xff09;、…

LeNet-5详解

一、前言 LeNet-5出自论文Gradient-Based Learning Applied to Document Recognition&#xff0c;是一种用于手写体字符识别的非常高效的卷积神经网络。 本文将从卷积神经网络结构的基础说起&#xff0c;详细地讲解每个网络层。 论文下载&#xff1a;请到文章结尾处下载。 …

卷积神经网络:LeNet-5

2021SCSDUSC LeNet-5卷积神经网络的整体框架&#xff1a; 特征映射&#xff1a;一幅图在经过卷积操作后得到结果称为特征图。 LeNet-5共有8层&#xff0c;包含输入层&#xff0c;每层都包含可训练参数&#xff1b;每个层有多个特征映射(&#xff0c;每个特征映射通过一种卷积…