【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程

提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论

文章目录

  • 【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
  • 前言
  • 模型转换--pytorch转rknn
    • pytorch转onnx
    • onnx转rknn
  • RKNPU2平台搭建依赖环境
  • RKNPU2调用rknn模型
    • RKNPU2推理核心流程
    • RKNPU2推理代码
  • 总结


前言

Orangepi RKNN(Rockchip Neural Network)是Rockchip公司推出的一种用于其处理器上的高效神经网络加速技术。它与Rockchip的处理器紧密结合,旨在通过硬件加速提升AI应用的运行效率,特别是在边缘计算设备上,为嵌入式设备和边缘计算场景提供了高性能、低功耗的深度学习解决方案。RKNN Toolkit 和 RKNPU2 是支持RKNN技术的两个重要工具集。RKNN Toolkit 是一套软件开发工具包,提供了模型转换、优化、测试和部署等功能,帮助开发者将训练好的深度学习模型轻松转换为适合Rockchip硬件的格式,以获得最佳性能。而RKNPU2 则是指Rockchip的神经网络处理单元(Neural Processing Unit, NPU)的第二代驱动程序和库文件,它为RKNN Toolkit 提供底层支持,确保了模型能够在Rockchip的硬件上高效地执行。
RKNN Toolkit 和 RKNPU2 是对 Rockchip NPU最原生的支持。


模型转换–pytorch转rknn

博主在RK3566开发板上进行部署演示: 模型转化可以是在Ubuntu环境的主机上或者虚拟机上,但是模型部署必须是在 Orangepi 的开发板子上。

Pytorch 模型转 RKNN 并推理的步骤如下:

  1. 将 PyTorch 预训练模型文件( .pth 或 .pt 格式)转换成 ONNX 格式的文件(.onnx格式),这一转换过程在 PyTorch 环境中进行。
  2. 将转换得到的 .onnx 文件再次转换成 .rknn 格式的文件,这一转换过程需要在安装有转换工具 rknn-toolkit2 的Ubuntu系统上运行。这里博主建议在 docker 的 Ubuntu 虚拟机上进行。
  3. 将转换得到的 .rknn 文件随后作为输入,在 RKNN 平台上调用 RKNPU2 的 C++ API 来执行模型的推理。

pytorch转onnx

博主使用AlexNet图像分类(五种花分类)进行演示,需要安装pytorch环境,对于该算法的基础知识,可以参考博主【AlexNet模型算法Pytorch版本详解】博文。

conda create --name AlexNet python==3.10
conda activate AlexNet
# 根据自己主机配置环境
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 假设模型转化出错则降级为指定1.16.1版本
pip install onnx==1.16.1

然后把训练模型好的AlexNet.pth模型转成AlexNet.onnx模型,pyorch2onnx.py转换代码如下:
【AlexNet.pth百度云链接,提取码:ktq5 】直接下载使用即可。

import torch
from model import AlexNet
model = AlexNet(num_classes=5)
weights_path = "./AlexNet.pth"
# 加载模型权重
model.load_state_dict(torch.load(weights_path))
# 模型推理模式
model.eval()
model.cpu()
# 虚拟输入数据
dummy_input1 = torch.randn(1, 3, 224, 224)
# 模型转化函数
torch.onnx.export(model, (dummy_input1), "AlexNet.onnx", verbose=True, opset_version=11)

onnx转rknn

【平台:x64 架构 windoes11 docker虚拟机 Ubuntu 系统】
1.安装dockers【参考】: Windows11系统下安装并配置阿里云镜像加速,并完成启动。

2.下载 RKNN Toolkit 转换工具【githup下载】: 博主这里没有下载master分支,而是下载 rknn-toolkit2-v1.5.2分支。

3.搭建docker镜像: 在doc/Rockchip_Quick_Start_RKNN_SDK_V1.5.2_CN.pdf中,博主参考该官方文档详细介绍如何构建docker的Ubuntu容器并安装转换工具 rknn-toolkit2,以及其详细的使用方式。

