LPRNet 车牌识别部署 rk3588(pt-onnx-rknn)包含各个步骤完整板端代码

  虽然车牌识别技术很成熟了,但完全没有接触过。一直想搞一下、整一下、试一下、折腾一下,工作之余找了一个简单的例子入个门。本博客简单记录一下 LPRNet 车牌识别部署 rk3588流程,训练参考 LPRNet 官方代码。

1、导出onnx
  导出onnx很容易,在推理时加入保存onnx代码,但用onnx推理时发现推理失败,是有算子onnx推理时不支持,看了一下不支持的操作 nn.MaxPool3d() ,查了一下资料有等价的方法,用等价方法替换后推理结果是一致的。

import torch.nn as nn
import torchclass 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 xclass 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, lpr_max_len, phase, class_num, dropout_rate):super(LPRNet, self).__init__()self.phase = phaseself.lpr_max_len = lpr_max_lenself.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(),  # 2# nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 1, 1)),  # 这个可以用MaxPool2d等价nn.MaxPool2d(kernel_size=(3, 3), stride=(1, 1)),small_basic_block(ch_in=64, ch_out=128),  # *** 4 ***nn.BatchNorm2d(num_features=128),nn.ReLU(),  # 6# nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(2, 1, 2)),maxpool_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(),# nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(4, 1, 2)),  # 14maxpool_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=448 + self.class_num, out_channels=self.class_num, kernel_size=(1, 1), stride=(1, 1)),# nn.BatchNorm2d(num_features=self.class_num),# nn.ReLU(),# nn.Conv2d(in_channels=self.class_num, out_channels=self.lpr_max_len+1, kernel_size=3, stride=2),# nn.ReLU(),)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]:  # [2, 4, 8, 11, 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 build_lprnet(lpr_max_len=8, phase=False, class_num=66, dropout_rate=0.5):Net = LPRNet(lpr_max_len, phase, class_num, dropout_rate)if phase == "train":return Net.train()else:return Net.eval()

保存onnx代码

print("===========  onnx =========== ")
dummy_input = torch.randn(1, 3, 24, 94).cuda()
input_names = ['image']
output_names = ['output']
torch.onnx.export(lprnet, dummy_input, "./weights/LPRNet_model.onnx", verbose=False, input_names=input_names, output_names=output_names, opset_version=12)
print("======================== convert onnx Finished! .... ")

2 onnx转换rknn

  onnx转rknn代码

# -*- coding: utf-8 -*-import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
from rknn.api import RKNN
from math import expimport mathONNX_MODEL = './LPRNet.onnx'
RKNN_MODEL = './LPRNet.rknn'
DATASET = './images_list.txt'QUANTIZE_ON = True'''
CHARS = ['京', '沪', '津', '渝', '冀', '晋', '蒙', '辽', '吉', '黑','苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤','桂', '琼', '川', '贵', '云', '藏', '陕', '甘', '青', '宁','新','0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K','L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V','W', 'X', 'Y', 'Z', 'I', 'O', '-']'''CHARS = ['BJ', 'SH', 'TJ', 'CQ', 'HB', 'SN', 'NM', 'LN', 'JN', 'HL','JS', 'ZJ', 'AH', 'FJ', 'JX', 'SD', 'HA', 'HB', 'HN', 'GD','GL', 'HI', 'SC', 'GZ', 'YN', 'XZ', 'SX', 'GS', 'QH', 'NX','XJ','0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K','L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V','W', 'X', 'Y', 'Z', 'I', 'O', '-']def export_rknn_inference(img):# Create RKNN objectrknn = RKNN(verbose=True)# pre-process configprint('--> Config model')rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], quantized_algorithm='normal', quantized_method='channel', target_platform='rk3588')  # mmseprint('done')# Load ONNX modelprint('--> Loading model')ret = rknn.load_onnx(model=ONNX_MODEL, outputs=['output'])if ret != 0:print('Load model failed!')exit(ret)print('done')# Build modelprint('--> Building model')ret = rknn.build(do_quantization=QUANTIZE_ON, dataset=DATASET, rknn_batch_size=1)if ret != 0:print('Build model failed!')exit(ret)print('done')# Export RKNN modelprint('--> Export rknn model')ret = rknn.export_rknn(RKNN_MODEL)if ret != 0:print('Export rknn model failed!')exit(ret)print('done')# Init runtime environmentprint('--> Init runtime environment')ret = rknn.init_runtime()# ret = rknn.init_runtime(target='rk3566')if ret != 0:print('Init runtime environment failed!')exit(ret)print('done')# Inferenceprint('--> Running model')outputs = rknn.inference(inputs=[img])rknn.release()print('done')return outputsif __name__ == '__main__':print('This is main ...')input_w = 94input_h = 24image_path = './test.jpg'origin_image = cv2.imread(image_path)image_height, image_width, images_channels = origin_image.shapeimg = cv2.resize(origin_image, (input_w, input_h), interpolation=cv2.INTER_LINEAR)img = np.expand_dims(img, 0)print(img.shape)preb = export_rknn_inference(img)[0][0]preb_label = []result = []for j in range(preb.shape[1]):preb_label.append(np.argmax(preb[:, j], axis=0))print(preb_label)pre_c = preb_label[0]if pre_c != len(CHARS) - 1:result.append(pre_c)for c in preb_label:if (pre_c == c) or (c == len(CHARS) - 1):if c == len(CHARS) - 1:pre_c = ccontinueresult.append(c)pre_c = cptext = ''for v in result:ptext += CHARS[v]print(ptext)zero_image = np.ones((image_height, image_width, images_channels), dtype=np.uint8) * 255cv2.putText(zero_image, ptext, (0, int(image_height / 2)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2, cv2.LINE_AA)combined_image = np.vstack((origin_image, zero_image))cv2.imwrite('./test_result.jpg', combined_image)

