节能洗车房车牌识别项目实战

项目背景

学电子信息的你加入了一家节能环保企业,公司的主营产品是节能型洗车房。由于节水节电而且可自动洗车,产品迅速得到了市场和资本的认可。公司决定继续投入研发新一代产品:在节能洗车房的基础上实现无人值守的功能。新产品需要通过图像识别检测出车牌号码,车主通过扫码支付后,洗车房的卷帘门自动开启。新产品的研发由公司总工亲自挂帅,他对团队寄予厚望,作为人工智能训练师,你被分在图像识别团队。项目经理为你提供了数百张原始车牌图片,并配备了一位资深算法工程师为你提供预训练模型,要求你在硬件设计定稿打样之前,完成车牌识别的模型训练,能够识别京牌车号,并部署到边缘计算设备上测试通过。为了给后续实际上线工作提供可靠的基础,你的工作需要在一周内完成,请尽快开始。

分析项目

项目中需要我们通过图像识别能够检测出京牌车号,既然是图像识别,那就离不了cnn,而且里面还有一个要求是部署到边缘计算设备上,那么我们的网络及模型对硬件算力的要求就不能太高。这里我先想到的了LeNet,它输入数据大小是32*32的,也就是说拿到一张车牌图片,需要有以下步骤:切分图片、依次传入网络识别、拼接输出结果。

LeNet网络结构LeNet网络结构图

内心:“哦?就这三步骤?分分钟搞完。开始第一步:切分图片,好的第一步不会,项目崩溃“

后两步还好,难就难在切分图片,切分依据是什么?如何编写切分程序?切分程序能否在大多数据集上表现效果好?

🤔难道就要放弃了吗,要放弃了吗,真的要放弃了吗,不!我还有大招没放。之前参加过一个比赛也是关于车牌识别检测的,它给的数据集车牌图片是人在路边拍的,需要先裁剪出车牌图片再进行识别,记得当时做那个项目用了Yolo7 和 paddleocr这两个开源技术。不过这个洗车房项目里只要求了完成识别所以就免去第一步了,直接对车牌去进行识别。好的,识别这里我用LPRNet代替paddleocr

为什么要用LPRNet呢?原因如下:

  • LPRNet模型大小仅有1.8兆,非常轻量
  • LPRNet也是上次比赛中学习到的,那会我也去尝试了几遍,可是我测试集的识别准确率就是很低,那个博主的准确率高达97%,不服绝对不服,一定是我上次的打开方式不对哈哈哈哈,带着上次的不解与困惑让我再试一次

那最终就确立LPRNet为本次项目的主体

项目流程

1.部署LPRNet到本地

2.LPRNet项目分析

3.准备数据集

4.训练模型


搭建项目

1.部署LPRNet到本地

下载LPRNet

LPRNet下载地址icon-default.png?t=N7T8https://github.com/sirius-ai/LPRNet_Pytorch

安装模块

README文件里有说

跑通测试代码

data文件夹下自带了1000张测试图片,运行项目根目下的 test_LPRNet.py 尝试跑通

test_LPRNet.py中的 show函数会起到一个阻塞作用,可以将参数show置为False

但直接将show置为False后,就不会展示以下每张图片的预测效果

若想看到预测效果可以在show函数内部进行部分注释,show参数那要置为True

2.LPRNet项目分析

总的来看,这个项目是比较简单的,代码只有这4个文件

  • load_data.py
  • LPRNet.py
  • test_LPRNet.py
  • train_LPRNet.py

现在我想知道的是它的输入图片大小、标签格式

load_data.py中有resize方法,且与test_LPRNet.py、train_LPRNet.py相关联,表示当我的图片像素大小不为94*24的时候,会调用该方法重置图片大小。(它不会覆盖原有输入数据集的图片,只会使输入其他大小的图片不会报错)

标签的格式即图片文件名

LPRNet网络结构,直接看代码会比较难理解,可以结合图片去看

使用以下代码得到可视化onnx模型,导入到网页 https://netron.app/

