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

项目背景

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

分析项目

项目中需要我们通过图像识别能够检测出京牌车号,既然是图像识别,那就离不了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 torchlprnet = 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 osdirectory = '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 + '.' + suffixshutil.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 + '.' + suffixshutil.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主页:还没整理好呢,先不要着急!

最后感谢阅读!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Altium Designer——第一课

一个电子设计包含四个部分: 1.原理图库的设计 2.原理图的设计 3.PCB封装库的设计 4.PCB布局和PCB布线的设计 电子设计工程包含的部分: Free documents: 在我们保存AD工程的文件夹中,如果打开的是prjpcb后缀的工程文件&#xf…

《Fundamentals of Power Electronics》——基础交流建模方法

PWM整流器小信号交流模型建模的主要步骤为: (a)利用小纹波近似的动态版本,建立与电感和电容波形的低频平均值有关的方程; (b)平均方程的扰动和线性化; (c)交流等效电路模型的建立。 以下图buck-boost电路为例进行分析。 首先测…

二叉树的迭代遍历 | LeetCode 144. 二叉树的前序遍历、LeetCode 94. 二叉树的中序遍历、LeetCode 145. 二叉树的后序遍历

二叉树的前序遍历(迭代法) 1、题目 题目链接:144. 二叉树的前序遍历 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 示例 1: 输入:root [1,null,2,3] 输出:[1,2,3]示例 2&#x…

【Java】实现一个简单的线程池

📝个人主页:哈__ 期待您的关注 目录 ​编辑 一、线程池的模式 二、线程池的一些参数 三、代码实现 1.BlockingQueue 2.ThreadPool 四、拒绝策略 一、线程池的模式 线程池顾名思义就是管理线程的一个池子,我们把创建线程的过程交给…

Neo4j v5 中 Cypher 的变化

How Cypher changed in Neo4j v5 Neo4j v5 中 Cypher 的变化 几周前,Neo4j 5 发布了。如果你像我一样,在 Neo4j 4 的后期版本中忽略了所有的弃用警告,你可能需要更新你的 Cypher 查询以适应最新版本的 Neo4j。幸运的是,新的 Cyp…

SpringBoot自定义定时任务

通常,在我们的项目中需要定时给前台发送一些提示性消息或者我们想要的定时信息,这个时候就需要使用定时任务来实现这一功能,实现也很简单,接下来具体来看看吧~ 简单定时任务 首先,你需要在你的启动类上加上开启定时任…

Python-VBA函数之旅-oct函数

目录 一、oct函数的常见应用场景 二、oct函数使用注意事项 三、如何用好oct函数? 1、oct函数: 1-1、Python: 1-2、VBA: 2、推荐阅读: 个人主页:神奇夜光杯-CSDN博客 一、oct函数的常见应用场景 oc…

docker部署nginx并实现https

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

WORD排版常见问题与解决方案

前言 近期使用word软件进行论文排版工作,遇到了一些常见的问题,记录一下,避免遗忘。 基本配置 系统环境:win10/win11 word版本:Microsoft Office LTSC 专业增强版 2021 问题与解决方案 问题1:页眉显示内…

【STM32F407+CUBEMX+FreeRTOS+lwIP netconn UDP TCP记录】

STM32F407CUBEMXFreeRTOSlwIP netconn UDP TCP记录 注意UDPUDP1UDP2 TCPTCP clientTCP server图片 注意 1、超时 #include “lwipopts.h” #define LWIP_SO_RCVTIMEO 12、先保证能ping通 3、关于工程创建可参考 【STM32F407CUBEMXFreeRTOSlwIP之UDP记录】 4、…

C语言之数据结构之栈和队列的运用

目录 1. 用队列实现栈1.1 思路讲解1.2 代码实现 2. 用栈实现队列1.1 思路讲解1.2 代码实现 总结 •͈ᴗ•͈ 个人主页:御翮 •͈ᴗ•͈ 个人专栏:C语言数据结构 •͈ᴗ•͈ 欢迎大家关注和订阅!!! 1. 用队列实现栈 题目描述: 请你仅使用两个…

恶补《操作系统》5_2——王道学习笔记

5.2_1 I-O核心子系统 1、用户层软件 假脱机系统 2、设备独立性软件(设备无关性软件) IO调度、设备保护、设备分配与回收、缓冲区管理 3、设备驱动程序(比如打印机驱动) 4、中断处理程序 5、硬件 5.2_2 假脱机技术&#xff…

小程序引入 Vant Weapp 极简教程

一切以 Vant Weapp 官方文档 为准 Vant Weapp 官方文档 - 快速入手 1. 安装nodejs 前往官网下载安装即可 nodejs官网 安装好后 在命令行(winr,输入cmd)输入 node -v若显示版本信息,即为安装成功 2. 在 小程序根目录 命令行/终端…

www.fastssh.com SSH over WebSockets with CDNs

https://www.fastssh.com/page/create-ssh-cdn-websocket/server/这其实不是标准的websocket报文(服务器响应报文无Sec-Websocket-Accept字段),所以无法使用github.com/gorilla/websocket包:GET / HTTP/1.1 Host: hostname:8080 User-Agent: Go-http-cli…

jvm重要参数可视化和线上问题排查

jvm重要参数可视化和线上问题排查 目标jvm参数分类(了解)运行时数据区相关的(jdk1.8)处理 OOM 相关的垃圾回收器相关的GC 日志记录相关的意义,默认值,调优原则(重要, 待拆分) 排查 OOM 流程 和 常见原因参考文章 目标 …

【除了协程还有哪些方式可以实现异步编程】

在Unity中,除了使用协程实现异步编程外,还有以下几种方法: 异步加载资源: 使用UnityWebRequest类进行异步加载资源,这在加载网络资源或动态加载资源时非常有用。 using UnityEngine; using UnityEngine.Networking;…

基于OpenCv的图像特征点检测

⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计3077字,阅读大概需要3分钟 🌈更多学习内容, 欢迎👏关注👀【文末】我的个人微信公众号&#xf…

C语言/数据结构——(用双链表实现数据的增删查改)

一.前言 嗨嗨嗨,大家好久不见!前面我们已经通过数组实现数据的增删查改、单链表实现数据的增删查改,现在让我们尝试一下使用双链表实现数据的增删查改吧! 二.正文 如同往常一样,对于稍微大点的项目来说,…

【工程记录】Python爬虫入门记录(Requests BeautifulSoup)

目录 写在前面1. 环境配置2. 获取网页数据3. 解析网页数据4. 提取所需数据4.1 简单提取4.2 多级索引提取 5. 常见问题 写在前面 仅作个人学习与记录用。主要整理使用Requests和BeautifulSoup库的简单爬虫方法。在进行数据爬取时,请确保遵守相关法律法规和网站的服务…

FLIR LEPTON3.5 热像仪wifi 科研实验测温采集仪

点击查看详情!点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情 1、描述 这是一款桌面科研实验测温热成像多功能热像记录仪,小巧轻便…