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,一经查实,立即删除!

相关文章

EtherCAT设备配置:SCI EoeMacIp 文件与实际设备配置的比较过程

标题:EtherCAT设备配置:SCI文件与实际设备配置的比较过程 在工业自动化领域,EtherCAT(Ethernet for Control Automation Technology)作为一种高效的实时以太网协议,正在被广泛应用。在EtherCAT网络的配置过…

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…

我的PHP8编译日志

编译命令在arm和x86架构上是一样的,如果缺少依赖库,按需要安装: 登录后复制 yuminstall libcurl libcurl-devel yum install openssl openssl-devel yum install pcre2 pcre2-devel yum install libxml2 libxml2-devel 1.2.3.4. 配置和编译&…

Linux--生产消费模型

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

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

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

Llama2 训练指南

Llama2 是一个基于 Python 的机器学习框架,旨在帮助开发者快速构建和部署机器学习模型。下面是 Llama2 训练指南,旨在帮助您了解如何使用 Llama2 训练模型。 概述 Llama2 提供了多种方式来训练模型,包括使用 Keras 和 TensorFlow。下面是在…

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

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

力扣 202快乐数

快乐数这题有两个关键 一个是求n的 各个位上平方和 另一个是判断是否为快乐数的依据是是否在哈希表中找到已经出现过的数 1求各个位上平方和方法 定义sum sum N除以十取余的平方和 n/10 循环终止条件是n0 2查找一个数是否出现,用哈希表unordered_set &…

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

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

result.h

#ifndef ASYNCIO_RESULT_H #define ASYNCIO_RESULT_H#include <asyncio/exception.h> #include <variant> #include <optional> namespace ASYNCIO_NS {// 结果类封装&#xff08;不是协程函数的返回类型&#xff09; template<typename T> struct Res…

Javaweb11-Filter过滤器

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

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

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

【常用知识点-Java】读取Properties文件

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-11 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 简介 .properties文件是一种简单的文本文件&#xff0c;用于存储键值对&#xff0c;其每个键值对占一行&#xff0c;且键和值之间用…

力扣题解( 最长递增子序列的个数)

673. 最长递增子序列的个数 给定一个未排序的整数数组 nums &#xff0c; 返回最长递增子序列的个数 。 注意 这个数列必须是 严格 递增的。 思路&#xff1a; 用一个maxlen表示当前最长递增数组的长度&#xff0c;maxcount表示最大长度。当进下标为i的元素时&#xff0c;将…

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

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

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

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

聊聊mysql

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

模拟人机猜数游戏

设计目的 1、加深学生对该课程基础知识和基本理论的理解和掌握&#xff0c;培养学生综合运用所学知识独立 分析和解决问题的能力; 2、培养学生在计算机软硬件开发、理论计算、查阅资料等方面的能力&#xff0c;使学生逐步树立正 确的设计思想; 3、加强理论联系实际&#xff0c…