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/bicheng/46591.shtml

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

相关文章

【postgresql】权限(Privileges)

权限(privileges)是决定用户或角色可以对数据库对象(如表、视图、序列和函数)执行哪些操作的许可。权限对于维护安全性和控制对数据的访问至关重要。 权限分类 在 PostgreSQL 中,权限分为以下几种: SELEC…

数据库基本查询(表的增删查改)

一、增加 1、添加信息 insert 语法 insert into table_name (列名) values (列数据1,列数据2,列数据3...) 若插入时主键或唯一键冲突就无法插入。 但如果我们就是要修改一列信息也可以用insert insert into table_name (列名) values (列数据1&am…

客户端通过服务器进行TCP通信(三)

一. 对TCP的基础讲解 服务端 1. 首先创建一个套接字,TCP是面向字节流的套接字,故需要使用SOCK_STREAM 2. 然后使用bind()函数将套接字与服务器地址关联(如果是在本地测试,直接将地址设置为217.0.0.1或者localhost,端口号为1000…

内存函数(C语言)

内存函数 以下函数的头文件:string.h 针对内存块进行处理的函数 memcpy 函数原型: void* memcpy(void* destination, const void* source, size_t num);目标空间地址 源空间地址num,被拷贝的字节个数 返回目标空间的起始地…

Python与自动化脚本编写

Python与自动化脚本编写 Python因其简洁的语法和强大的库支持,成为了自动化脚本编写的首选语言之一。在这篇文章中,我们将探索如何使用Python来编写自动化脚本,以简化日常任务。 一、Python自动化脚本的基础 1. Python在自动化中的优势 Pyth…

1.31、基于长短记忆网络(LSTM)的发动机剩余寿命预测(matlab)

1、基于长短记忆网络(LSTM)的发动机剩余寿命预测的原理及流程 基于长短期记忆网络(LSTM)的发动机剩余寿命预测是一种常见的机器学习应用,用于分析和预测发动机或其他设备的剩余可用寿命。下面是LSTM用于发动机剩余寿命预测的原理和流程: 数据收集&#…

【Linux】 GCC/G++与Makefile使用

Linux GCC/G使用 GCC如何完成 格式:gcc [选项] 要编译的文件 [选项] [目标文件] 常用选项: -E:让gcc在预处理结束后停止编译过程,输出.i的C语言原始文件。-S:该选项只是进行编译而不是进行汇编,最终生成汇…

力扣144题:二叉树的先序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 示例 1: 输入:root [1,null,2,3] 输出:[1,2,3]示例 2: 输入:root [] 输出:[]示例 3: 输入:root [1] 输出&am…

C++入门学习——初始化列表

概念 初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式 class Date { public://初始化列表Date(int year,int month,int day):_year(year),_month(month),_d…

[Windows] 油.管视频下载神器 Gihosoft TubeGet Pro v9.3.88

描述 对于经常在互联网上进行操作的学生,白领等! 一款好用的软件总是能得心应手,事半功倍。 今天给大家带了一款高科技软件 管视频下载神器 无需额外付费,永久免费! 亲测可运行!! 内容 目前主…

高德地图显示圆形区域并在区域边上标注半径

bug:循环创建三个圆形区域 ,数组设置为[{raduis:500,color:“#FF0000”}],然后循环取颜色会莫名其妙报错修改为 strokeColor: [“#FF0000”, “#1EE3C2”, “#3772E9”][i]即可 initAMap() {AMapLoader.load({key: "130cca3be68a2ff0fd5…

记VMware网络适配器里的自定义特定虚拟网络一直加载问题解决办法

1、问题描述 VMware网络适配器里的自定义特定虚拟网络一直加载问题: 在自定义:特定虚拟网络选择的时候 没有上图所示的三个选择,而是正在加载虚拟网络.... 如下图所示: 2、解决办法 2.1、原因分析: 是安装时候出现…

安防视频监控/视频汇聚EasyCVR平台浏览器http可以播放,https不能播放,如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构,兼容性强、支持多协议接入,包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…

7.15洛谷蓝题

二分答案的两个模板&#xff1a; 1.最小值的最大化&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<bits/stdc.h> #include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> #include<…

Studying-代码随想录训练营day40| 198.打家劫舍、213.打家劫舍II、337.打家劫舍III

第40天&#xff0c;动态规划part07&#xff0c;动态规划经典题型“打家劫舍”(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 198.打家劫舍 213.打家劫舍II 337.打家劫舍III 总结 198.打家劫舍 文档讲解&#xff1a;代码随想录打家劫舍 视频讲解&#xff1a;手…

【C++进阶学习】第七弹——AVL树——树形结构存储数据的经典模块

二叉搜索树&#xff1a;【C进阶学习】第五弹——二叉搜索树——二叉树进阶及set和map的铺垫-CSDN博客 目录 一、AVL树的概念 二、AVL树的原理与实现 AVL树的节点 AVL树的插入 AVL树的旋转 AVL树的打印 AVL树的检查 三、实现AVL树的完整代码 四、总结 前言&#xff1a…

JavaScript青少年简明教程:输入输出

JavaScript青少年简明教程&#xff1a;输入输出 JavaScript的输入输出情况相对复杂&#xff0c;因为它依赖于其运行的宿主环境&#xff08;如Web浏览器或Node.js&#xff09;来提供具体的输入输出机制。JavaScript的核心规范&#xff08;ECMAScript&#xff09;本身并不直接提…

C基础day9

一、思维导图 二、课后练习 1> 使用递归实现 求 n 的 k 次方 #include<myhead.h>int Pow(int n,int k) {if(k 0 ) //递归出口{return 1;}else{return n*Pow(n,k-1); //递归主体} }int main(int argc, const char *argv[]) {int n0,k0;printf("请输入n和k:&…

韩国coupang上线的卖家官网是什么?韩国电商有哪些平台?

根据Statista的调查报告&#xff0c;预计2024年电子商务市场收入将达到4.117亿美元。而韩国的电子商务市场是全球最具活力和创新性的市场之一&#xff0c;有数据显示2023年韩国电商市场规模已突破1700亿美元&#xff0c;全球排名第四。 韩国coupang上线的卖家官网是什么&#x…

Linux虚拟机扩展磁盘空间

文章目录 在VM上进行扩展新的磁盘空间进入虚拟机将扩展的磁盘空间分配给对应的分区 VM 下的Linux虚拟机提示磁盘空间不足&#xff0c;需要对其进行磁盘扩容&#xff0c;主要有以下两步&#xff1a; 在VM上进行扩展新的磁盘空间 先关闭虚拟机在VM的虚拟机设置处进行硬盘扩展 …