转换rknn测试结果
在这里插入图片描述
说明:由于中文显示出现乱码,示例代码中用拼英简写对中文进行了规避

3 部署 rk3588

在rk3588上运行的【完整代码】

板子上运行结果和时耗。
在这里插入图片描述
在这里插入图片描述
  模型这么小在rk3588上推理时耗还是比较长的,毫无疑问是模型推理过程中有操作切换到CPU上了。如果对性能要求的比较高,可以针对切换的CPU上的操作进行规避或替换。查看转换rknn模型log可以知道是那些操作切换到CPU上了。
在这里插入图片描述

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

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

相关文章

SW - 将面导出为dxf

文章目录 SW - 将面导出为dxf概述笔记原点问题END SW - 将面导出为dxf 概述 在做PCB板框. 以前做过一个笔记,用autoCAD来制作导出dxf(cadence SPB17.4 - 用autoCAD2022画一个PCB板框)。 不喜欢用autoCAD(相对麻烦), 还是喜欢用SW&#xff0…

异步日志:性能优化的金钥匙

一、背景 2024 年 4 月的一个宁静的夜晚,正当大家忙完一天的工作准备休息时,应急群里“咚咚咚”开始报警,提示我们余利宝业务的赎回接口成功率下降。 通过 Monitor 监控发现,该接口的耗时已经超过了网关配置的超时阈值(2s)&#…

Spring Cloud Alibaba整合Seata实战

Spring Cloud Alibaba整合Seata实战 1.启动Seata Server 1.1 环境准备 1)指定nacos作为配置中心和注册中心 修改registry.conf文件 注意:客户端配置registry.conf使用nacos时也要注意group要和seata server中的group一致,默认group是&quo…

Linux--生产消费模型

线程系列: Linux–线程的认识(一) Linux–线程的分离、线程库的地址关系的理解、线程的简单封装(二) 线程的互斥:临界资源只能在同一时间被一个线程使用 生产消费模型 生产消费模型是多线程编程和分布式系统中的一个经典概念&…

我们水冷使制动电阻功率密度成倍增加-水冷电阻设计工厂

先进陶瓷 我们后来发现工业应用中对占用空间最小的水冷电阻器的工业需求,推出了适用于中压工业应用的水冷电阻器。它的特点是两块由具有特殊性能的先进陶瓷制成的板。 使用工业电驱动装置的一个重要好处是,可靠的再生和动态制动系统可以补充或取代传统…

Laravel :如何将Excel文件导入数据库

文章目录 一、前提二、使用2.1、新建一个导入文件2.2、新建一个控制器和方法,调用导入文件2.3、 新建一个页面,支持文件上传 一、前提 想要将excel内容入库,laravel有扩展可以使用,常用的扩展是maatwebsite/excel,安装步骤参考上一篇&#x…

Mosh|SQL教程第四弹(未完)

SQL有很多自带的内聚的函数(MAX、MIN、AVG、SUM、COUNT) 一、聚合函数(Aggregate Functions) 这里的括号可以写列名也可以写表达式,下面是一个练习: 二、GROUP BY子句 统计2019-07-01以后每个客户的总销售额 注意这…

Javaweb11-Filter过滤器