from LPRNet import build_lprnet
import torch

lprnet = build_lprnet(lpr_max_len=8, phase=True, class_num=68, dropout_rate=0.5)
device = torch.device("cuda:0" if torch.cuda else "cpu")
lprnet.to(device)
print("Successful to build network!")

inputs = (1, 3, 24, 94)
input_data = torch.randn(inputs).to(device)
torch.onnx.export(lprnet, input_data, 'lpr.onnx')

3.准备数据集

我用的数据集是CBLPRD-330k,用代码将里面为京牌的图片提取出来,有9000多张

import logging
import random
import os
import shutil

# 需要修改为自己的实际路径
datas = open('../data.txt', 'r').readlines()
total = len(datas)
# random.shuffle(datas)

print('开始过滤数据····')
jing = []
blue = []
green = []
for data_txt in datas:
    directory, licen, color = data_txt.split()
    if licen[0] == '京':
        if not licen[1].isdigit():
            if not licen[-1] == '挂':
                if not licen[-1] == '学':
                    print(licen)
                    if len(licen) == 7:
                        blue.append(licen)
                    elif len(licen) == 8:
                        green.append(licen)
                    else:
                        logging.warning(len(licen))
                    jing.append(licen)
                    new_dir = '京/' + licen + '.jpg'
                    shutil.copy(os.path.join('../', directory), new_dir)
print('total:', len(jing))
print('blue:', len(blue))
print('green:', len(green))

划分数据集(train、test)

import shutil
import random
from tqdm import tqdm
import os

directory = 'data'
datas = os.listdir(directory)
random.shuffle(datas)
total = len(datas)
# 90 10
train_data = datas[:int(0.9 * total)]
test_data = datas[int(0.9 * total):]

if not os.path.exists('train'):
    os.mkdir('train')

if not os.path.exists('test'):
    os.mkdir('test')

for data in tqdm(train_data):
    licen, suffix = data.split('.')
    new_dir = 'train/' + licen + '.' + suffix
    shutil.copy(os.path.join(directory, data), new_dir)
print('exec train over')

for data in tqdm(test_data):
    licen, suffix = data.split('.')
    new_dir = 'test/' + licen + '.' + suffix
    shutil.copy(os.path.join(directory, data), new_dir)
print('exec test over')

4.训练模型

虽然有9000张,其实也不算多,所以这里要用迁移学习思想

tips:比赛那次识别度低原因就是没有用迁移学习,全部参数传入,随着训练的进行,模型中的可学习参数层的参数都发生改变导致的🤧

  1. 将weights目录下自带的Final_LPRNet_model.pth重名为Pre_LPRNet_model.pth,将参数pretrained_model指定为预训练模型Pre_LPRNet_model.pth
  2. 冻结网络主干部分,仅训练输出层
    # 将不更新的参数的requires_grad设置为False
    for name, param in lprnet.backbone.named_parameters():
        param.requires_grad = False
    # 仅把需要更新的模型参数传入optimizer
    optimizer = optim.RMSprop(lprnet.container.parameters(), lr=args.learning_rate, alpha=0.9, eps=1e-08,momentum=args.momentum, weight_decay=args.weight_decay)
  3. (可选)加入tensorboard可视化训练
    from torch.utils.tensorboard import SummaryWriter
    
    # 加入到训练的地方(只加入了loss变化图)
    writer.add_scalar('loss', scalar_value=loss.item(), global_step=epoch)
    writer.close()
    
    # 可以定义在 if __name__ == "__main__": 里
    writer = SummaryWriter()

指定参数,绿色箭头表示一些常用的参数

运行train_LPRNet.py进行训练

5.测试

我这里训练了14轮,cpu上测试得到的效果 800多张图片准确率在86%~87%

在15轮之后,loss值会反弹的很厉害,效果会变差

项目总结

通过这个项目,又学到了一些新的东西,巩固实践了旧的知识。还有就是写这个总结挺费劲的,写总结花费的时间比我做项目的时间还要久····