docker常用指令
Dockerfile文件构建镜像镜像(不推荐):经常出现 failed with status code [manifests 18.04]: 403 Forbidden 的错误,博主暂时没有解决方案。

# 进入docker配置文件目录
cd XXX\rknn-toolkit2-1.x.x\docker\docker_file\ubuntu_xx_xx_cpxx
# eg: cd C:\Users\AYU\Downloads\rknn-toolkit2-1.5.2\docker\docker_file\ubuntu_18_04_cp36# 查询配资文件
ls
# 出现三个文件分别是:
# 1.Dockerfile_ubuntu_18_04_for_cp36:特定 Dockerfile,用于创建一个基于Ubuntu 18.04的Docker镜像,专门针对Python3.6(cp36)进行配置.
# 2.rknn_toolkit2-1.5.2+b642f30c-cp36-cp36m-linux_x86_64.whl:针对Linux x86_64架构的rknn-toolkit2 Python(wheel )安装包,支持Python3.6(cp36).
# 3.sources_bionic.list: APT源列表文件,用于Ubuntu系统中的软件包管理.# 构建 Docker 镜像 
# -f 指定Dockerfile文件  
# -t 镜像名:标签
docker build -f Dockerfile_ubuntu_xx_xx_for_cpxx -t rknn-toolkit2:x.x.x-cpxx .
# eg:docker build -f Dockerfile_ubuntu_18_04_for_cp36 -t rknn-toolkit2-env:1.5.2-cp36 .

Docker 镜像文件下载(推荐)【官方网盘,提取码:rknn】【个人网盘,提取码:rknn】

因为官方网盘没有保留一些旧版本的docker镜像,因此博主的个人网盘将旧的网盘补充完整了。

# 进入到docker镜像目录,加载镜像
docker load -i XXX\rknn-toolkit2-x.x.x-cpxx-docker.tar.gz
# eg: docker load -i E:\rknn-toolkit2-1.5.2-cp36-docker.tar.gz# 查看安装的镜像
docker images# 创建容器
docker run -it --name rknn_toolkit2_x.x.x_cpxx -d rknn-toolkit2:x.x.x-cpxx
# eg: docker run -it --name rknn_toolkit2_1.5.2_cp36 -d rknn-toolkit2:1.5.2-cp36# 查看运行的容器
docker ps


4.完成模型onnx到rknn的转化: convert_rknn文件拷贝至虚拟机,完成转化过程,并将rknn模型从虚拟机拷贝到主机。

参考下载的rknn-toolkit2-1.5.2\examples\onnx\resnet50v2中的内容

convert_rknn文件包括之前成功转化的AlexNet.onnx模型文件,一张验证图片,一个保存着验证图片相对路径的dataset.txt,以及转化rknn所需的简化版代码convert.py。

convert.py内容如下:

import os
import urllib
import traceback
import time
import sys
import numpy as np
import cv2
from rknn.api import RKNNONNX_MODEL = 'AlexNet.onnx'
RKNN_MODEL = 'AlexNet.rknn'
DATASET = './dataset.txt'if __name__ == '__main__':# 创建RKNN对象rknn = RKNN(verbose=True)# 配置RKNN模型:标准化以及指定部署平台print('--> config model')# 注意target_platform='rk3566'要替换成自己的平台rknn.config(mean_values=[127.5, 127.5, 127.5], std_values=[127.5, 127.5, 127.5], target_platform='rk3566')print('done')# 加载对应的深度学习框架print('--> Loading model')ret = rknn.load_onnx(model=ONNX_MODEL)if ret != 0:print('Load model failed!')exit(ret)print('done')# 构造RKNN模型print('--> Building model')# 注意do_quantization 用于控制模型参数和输入数据的量化,即是否将浮点数(float32)转换为整数(int8或int16).ret = rknn.build(do_quantization=True, dataset=DATASET)if ret != 0:print('Build model failed!')exit(ret)print('done')# 导出RKNN模型print('--> Export rknn model')ret = rknn.export_rknn(RKNN_MODEL)if ret != 0:print('Export rknn model failed!')exit(ret)print('done')# 释放RKNN对象rknn.release()

