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.pswp.cn/web/44235.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44235.shtml
英文地址,請注明出處:http://en.pswp.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…