项目拓展

洗车房项目所使用的数据集图像大小全部为128 * 48,这种图片相当于贴脸拍了

现在的需求:当给到一张大图片的时候,也能够识别出来、识别准确。

就是说,我现在拿自己手机去外面拍一张,只要这张照图片里有车牌都要能够识别到

这就需要使用YOLO先对目标(车牌)进行定位,再通过LPRNet识别,好!开整!

1.训练车牌识别模型

这次我用yolov5来做,就不写那么详细了,类似的训练详解我之前博客有可以去看

数据有限,网上找了36张图片标注训练,15张图片测试

测试图片

测试视频

有车牌识别模型了,看上去效果还不错

2.把LPRNet加入到yolov5的detect.py中

改了一下午,此时00:10终于完成啦!现在可识别图片也可以识别视频流,而且可以显示中文哦!

由于detect.py代码篇幅较长,改动的地方解释起来不是很好理解,也怕误导读者,所以就不在此解读了,如果你们对这个拓展感兴趣,等我整理好后会放到我的github主页,你们可以直接拿去调试

我的github主页:还没整理好呢,先不要着急!

最后感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/592589.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java高阶私房菜:JVM性能优化案例及讲解

目录 核心思想 优化思考方向 压测环境准备 堆大小配置调优 调优前 调优后 分析结论 垃圾收集器配置调优 调优前 调优后 分析结论 JVM性能优化是一项复杂且耗时的工作,该环节没办法一蹴而就,它需要耐心雕琢,逐步优化至理想状态。“…

Qt服务器端与客户端交互

Qt做客户端与服务器端交互第一步引入network 第一步引入network后继续编程首先界面设计 创建server和socket 引入QTcpServer&#xff0c;QTcpSocket MainWindow.h代码如下 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QTcpServer&…

EPAI手绘建模APP演示板、材质编辑器、样式编辑器

(11) 更多 图 74 更多工具栏 ① 演示板&#xff1a;打开关闭演示板。演示板用来显示从设备导入的模型图纸图片或者打开模型建模教程网页&#xff0c;是建模过程中一个辅助功能。有些设备有小窗口功能有些没有&#xff0c;对于没有小窗口功能的设备&#xff0c;通过演示板能够在…

智慧旅游引领旅游行业创新发展:借助智能科技的力量,实现旅游资源的优化配置和高效利用,推动旅游行业的转型升级和可持续发展

目录 一、引言 二、智慧旅游的定义与特点 1、信息化程度高 2、智能化服务丰富 3、互动性强 4、个性化服务突出 5、可持续性发展 三、智慧旅游在旅游行业创新发展中的作用 &#xff08;一&#xff09;优化旅游资源配置 &#xff08;二&#xff09;提升旅游服务质量 &…

【吃透Java手写】- Spring(上)-启动-扫描-依赖注入-初始化-后置处理器

【吃透Java手写】Spring&#xff08;上&#xff09;启动-扫描-依赖注入-初始化-后置处理器 1 准备工作1.1 创建自己的Spring容器类1.2 创建自己的配置类 ComponentScan1.3 ComponentScan1.3.1 Retention1.3.2 Target 1.4 用户类UserService Component1.5 Component1.6 测试类 2…

HCIA-题目解析1

0x00 前言 遇到这样一道题,这种题目对于我来说还是比较复杂的,所以记录一下。主要还是和熟练度有关系。 0x01 题目 路由器RouterID邻居关系如下,下列说法正确的是 A:本路由器和Router-lD为10.0.3.3的路由器不能直接交换链路状态信息 B:DR路由器的Router-lD为10.0.1.2 C:…

机器学习:基于K-近邻(KNN)、高斯贝叶斯(GaussianNB)、SVC、随机森林(RF)、梯度提升树(GBDT)对葡萄酒质量进行预测

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

Finder Windows for Mac:双系统窗口,一键切换!