转化流程的指令如下:

# 将转化代码拷贝到ubuntu
docker cp E:\convert_rknn container_id:/root
# eg: docker cp E:\convert_rknn 38097dad59cc:/root# 进入ubuntu容器
docker exec -it container_id bash
# eg: docker exec -it 38097dad59cc bash# 执行代码
cd /root/convert_rknn
python convert.py# 查看目录内容
ls# 退出ubuntu容器,并将模型从ubuntu中拷贝出来
exit
docker cp container_id:/root/convert_rknn/xxx.rknn  E:\
# eg: docker cp 38097dad59cc:/root/convert_rknn/AlexNet.rknn  E:\



RKNPU2平台搭建依赖环境

【平台:aarch64 架构 Orange Pi 3B (RK3566) 的 Ubuntu 系统】
VNC可视化控制RK3566参考

常用的rknpu1、rknpu2用于端侧内容的开发和编译,对应python模型转换环境分别为:rknn-toolkit、rknn-toolkit2。

参考Rockchip NPU C++推理示例工程【githup下载】,构建AlexNet C++ 图像分类推理工程:

  1. rknpu2-master\examples\3rdparty\opencv\opencv-linux-aarch64拷贝到AlexNet;
  2. rknpu2-master\runtime\RK356X\Linux\librknn_api拷贝到AlexNet;
  3. 在AlexN目录下新建weights目录将rknn权重文件放到里面;
  4. 在AlexN目录下新建src目录放置推理代码用于执行c++推理(后面会提供);
  5. 构建CMakeLists.txt核心配置文件(后面会提供)。
 AlexNet └── 3rdparty├── opencv|   ├── opencv-linux-aarch64└── librknn_api├── aarch64|   ├── vlibrknnrt.so├── include|   ├── rknn_api.h|   ├── rknn_matmul_api.h└── src├── AlexNet.cpp└── weights├── AlexNet.rknn└── CMakeLists.txt

RKNPU2调用rknn模型

RKNPU2推理核心流程

初始化RKNN模型
用于初始化一个 RKNN 上下文,并加载指定的模型。

 ret = rknn_init(&ctx, model, model_len, 0, NULL);
rknn_init参数ctxmodelmodel_sizeflagsconfig
作用指向一个 rknn_context 类型的指针,用于存储初始化后的上下文。指向模型数据的指针,通常是经过编译和优化的二进制文件。以字节为单位模型数据的大小。用于指定一些特殊初始化选项的标志位,通常设置为 0。指向一个 rknn_sdk_config 结构体的指针,用于配置 SDK 的行为,不需要特殊配置通常设置为 NULL。

获取模型输入输出信息
用于查询 RKNN 上下文中的各种属性,包括输入和输出的详细信息(数量、名称和形状)、性能统计等信息。

ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
rknn_query参数ctxcmd*paramparam_size
作用调用 rknn_init 函数时已经初始化的 RKNN 上下文,标识当前的 RKNN 模型实例。查询命令,用于指定要查询的信息类型。指向一个缓冲区的指针,用于存储查询结果,缓冲区的类型和大小取决于查询命令。以字节为单位的缓冲区大小。
内容RKNN_QUERY_IN_OUT_COUNT:查询模型的输入和输出张量的数量。RKNN_QUERY_INPUT_ATTR:查询输入张量的属性。RKNN_QUERY_OUTPUT_ATTR:查询输出张量的属性。RKNN_QUERY_PERF_DETAIL:查询性能详细信息,包括每个层的执行时间。RKNN_QUERY_PERF_STAT:查询性能统计信息,包括总的执行时间和平均执行时间。RKNN_QUERY_TARGET_PROCESSOR:查询目标处理器信息。RKNN_QUERY_MODEL_INFO:查询模型信息,包括模型的版本、输入输出张量的数量等。

预处理输入数据
对输入数据进行颜色空间转换,尺寸缩放操作。

cv::cvtColor(orig_img, orig_img_rgb, cv::COLOR_BGR2RGB);
cv::resize(orig_img, img, cv::Size(MODEL_IN_WIDTH, MODEL_IN_HEIGHT), 0, 0, cv::INTER_LINEAR);