Filter过滤器 1.Filter的基本概念: 在Java Servlet中,Filter接口是用来处理HttpServletRequest和HttpServletResponse的对象的过滤器。主要用途是在请求到达Servlet之前或者响应离开Servlet之前对请求或响应进行预处理或后处理。 2.Filter常见的API F…

探展2024世界人工智能大会之合合信息扫描黑科技~

文章目录 ⭐️ 前言⭐️ AIGC古籍修复文化遗产焕新⭐️ 高效的文档图像处理解决方案⭐️ AIGC扫描黑科技一键全搞定⭐️ 行业级的大模型加速器⭐️ 结语 ⭐️ 前言 大家好,我是 哈哥(哈哥撩编程) ,这次非常荣幸受邀作为专业观众参…

重磅!新公司法正式实施,这些变化你必须知道! ️

新公司法来了!企业设立和经营必知的关键变动 🏛️🚀 大家好,我是猫头虎,科技自媒体博主。今天我们来聊聊一件大事——新公司法的实施,这对企业设立和经营带来了哪些重大影响?跟着我&#xff0c…

【DDIM】DENOISING DIFFUSION IMPLICIT MODELS【论文精读】【视频讲解】【公式推导】

论文:DENOISING DIFFUSION IMPLICIT MODELS(https://arxiv.org/abs/2010.02502) B站视频链接 DDIM论文精讲视频 去噪扩散隐模型的论文精读,涉及本文的大部分公式逐步推导。总计3小时的详细论文讲解。 讲解详细对应文档 DDIM视频…

聊聊mysql

记录那些坑 本文会持续更新,陆续更新有关mysql技术内幕、实战优化、面试技巧。 文章目录 前言索引BTree之聚集索引BTree之辅助索引BTree之联合索引BTree之覆盖索引 使用到的工具1、py_innodb_page_info工具2、hexdump工具 总结 前言 重中之重的MySql数据库 mysql…

概率论期末速成(知识点+例题)

考试范围 一: 事件关系运算性质全概率公式、贝叶斯公式古典概型 二: 离散分布律连续密度函数性质 -> 解决三个问题(求待定系数、求概率、求密度函数)分布函数 -> 解决三个问题常用分布(最后一节课的那几个分…

手电筒的光能飞到宇宙尽头吗

如果我们打开手电筒向夜空照一秒再关掉,我们将会看到,在关掉手电筒的一瞬间,手电筒发出的光束也会消失,那么,它发出的光哪去了呢?下面我们就来聊一下这个话题。实际上,我们看到的光束&#xff0…

Spark SQL 概述

Spark SQL 概述 Spark SQL 是 Apache Spark 的一个模块,专门用于处理结构化数据。它集成了 SQL 查询和 Spark 编程的强大功能,使得处理大数据变得更加高效和简便。通过 Spark SQL,用户可以直接在 Spark 中使用 SQL 查询,或者使用 …

ubuntu16.04安装低版本cmake(安装cmake安装)

文章目录 ubuntu16.04安装低版本cmake(安装cmake安装)1. **下载并解压CMake压缩文件**:- 首先,你需要从CMake的官方网站或其他可靠来源下载cmake-2.8.9-Linux-i386.tar.gz文件。- 然后在终端中使用以下命令解压文件: 2…

BFS:多源BFS问题

一、多源BFS简介 超级源点&#xff1a;其实就是把相应的原点一次性都丢到队列中 二、01矩阵 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:const int dx[4]{1,-1,0,0};const int dy[4]{0,0,1,-1};vector<vector<int>> updateMatrix(vector…

headerpwn:一款针对服务器响应与HTTP Header的模糊测试工具

关于headerpwn headerpwn是一款针对服务器响应与HTTP Header的模糊测试工具&#xff0c;广大研究人员可以利用该工具查找网络异常并分析服务器是如何响应不同HTTP Header的。 功能介绍 当前版本的headerpwn支持下列功能&#xff1a; 1、服务器安全与异常检测&#xff1b; 2、…

QFileDialog的简单了解

ps&#xff1a;写了点垃圾&#xff08;哈哈哈&#xff09; 它继承自QDialog 这是Windows自己的文件夹 这是两者的对比图&#xff1a; 通过看QFileDialog的源码&#xff0c;来分析它是怎么实现这样的效果的。 源码组成&#xff1a; qfiledialog.h qfiledialog_p.h&#xff…

Python面试宝典第11题:最长连续序列

题目 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1&#xff1a; 输入&#xff1a;nums [100,4,200,1,3,2] 输出&#xff1a;…