Finder Windows for Mac是一款专为Mac用户设计的实用工具&#xff0c;它模拟了Windows系统的窗口管理功能&#xff0c;让Mac用户也能享受到类似Windows的窗口操作体验。这款软件的主要功能是提供一个浮动面板&#xff0c;帮助用户随时即时访问打开的Finder窗口列表&#xff0c;…

力扣每日一题106:从中序与后序遍历序列构造二叉树

题目 中等 相关标签 相关企业 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], postorder …

在线协作,开源的设计和原型创作平台:penpot

penpot&#xff1a;面向团队&#xff0c;设计自由- 精选真开源&#xff0c;释放新价值。 概览 Penpot 是一款专为跨职能团队量身定制的开源设计软件&#xff0c;与行业领先的 Figma 齐名&#xff0c;提供了一个强大而灵活的在线设计解决方案。其最大的亮点在于&#xff0c;用户…

Qt与MySQL连接

QT连接Mysql数据库&#xff08;详细成功版&#xff09;-CSD N博客 我的MySQL是64位的&#xff0c;所以我的Qt的套件也需要是64位的 遇到的问题&#xff1a; &#xff08;available drivers中已经有QMYSQL QMYSQL3&#xff0c;还是not loaded&#xff09; QSqlDatabase: QMYS…

SQL注入-基础知识

目录 前言 一&#xff0c;SQL注入是什么 二&#xff0c;SQL注入产生的条件 三&#xff0c;学习环境介绍 四、SQL注入原理 五&#xff0c;SQL中常用的函数 六&#xff0c;关于Mysql数据库 前言 在网络安全领域中&#xff0c;sql注入是一个无法被忽视的关键点&#xff0c…

安卓 app icon大小 安卓app界面尺寸大小

移动应用的界面设计画布尺寸设计多大&#xff08;特别是Android&#xff09;、图标和字体大小怎么定、需要设计多套设计稿么、如何切图以配合开发的实现&#xff1f; 本篇将结合iOS和android官方的设计规范、搜集的资料以及工作中的摸索&#xff0c;来分享移动应用界面设计中的…

【C++】STL — List的接口讲解 +详细模拟实现

前言&#xff1a; 本章我们将学习STL中另一个重要的类模板list… list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是带头双向循环链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xf…

Conntroller内存马详解(2)

流程分析 获取context 第一种&#xff1a;getCurrentWebApplicationContext() // getCurrentWebApplicationContext方法获得的是一个XmlWebApplicationContext实例类型的Root WebApplicationContext。WebApplicationContext context ContextLoader.getCurrentWebApplication…

docker部署nginx并实现https

文章目录 docker部署nginx并实现https1、服务器环境2、安装docker3、准备证书4、准备nginx配置文件和dockerfile文件5、创建nginx镜像与容器6、验证访问 docker部署nginx并实现https 1、服务器环境 [rootliuyanfen12 ~]#systemctl stop firewalld [rootliuyanfen12 ~]#setenf…

Flutter笔记:美工设计.导出视频到RIVE

Flutter笔记 美工设计.导出视频到RIVE - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28…

RabbitMQ之顺序消费

什么是顺序消费 例如&#xff1a;业务上产生者发送三条消息&#xff0c; 分别是对同一条数据的增加、修改、删除操作&#xff0c; 如果没有保证顺序消费&#xff0c;执行顺序可能变成删除、修改、增加&#xff0c;这就乱了。 如何保证顺序性 一般我们讨论如何保证消息的顺序性&…

【Linux】进程的隔离和控制:namespace 隔离、cgroup 控制

文章目录 五、namespace 隔离dd -- 读取、转换并输出数据mkfs -- 格式化文件系统df -- 显示文件系统磁盘使用情况mount -- 加载文件系统到指定的加载点unshare -- 创建子进程&#xff0c;同时与父程序不共享namespace一个 demo 六、cgroup(Control Group) 相关命令pidstat -- 监…

【LeetCode刷题记录】230. 二叉搜索树中第K小的元素

230 二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,1,4,null,2], k 1 输出&#xff1…