这部分不是 RKNPU2 核心部分,根据任务需求不同,代码略微不同。

设置输入
设置 RKNN 模型的输入数据。

 ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
cudaMalloc参数ctxn_inputsinputs
作用标识当前的 RKNN 模型实例。输入张量的数量,与模型的输入张量数量必须一致。rknn_tensor_attr 结构体数组的指针,每个结构体描述一个输入张量的属性和数据。

执行推理
用于执行神经网络模型的推理,触发模型的前向传播过程,将输入数据传递给模型,并生成输出结果。

 ret = rknn_run(ctx, nullptr);
函数ctxmem
作用标识当前的 RKNN 模型实例。指向 rknn_input_output_mem 结构体数组的指针,用于指定输入和输出数据的内存地址;设置为 nullptr,则表示使用默认的输入和输出内存。

获取输出
设置 RKNN 模型的输入数据。

 ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
cudaMalloc参数ctxn_inputsinputs
作用标识当前的 RKNN 模型实例。输入张量的数量,与模型的输入张量数量必须一致。rknn_tensor_attr 结构体数组的指针,每个结构体描述一个输入张量的属性和数据。

后处理推理结果
推理完成后,从输出张量中获取结果数据,根据需要对结果进行后处理,以获得最终的预测结果。

 cv::Mat prob(output_attrs[i].dims[0], output_attrs[i].dims[1], CV_32F, (float*)buffer);cv::minMaxLoc(prob, &minv, &maxv, &minL, &maxL); 

这部分不是 RKNPU2 核心部分,根据任务需求不同,代码基本不同。


RKNPU2推理代码

需要配置flower_classes.txt文件存储五种花的分类标签,并将其放置到工程目录的src路径下(推荐)。

daisy
dandelion
roses
sunflowers
tulips

这里需要将AlexNet.rknn放置到工程目录的weight路径下(推荐),并且将以下推理代码拷贝到src路径下的AlexNet.cpp文件中:

#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "rknn_api.h"#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>#include <fstream>
#include <iostream>using namespace std;
using namespace cv;std::string labels_txt_file = "src/flower_classes.txt";
std::vector<std::string> readClassNames()
{std::vector<std::string> classNames;std::ifstream fp(labels_txt_file);if (!fp.is_open()){printf("could not open file...\n");exit(-1);}std::string name;while (!fp.eof()){std::getline(fp, name);if (name.length())classNames.push_back(name);}fp.close();return classNames;
}// 从文件中读取二进制模型数据
// 参数:filename:模型文件名,model_size:模型大小 返回值:模型数据指针
static unsigned char *load_model(const char *filename, int *model_size)
{FILE *fp = fopen(filename, "rb");if (fp == nullptr){printf("fopen %s fail!\n", filename);return NULL;}fseek(fp, 0, SEEK_END);int model_len = ftell(fp);unsigned char *model = (unsigned char *)malloc(model_len); // 申请模型大小的内存,返回指针fseek(fp, 0, SEEK_SET);if (model_len != fread(model, 1, model_len, fp)){printf("fread %s fail!\n", filename);free(model);return NULL;}*model_size = model_len;if (fp){fclose(fp);}return model;
}int main(int argc, char **argv)
{rknn_context ctx = 0;       //  Rockchip NPU 的上下文句柄,用于标识和管理当前的模型实例.int ret;                    //  用于检查函数调用是否成功.int model_len = 0;          //  用于存储模型文件的长度(以字节为单位).unsigned char *model;       //  指向模型数据的指针.int MODEL_IN_WIDTH;         //  输入模型图像的宽.int MODEL_IN_HEIGHT;        //  输入模型图像的高.// const char *model_path = "weights/AlexNet.rknn";// const char *img_path = "images/40410963_3ac280f23a_n.jpg";const char *model_path = argv[1];const char *img_path = argv[2];if (argc != 3){printf("Usage: %s <rknn model> <image_path> \n", argv[0]);return -1;}std::vector<std::string> labels = readClassNames();   // 预测的目标标签数// ======================= 读取图片 ===================cv::Mat orig_img = imread(img_path, cv::IMREAD_COLOR);if (!orig_img.data){printf("cv::imread %s fail!\n", img_path);return -1;}// ======================= 初始化RKNN模型 ===================model = load_model(model_path, &model_len);         // 获取模型指针ret = rknn_init(&ctx, model, model_len, 0, NULL);   // 初始化RKNN模型if (ret < 0){printf("rknn_init fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输入输出信息 ===================// ********** 输入输出数量 **********rknn_input_output_num io_num;ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));                    // 使用rknn_query函数获取模型输入输出数量if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}printf("model input num: %d, output num: %d\n", io_num.n_input, io_num.n_output);         // 打印模型输入输出数量// ********** 输入输出属性 **********rknn_tensor_attr input_attrs[io_num.n_input];        // 使用rknn_tensor_attr结构体存储模型输入属性memset(input_attrs, 0, sizeof(input_attrs));         // 将input_attrs用0初始化for (int i = 0; i < io_num.n_input; i++)             // 网络可能有多个输入,遍历模型所有输入{input_attrs[i].index = i;                          // 设置模型输入索引ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));    // 使用rknn_query函数获取模型输入信息,存储在input_attrsif (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}MODEL_IN_WIDTH = input_attrs[i].dims[1];    // 获取模型输入的具体宽MODEL_IN_HEIGHT = input_attrs[i].dims[2];   // 获取模型输入的具体高// 打印模型输入信息printf("input tensors:  index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, ""zp=%d, scale=%f\n",input_attrs[i].index, input_attrs[i].name, input_attrs[i].n_dims, input_attrs[i].dims[0], input_attrs[i].dims[1], input_attrs[i].dims[2], input_attrs[i].dims[3],input_attrs[i].n_elems, input_attrs[i].size, get_format_string(input_attrs[i].fmt), get_type_string(input_attrs[i].type),get_qnt_type_string(input_attrs[i].qnt_type), input_attrs[i].zp, input_attrs[i].scale);}rknn_tensor_attr output_attrs[io_num.n_output];       // 使用rknn_tensor_attr结构体存储模型输出信息memset(output_attrs, 0, sizeof(output_attrs));        // 将output_attrs用0初始化for (int i = 0; i < io_num.n_output; i++)             // 网络可能有多个输出,遍历模型所有输出{output_attrs[i].index = i;                          // 设置模型输入索引ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));if (ret != RKNN_SUCC){printf("rknn_query fail! ret=%d\n", ret);return -1;}// 打印模型输出信息printf("output tensors: index=%d, name=%s, n_dims=%d, dims=[%d, %d, %d, %d], n_elems=%d, size=%d, fmt=%s, type=%s, qnt_type=%s, ""zp=%d, scale=%f\n",output_attrs[i].index, output_attrs[i].name, output_attrs[i].n_dims, output_attrs[i].dims[0], output_attrs[i].dims[1], output_attrs[i].dims[2], output_attrs[i].dims[3],output_attrs[i].n_elems, output_attrs[i].size, get_format_string(output_attrs[i].fmt), get_type_string(output_attrs[i].type),get_qnt_type_string(output_attrs[i].qnt_type), output_attrs[i].zp, output_attrs[i].scale);}// ======================= 前处理 ===================cv::Mat orig_img_rgb;cv::cvtColor(orig_img, orig_img_rgb, cv::COLOR_BGR2RGB);      // 默认是BGR需要转化成RGBcv::Mat img = orig_img_rgb.clone();if (orig_img.cols != MODEL_IN_WIDTH || orig_img.rows != MODEL_IN_HEIGHT){cv::resize(orig_img, img, cv::Size(MODEL_IN_WIDTH, MODEL_IN_HEIGHT), 0, 0, cv::INTER_LINEAR);           // 对图像尺寸进行缩放}// ======================= 设置模型输入 ===================rknn_input inputs[io_num.n_input];     // 使用rknn_input结构体存储模型输入信息memset(inputs, 0, sizeof(inputs));     // 将inputs用0初始化for (int i = 0; i < io_num.n_input; i++){ inputs[i].index = input_attrs[i].index;     // 设置模型输入索引                                                    inputs[i].type = RKNN_TENSOR_UINT8;         // 设置模型输入类型                             inputs[i].size = input_attrs[i].dims[1] * input_attrs[i].dims[2] * input_attrs[i].dims[3] * sizeof(uint8_t);  // 设置模型输入大小inputs[i].fmt = input_attrs[i].fmt;         // 设置模型输入格式:NHWC                                    inputs[i].buf = img.data;                   // 设置模型输入数据                                                }ret = rknn_inputs_set(ctx, io_num.n_input, inputs);     // 使用rknn_inputs_set函数设置模型输入if (ret < 0){printf("rknn_input_set fail! ret=%d\n", ret);return -1;}// ======================= 推理 ===================ret = rknn_run(ctx, nullptr);         // 使用rknn_run函数运行RKNN模型if (ret < 0){printf("rknn_run fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输出 ===================rknn_output outputs[io_num.n_output];     // 使用rknn_output结构体存储模型输出信息memset(outputs, 0, sizeof(outputs));      // 将outputs用0初始化for (int i = 0; i < io_num.n_output; i++){ outputs[i].want_float = 1;                // 设置模型输出类型为float}ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);      // 使用rknn_outputs_get函数获取模型输出if (ret < 0){printf("rknn_outputs_get fail! ret=%d\n", ret);return -1;}// ======================= 后处理 ===================for (int i = 0; i < io_num.n_output; i++)           // 遍历模型所有输出{float *buffer = (float *)outputs[i].buf;    // 模型输出数据// 1x5 获取输出数据并包装成一个cv::Mat对象,为了方便后处理cv::Mat prob(output_attrs[i].dims[0], output_attrs[i].dims[1], CV_32F, (float*)buffer);std::cout << "prob: " << prob << std::endl;// 后处理推理结果cv::Point maxL, minL;		// 用于存储图像分类中的得分最小值索引和最大值索引(坐标)double maxv, minv;			// 用于存储图像分类中的得分最小值和最大值cv::minMaxLoc(prob, &minv, &maxv, &minL, &maxL); int max_index = maxL.x;		// 获得最大值的索引,只有一行所以列坐标既为索引std::cout << "label id: " << max_index << std::endl;cv::putText(orig_img, labels[max_index], cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, 8);cv::imwrite("./output.jpg", orig_img);}// ======================= 释放输出缓冲区 ===================rknn_outputs_release(ctx, 1, outputs);      // 释放rknn_outputs_get获取的输出if (ret < 0){printf("rknn_outputs_release fail! ret=%d\n", ret);return -1;}else if (ctx > 0){// ======================= 释放RKNN模型 ===================rknn_destroy(ctx);}// ======================= 释放模型数据 ===================if (model){free(model);}return 0;
}

