RK3568笔记二十七:LPRNet车牌识别

若该文为原创文章,转载请注明原文出处。

记录自训练并在RK3568上部署。

一、介绍

LPRNet的Pytorch实现,一种高性能和轻量级的车牌识别框架。完全适用于中国车牌识别(Chinese License Plate Recognition)及国外车牌识别!
目前仅支持同时识别蓝牌和绿牌,即新能源车牌等中国车牌,但可通过扩展训练数据或微调支持其他类型车牌及提高识别准确率!

该网络的特点:

1、不需要对字符进行预分割,是一个端到端的轻量化字符识别模型,速度快,精度还不错;这里主要是因为仿照squeezenet和inception的思想设计了一个轻量化的卷积模块。
2、仿照的还是经典的CRNN+CTC的思路,不过LPRNet首次将RNN删除了,整个网络只有CNN+CTC Loss。但是也不是说不要上下文信息,只是舍弃了BiLSTM那样的RNN提取上下文,而是在backbone的末尾使用了一个13x1的卷积模块提取序列方向(w)的上下文信息。而且在backbone外还额外使用一个全连接层进行全局上下文特征提取,提取之后再和backbone进行concat特征融合,再输入head。
3、损失使用的CTC Loss、推理应用了贪心算法,搜索取每个位置上类概率的最大值。

二、环境

1、开发板:ATK-DLRK3568

2、系统:buildroot

3、训练环境:Autodl

三、训练和测试

1、训练测试环境搭建

1、创建虚拟环境

conda create -n LRPNet_env python=3.8

2、激活

conda activate LRPNet_env

3、下载代码

git clone https://github.com/sirius-ai/LPRNet_Pytorch.git

注意,使用git克隆方式,不要自己下载解压,经测试自己下载解压文本格式会不同,运行会出错

4、安装依赖项

pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html
pip install imutils
pip install opencv-python

安装后执行测试命令

python test_LPRNet.py

在测试过程中出错了下面的错误:

出错1:

ValueError: num_samples should be a positive integer value, but got num_samples=0

pytorch报错:ValueError: num_samples should be a positive integer value, but got num_samp=0-CSDN博客

shuffle的参数设置错误导致,因为已经有batch_sample了,就不需要shuffle来进行随机的sample了,所以在这里的shuffle应该设置为FALSE才对。

修改:

train_LPRNET.py的208行,TRUE改成False

batch_iterator = iter(DataLoader(datasets, args.test_batch_size, shuffle=False, num_workers=args.num_workers, collate_fn=collate_fn))

出错2:

python 代码遇到 float division by zero 怎么解决?-CSDN博客

File "train_LPRNet.py", line 261, in Greedy_Decode_Eval Acc = Tp * 1.0 / (Tp + Tn_1 + Tn_2) ZeroDivisionError: float division by zero

处理:

if Tp + Tn_1 + Tn_2 == 0:Acc = 0  # 或者 Acc = 1,根据实际需求设置
else:Acc = Tp * 1.0 / (Tp + Tn_1 + Tn_2)
​

出错3:

File "train_LPRNet.py", line 268, in Greedy_Decode_Eval print("[Info] Test Speed: {}s 1/{}]".format((t2 - t1) / len(datasets), len(datasets))) ZeroDivisionError: float division by zero

处理:

if len(datasets) == 0:print("[Info] 数据集为空,无法计算测试速度")
else:print("[Info] Test Speed: {}s 1/{}".format((t2 - t1) / len(datasets), len(datasets)))

出错4:

AttributeError: module 'numpy' has no attribute 'int'. np.int was a deprecated alias for the builtin int. To avoid this error in existing code, use int by itself. Doing this will not modify any behavior and is safe. When replacing np.int, you may wish to use e.g. np.int64 or np.int32 to specify the precision. If you wish to review your current use, check the release note link for additional information.

处理:

pip install numpy==1.19.0

再次执行上面命令

运行正常

2、训练

训练按readme执行下面命令:

 python train_LPRNet.py 