CMakeLists.txt核心配置文件:

# 设置最低版本号
cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
# 设置项目名称,博主的平台是3566
project(rk3566-demo VERSION 0.0.1 LANGUAGES CXX)# 输出系统信息
message(STATUS "System: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")# 设置编译器
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)#  rknn_api 文件夹路径
set(RKNN_API_PATH ${CMAKE_CURRENT_SOURCE_DIR}/librknn_api)
#  rknn_api include 路径
set(RKNN_API_INCLUDE_PATH ${RKNN_API_PATH}/include)
#  rknn_api lib 路径
set(RKNN_API_LIB_PATH ${RKNN_API_PATH}/aarch64/librknnrt.so)# 寻找OpenCV库,使用自定义的OpenCV_DIR
set(3RDPARTY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty)
set(OpenCV_DIR ${3RDPARTY_PATH}/opencv/opencv-linux-${LIB_ARCH}/share/OpenCV)
find_package(OpenCV 3.4.5 REQUIRED) # 输出OpenCV信息
message(STATUS "include path: ${OpenCV_INCLUDE_DIRS}")# 用来搜索头文件的目录
include_directories(${OpenCV_INCLUDE_DIRS}${RKNN_API_INCLUDE_PATH}
)# 测试NPU:rknn alexnet 
add_executable(alexnet src/AlexNet.cpp)# 链接库
target_link_libraries(alexnet${RKNN_API_LIB_PATH}${OpenCV_LIBS}
)

编译和链接,完成推理,查看结果:

# 用于配置 CMake 项目的命令 
# -S .: 指定了源代码目录,.当前目录
# -B build: 指定了构建目录,当前目录下创建build子目录
cmake -S . -B build# 使用先前配置好的构建系统来编译和链接项目
cmake --build build# 执行推理
./build/alexnet ./weights/AlexNet.rknn ./images/sunflowers.jpg

向日葵图片预测不准确:
在这里插入图片描述

不知道为什么,可能是在模型转化过程中造成了精度损失,在五种花分类这种类别相近的任务中,分类准确度超级低。


总结

尽可能简单、详细的介绍了pytorch模型到rknn模型的转化,C++下 RKNN Toolkit 和 RKNPU2 环境的搭建以及 rknn 模型的 RKNPU2 部署。

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

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

相关文章

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…

51单片机-独立按键与数码管联动

独立键盘和矩阵键盘检测原理及实现 键盘的分类&#xff1a;编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如&#xff1a;计算机键盘。靠软件编程识别的称为非编码键盘&#xff1b;在单片机组成…

华为无线AC+AP组网实际应用小结

之前公司都是使用的H3C的交换机、防火墙以及无线AC和AP的&#xff0c;最近优化下无线网络&#xff0c;说新的设备用华为的&#xff0c;然后我是直到要部署的当天才知道用华为设备的&#xff0c;就很无语了&#xff0c;一点准备没有&#xff0c;以下为这次的实际操作记录吧&…

浅谈网络 | 传输层之TCP协议

目录 TCP 包头格式TCP 的三次握手TCP 的四次挥手TCP 的可靠性与"靠谱"的哲学TCP流量控制TCP拥塞控制 上一章我们提到&#xff0c;UDP 就像我们小时候一样简单天真&#xff0c;它相信“网之初&#xff0c;性本善&#xff0c;不丢包&#xff0c;不乱序”&#xff0c;因…

MongoDB相关问题

视频教程 【GeekHour】20分钟掌握MongoDB Complete MongoDB Tutorial by Net Ninja MongoDB开机后调用缓慢的原因及解决方法 问题分析&#xff1a; MongoDB开机后调用缓慢&#xff0c;通常是由于以下原因导致&#xff1a; 索引重建&#xff1a; MongoDB在启动时会重建索引…

嵌入式驱动开发详解3(pinctrl和gpio子系统)

文章目录 前言pinctrl子系统pin引脚配置pinctrl驱动详解 gpio子系统gpio属性配置gpio子系统驱动gpio子系统API函数与gpio子系统相关的of函数 pinctrl和gpio子系统的使用设备树配置驱动层部分用户层部分 前言 如果不用pinctrl和gpio子系统的话&#xff0c;我们开发驱动时需要先…

[模版总结] - 树的基本算法4 -最近公共祖先 LCA

什么是最近公共祖先LCA LCA&#xff1a;在一个树中&#xff0c;距离两个节点p,q最近可以是其本身并且同时包含这两个子节点的节点 题目连接 Leetcode 236 - LCA Leetcode 1644 - LCA II Leetcode 1650 - LCAIII Leetcode 1123 - LCA of Deepest leaves 基本思路 Leetcode 23…

永磁同步电机末端振动抑制(输入整形)

文章目录 1、前言2、双惯量系统3、输入整形3.1 ZV整形器3.2 ZVD整形器3.3 EI整形器 4、伺服系统位置环控制模型5、仿真5.1 快速性分析5.2 鲁棒性分析 参考 1、前言 什么是振动抑制&#xff1f;对于一个需要精确定位的系统&#xff0c;比如机械臂、塔吊、码头集装箱等&#xff…

jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js