但执行后会出错

原因是没有训练的数据集,为了测试,使用的是自带的测试数据集

 python train_LPRNet.py --train_img_dirs ./data/test/ 

默认训练只有15轮,数据集也不对,所以测试结果无法作准。

3、测试

自带的show显示不能使用,原因是没有插件,修改了显示的内容

修改test_LPRNet.py文件下的show函数

def show(img, label, target):img = np.transpose(img, (1, 2, 0))img *= 128.img += 127.5img = img.astype(np.uint8)lb = ""for i in label:lb += CHARS[i]tg = ""for j in target.tolist():tg += CHARS[int(j)]flag = "F"if lb == tg:flag = "T"# img = cv2.putText(img, lb, (0,16), cv2.FONT_HERSHEY_COMPLEX_SMALL, 0.6, (0, 0, 255), 1)img = cv2ImgAddText(img, lb, (0, 0))#cv2.imshow("test", img)cv2.imwrite("test.jpg", img)print("target: ", tg, " ### {} ### ".format(flag), "predict: ", lb)#cv2.waitKey()#cv2.destroyAllWindows()

 执行下面命令,执行是正常的,但模型不对,原因是数据集太少。

python test_LPRNet.py --show 1

使用官方给的模型,识别率还是挺好的。

官方训练集2W多张,自行训练测试。测试增加到1000轮,有部分可以识别了。

四、导出onnx

创建export_onnx.py文件,内容如下:

import torch.nn as nn
import torch
import os
import sys
import urllib
import urllib.request
import time
import traceback
import numpy as npMODEL_DIR = './weights/'
MODEL_PATH = MODEL_DIR + 'Final_LPRNet_model.pth'# Convert maxpool3d to the class of maxpool2d
class maxpool_3d(nn.Module):def __init__(self, kernel_size, stride):super(maxpool_3d, self).__init__()assert(len(kernel_size)==3 and len(stride)==3)kernel_size2d1 = kernel_size[-2:]stride2d1 = stride[-2:]kernel_size2d2 = (kernel_size[0],kernel_size[0])stride2d2 = (kernel_size[0], stride[0])self.maxpool1 = nn.MaxPool2d(kernel_size=kernel_size2d1, stride=stride2d1)self.maxpool2 = nn.MaxPool2d(kernel_size=kernel_size2d2, stride=stride2d2)def forward(self,x):x = self.maxpool1(x)x = x.transpose(1,3)x = self.maxpool2(x)x = x.transpose(1,3)return x class small_basic_block(nn.Module):def __init__(self, ch_in, ch_out):super(small_basic_block, self).__init__()self.block = nn.Sequential(nn.Conv2d(ch_in, ch_out // 4, kernel_size=1),nn.ReLU(),nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(3, 1), padding=(1, 0)),nn.ReLU(),nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(1, 3), padding=(0, 1)),nn.ReLU(),nn.Conv2d(ch_out // 4, ch_out, kernel_size=1),)def forward(self, x):return self.block(x)class LPRNet(nn.Module):def __init__(self, class_num, dropout_rate):super(LPRNet, self).__init__()self.class_num = class_numself.backbone = nn.Sequential(nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1), # 0nn.BatchNorm2d(num_features=64),nn.ReLU(),  # 2maxpool_3d(kernel_size=(1, 3, 3), stride=(1, 1, 1)),small_basic_block(ch_in=64, ch_out=128),    # *** 4 ***nn.BatchNorm2d(num_features=128),nn.ReLU(),  # 6maxpool_3d(kernel_size=(1, 3, 3), stride=(2, 1, 2)),small_basic_block(ch_in=64, ch_out=256),   # 8nn.BatchNorm2d(num_features=256),nn.ReLU(),  # 10small_basic_block(ch_in=256, ch_out=256),   # *** 11 ***nn.BatchNorm2d(num_features=256),   # 12nn.ReLU(),maxpool_3d(kernel_size=(1, 3, 3), stride=(4, 1, 2)),  # 14nn.Dropout(dropout_rate),nn.Conv2d(in_channels=64, out_channels=256, kernel_size=(1, 4), stride=1),  # 16nn.BatchNorm2d(num_features=256),nn.ReLU(),  # 18nn.Dropout(dropout_rate),nn.Conv2d(in_channels=256, out_channels=class_num, kernel_size=(13, 1), stride=1), # 20nn.BatchNorm2d(num_features=class_num),nn.ReLU(),  # *** 22 ***)self.container = nn.Sequential(nn.Conv2d(in_channels=256+class_num+128+64, out_channels=self.class_num, kernel_size=(1,1), stride=(1,1)),)def forward(self, x):keep_features = list()for i, layer in enumerate(self.backbone.children()):x = layer(x)if i in [2, 6, 13, 22]: keep_features.append(x)global_context = list()for i, f in enumerate(keep_features):if i in [0, 1]:f = nn.AvgPool2d(kernel_size=5, stride=5)(f)if i in [2]:f = nn.AvgPool2d(kernel_size=(4, 10), stride=(4, 2))(f)f_pow = torch.pow(f, 2)f_mean = torch.mean(f_pow)f = torch.div(f, f_mean)global_context.append(f)x = torch.cat(global_context, 1)x = self.container(x)logits = torch.mean(x, dim=2)return logitsdef readable_speed(speed):speed_bytes = float(speed)speed_kbytes = speed_bytes / 1024if speed_kbytes > 1024:speed_mbytes = speed_kbytes / 1024if speed_mbytes > 1024:speed_gbytes = speed_mbytes / 1024return "{:.2f} GB/s".format(speed_gbytes)else:return "{:.2f} MB/s".format(speed_mbytes)else:return "{:.2f} KB/s".format(speed_kbytes)def show_progress(blocknum, blocksize, totalsize):speed = (blocknum * blocksize) / (time.time() - start_time)speed_str = " Speed: {}".format(readable_speed(speed))recv_size = blocknum * blocksizef = sys.stdoutprogress = (recv_size / totalsize)progress_str = "{:.2f}%".format(progress * 100)n = round(progress * 50)s = ('#' * n).ljust(50, '-')f.write(progress_str.ljust(8, ' ') + '[' + s + ']' + speed_str)f.flush()f.write('\r\n')def check_and_download_origin_model():global start_timeif not os.path.exists(MODEL_PATH):print('--> Download {}'.format(MODEL_PATH))url = 'https://github.com/sirius-ai/LPRNet_Pytorch/raw/master/weights/Final_LPRNet_model.pth'download_file = MODEL_PATHtry:start_time = time.time()urllib.request.urlretrieve(url, download_file, show_progress)except:print('Download {} failed.'.format(download_file))print(traceback.format_exc())exit(-1)print('done')if __name__ == "__main__":# Download model if not exist (from https://github.com/sirius-ai/LPRNet_Pytorch/blob/master/weights)check_and_download_origin_model()device = torch.device('cpu')lprnet = LPRNet(class_num=68, dropout_rate=0).to(device)lprnet.load_state_dict(torch.load('./weights/Final_LPRNet_model.pth'))lprnet.eval()torch.onnx.export(lprnet,              torch.randn(1,3,24,94),                         MODEL_DIR + 'lprnet.onnx',   export_params=True,        input_names = ['input'],   output_names = ['output'], )if os.path.exists(MODEL_DIR + 'lprnet.onnx'):print('onnx model had been saved in '+ MODEL_DIR + 'lprnet.onnx')else:print('export onnx failed!')

执行后导出ONNX

python export_onnx.py

五、部署

环境搭建,自行参考前面博客搭建。

下载rknn_model_zoo到虚拟机

把上面生成的onnx拷贝到/home/alientek/rknn_model_zoo/examples/LPRNet/model目录下。

1、导出RKNN模型

python convert.py ../model/lprnet.onnx rk3568

2、板载测试

1、修改GCC_COMPILER

export GCC_COMPILER=/opt/atk-dlrk356x-toolchain/usr/bin/aarch64-buildroot-linux-gnu

修改成自己的路径

2、编译

./build-linux.sh -t rk356x -a aarch64 -d LPRNet


3、运行

把编译后的执行文件通过adb或tftp上传到板子

运行下面命令测试

./rknn_lprnet_demo ./model/lprnet.rknn ./model/test.jpg 

接下来使用结合yolov5把框检测出来在识别。

如有侵权,或需要完整代码,请及时联系博主。

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

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

相关文章

张大哥笔记:赚钱高手养成计划---如何将一份时间产生N份收入?

我们常说的赚钱的四种境界有哪些? 1.靠体力挣钱 2.靠技能挣钱 3.靠知识挣钱 4.靠平台钱生钱 所以对应的收入的模式就会是下面4种模式: 1.一份时间卖1次 2.一份时间卖N次 3.一份时间溢价卖N次 4.购买他人时间为自己所用 时间对于每个人都是相同的…

书生·浦语大模型全链路开源体系-作业1

视频链接:书生浦语大模型全链路开源体系_哔哩哔哩_bilibili 1. LLM发展 LLM是近年来人工智能领域的一个重要发展方向。大型语言模型的历史可以追溯到2017年,当时OpenAI推出了GPT-1(Generative Pre-trained Transformer)模型,这是一个基于Transformer架构的语言生成…

使用OpenVINO™.CSharp.API.Extensions.PaddleOCR NuGet Package快速实现OCR文本识别

PP-OCR是PaddleOCR自研的实用的超轻量OCR系统,可以实现端到端的图像文本检测。为了在C#平台实现使用OpenVINO™部署PP-OCR模型实现文本识别,让更多开发者快速上手PP-OCR项目,基于此,封装了OpenVINO.CSharp.API.Extensions.PaddleO…

【C语言】数据指针地址的取值、赋值、自增操作避坑

【C语言】数据指针的取值、赋值、自增操作避坑 文章目录 指针地址指针自增指针取值、赋值附录:压缩字符串、大小端格式转换压缩字符串浮点数压缩Packed-ASCII字符串 大小端转换什么是大端和小端数据传输中的大小端总结大小端转换函数 指针地址 请看下列代码&#…

嵩山为什么称为五岳之尊

在此之前,人们心目中的五岳之尊一般是东岳泰山。自此以后,观点一定会改变:五岳之尊是中岳嵩山!且听我慢慢道来。 首先将二者进行一下对比—— 中与东的对比,嵩山居中,泰山居东。东方是太阳升起的地方&#…

谷粒商城实战(029 业务-订单支付模块-支付宝支付2)

Java项目《谷粒商城》架构师级Java项目实战,对标阿里P6-P7,全网最强 总时长 104:45:00 共408P 此文章包含第305p-第p310的内容 代码编写 前端代码 这里使用的是jsp 在这里引用之前配置的各种支付信息 在AlipayConfig.java里 这里是调用阿里巴巴写…

AI大模型日报#0527:豆包大模型披露评测成绩、天工AI日活超100万、AI初创集体跳槽OpenAI

导读:AI大模型日报,爬虫LLM自动生成,一文览尽每日AI大模型要点资讯!目前采用“文心一言”(ERNIE 4.0)、“零一万物”(Yi-Large)生成了今日要点以及每条资讯的摘要。欢迎阅读&#xf…

PyQt5-新手避坑指南(持续更新)

文章目录 一.前言二.开发环境三.坑1.程序没有详细报错就退出了2.qrc资源文件的使用3.QLabel文字自动换行4.图片自适应大小5.checkbox自定义样式后✓不见了6.多线程 四.记录 一.前言 本篇博客整理了一些初学者容易犯的…

技术贴 | Query 物理计划构建指南

在往期博客《执行器 - Query 执行详解》中,我们介绍到到一条 Query 的 SQL 语句需要经过:词法分析 —— 生成 AST 语法树 —— 生成物理计划。本期博客我们接续上篇讲解一条 Query 语句物理计划的具体结构,以及如何构建物理计划。 物理计划是…

Jmeter环境安装(超级简单)

Jmeter的安装是非常简单的,只需要将下载的安装包解压后,就可以运行了!! 一、首先要下载Jmeter 1.1、官网下载: 下载最新版:https://jmeter.apache.org/download_jmeter.cgi https://jmeter.apache.org/…

论文阅读》学习了解自己:一个粗略到精细的个性化对话生成的人物感知训练框架 AAAI 2023

《论文阅读》学习了解自己:一个粗略到精细的个性化对话生成的人物感知训练框架 AAAI 2023 前言 简介研究现状任务定义模型架构Learning to know myselfLearning to avoid Misidentification损失函数实验结果消融实验 前言 亲身阅读感受分享,细节画图解释…

代码随想录算法训练营第四十一天 | 理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

理论基础 代码随想录 视频:从此再也不怕动态规划了,动态规划解题方法论大曝光 !| 理论基础 |力扣刷题总结| 动态规划入门_哔哩哔哩_bilibili 动归五部曲 1.dp数组以及下标的含义 2.递推公式 3.dp数组如何初始化 4.遍历顺序(例如先背包再…

java_方法重写(覆盖)

介绍 现在我们来试一下 代码 父类 package b;public class father_ {//father class//4attributepublic void cry() {System.out.println("小动物叫唤");} } package b; 子类 public class graduate extends father_ {public void cry() {//子类方法//非私有的属性和…

有哪些和excel类似或基于excel扩展的软件?

Workfine数字化管理平台是一款易上手、便捷、高效的数字化管理工具,是类excel设计,更容易上手进行企业业务系统的搭建,在信息记录和表格管理方面,比excel更简单易用,在这里,给大家挑几个点展示下~ 首先表格…

一键恢复,U盘被删除文件方法分享

U盘是一种轻巧便携的移动储存工具,在日常的工作以及学习过程中,我们经常性会使用它来传输、备份、存储一些重要文件。然而,随着后期使用频率的增多,会在不同的设备上来回插拔,也就给里面存储文件带来了很大的隐患。比方…

I.MX6ULL的蜂鸣器实验-GPIO输出实验

系列文章目录 I.MX6ULL的蜂鸣器实验 I.MX6ULL的蜂鸣器实验 系列文章目录一、前言二、有源蜂鸣器简介三、硬件原理分析四、程序编写4.1程序编写前提工作4.2程序编写 五、编译下载验证5.1编写 Makefile 和链接脚本5.2编译下载 一、前言 在 I.MX6U-ALPHA 开发板上有一个有源蜂鸣器…

九宫格转圈圈抽奖活动,有加速,减速效果

在线访问demo和代码在底部 代码&#xff0c;复制就可以跑 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><tit…

Thingsboard规则链:GPS Geofencing Filter节点详解

​​​​​​​ 引言 GPS Geofencing Filter节点具体作用 使用教程 源码浅析 应用场景与案例 物流与运输 农业智能化 城市安全管理 结语 引言 在物联网技术迅速发展的今天&#xff0c;精准的位置服务已成为诸多应用不可或缺的一环。作为物联网平台的佼佼者&…

【机器学习】随机梯度下降算法以及优化

一、概述&#xff1a; 什么是梯度下降&#xff1f; 梯度下降法的基本思想可以类比为一个下山的过程。 假设这样一个场景:一个人被困在山上&#xff0c;需要从山上下来(i.e.找到山的最低点&#xff0c;也就是山谷)。但此时山上 的浓雾很大&#xff0c;导致可视度很低。因此&am…

【Postman接口测试】第一节.接口测试基础认识

文章目录 前言一、接口的基础 1.1 什么是接口 1.2 软件为什么需要接口 1.3 为什么要做接口测试二、接口测试的基础 2.1 接口测试介绍 2.2 接口测试的实现方式三、接口返回数据和JSON详解四、接口测试协议详解总结 前言 一、接口的基础知识 1.1 什么是…