参考资料&#xff1a; jQuery-Word-Export导出word_jquery.wordexport.js下载-CSDN博客 近期又需要自己做个 Html2Doc 的解决方案&#xff0c;因为客户又不想要 Html2pdf 的下载了&#xff0c;当初还给我费尽心思解决Html转pdf时中文输出的问题&#xff08;html转pdf文件下载之…

【Redis_Day6】Hash类型

【Redis_Day6】Hash类型 Hash类型操作hash的命令hset&#xff1a;设置hash中指定的字段&#xff08;field&#xff09;的值&#xff08;value&#xff09;hsetnx&#xff1a;想hash中添加字段并设置值hget&#xff1a;获取hash中指定字段的值hexists&#xff1a;判断hash中是否…

【CSP CCF记录】201809-2第14次认证 买菜

题目 样例输入 4 1 3 5 6 9 13 14 15 2 4 5 7 10 11 13 14 样例输出 3 思路 易错点&#xff1a;仅考虑所给样例&#xff0c;会误以为H和W两人的装车时间是一一对应的&#xff0c;那么提交结果的运行错误就会让你瞬间清醒。 本题关键是认识到H和W的装车时间不一定一一对应&…

Unity清除所有的PlayerPrefs

方法1&#xff1a; 直接在你的代码中加入这句 PlayerPrefs.DeleteAll(); 方法2&#xff1a; 点击编辑窗口的这里

非交换几何与黎曼ζ函数:数学中的一场革命性对话

非交换几何与黎曼ζ函数&#xff1a;数学中的一场革命性对话 非交换几何&#xff08;Noncommutative Geometry, NCG&#xff09;是数学的一个分支领域&#xff0c;它将经典的几何概念扩展到非交换代数的框架中。非交换代数是一种结合代数&#xff0c;其中乘积不是交换性的&…

微信小程序下拉刷新与上拉触底的全面教程

微信小程序下拉刷新与上拉触底的全面教程 引言 在微信小程序的开发中,用户体验至关重要。下拉刷新和上拉触底是提高用户交互体验的重要功能,能够让用户轻松获取最新数据和内容。本文将详细介绍这两个功能的实现方式,结合实际案例、代码示例和图片展示,帮助开发者轻松掌握…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

论文阅读:A fast, scalable and versatile tool for analysis of single-cell omics data

Zhang, K., Zemke, N.R., Armand, E.J. et al. A fast, scalable and versatile tool for analysis of single-cell omics data. Nat Methods 21, 217–227 (2024). 论文地址&#xff1a;https://doi.org/10.1038/s41592-023-02139-9 代码地址&#xff1a;https://github.com…

Hive离线数仓结构分析

Hive离线数仓结构 首先&#xff0c;在数据源部分&#xff0c;包括源业务库、用户日志、爬虫数据和系统日志&#xff0c;这些都是数据的源头。这些数据通过Sqoop、DataX或 Flume 工具进行提取和导入操作。这些工具负责将不同来源的数据传输到基于 Hive 的离线数据仓库中。 在离线…

设计模式之 模板方法模式

模板方法模式是行为型设计模式的一种。它定义了一个算法的骨架&#xff0c;并将某些步骤的实现延迟到子类中。模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些特定步骤。 模板方法模式的核心在于&#xff1a; 封装算法的骨架&#xff1a;通过父类中的模板方…

【分治】--- 快速选择算法

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 算法Journey &#x1f3e0; 颜色划分 &#x1f4cc; 题目解析 颜色分类 本题要求我们原地对元数组划分0,1,2三个区域,也就是不能使用辅助数组&#xf…

万物皆可Docker,在NAS上一键部署最新苹果MacOS 15系统

万物皆可Docker&#xff0c;在NAS上一键部署最新苹果MacOS 15系统 哈喽小伙伴们还&#xff0c;我是Stark-C~ 最近苹果Mac mini 2024款在政府补贴的加持下&#xff0c;仅需3500块钱左右就能到手确实挺香的。我看很多评论区的小伙伴跃跃欲试&#xff0c;但是也有不少之前从未体…