【深度学习】【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 并推理的步骤如下:
- 将 PyTorch 预训练模型文件( .pth 或 .pt 格式)转换成 ONNX 格式的文件(.onnx格式),这一转换过程在 PyTorch 环境中进行。
- 将转换得到的 .onnx 文件再次转换成 .rknn 格式的文件,这一转换过程需要在安装有转换工具 rknn-toolkit2 的Ubuntu系统上运行。这里博主建议在 docker 的 Ubuntu 虚拟机上进行。
- 将转换得到的 .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++ 图像分类推理工程:
- rknpu2-master\examples\3rdparty\opencv\opencv-linux-aarch64拷贝到AlexNet;
- rknpu2-master\runtime\RK356X\Linux\librknn_api拷贝到AlexNet;
- 在AlexN目录下新建weights目录将rknn权重文件放到里面;
- 在AlexN目录下新建src目录放置推理代码用于执行c++推理(后面会提供);
- 构建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参数 | ctx | model | model_size | flags | config |
---|---|---|---|---|---|
作用 | 指向一个 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参数 | ctx | cmd | *param | param_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参数 | ctx | n_inputs | inputs |
---|---|---|---|
作用 | 标识当前的 RKNN 模型实例。 | 输入张量的数量,与模型的输入张量数量必须一致。 | rknn_tensor_attr 结构体数组的指针,每个结构体描述一个输入张量的属性和数据。 |
执行推理
用于执行神经网络模型的推理,触发模型的前向传播过程,将输入数据传递给模型,并生成输出结果。
ret = rknn_run(ctx, nullptr);
函数 | ctx | mem |
---|---|---|
作用 | 标识当前的 RKNN 模型实例。 | 指向 rknn_input_output_mem 结构体数组的指针,用于指定输入和输出数据的内存地址;设置为 nullptr,则表示使用默认的输入和输出内存。 |
获取输出
设置 RKNN 模型的输入数据。
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
cudaMalloc参数 | ctx | n_inputs | inputs |
---|---|---|---|
作用 | 标识当前的 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-aarch64/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 部署。
相关文章:
【深度学习】【RKNN】【C++】模型转化、环境搭建以及模型部署的详细教程
【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【深度学习】【RKNN】【C】模型转化、环境搭建以及模型部署的详细教程前言模型转换--pytorch转rknnpytorch转onnxonnx转rkn…...
刷题日常(移动零,盛最多水的容器,三数之和,无重复字符的最长子串)
移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 俩种情况: 1.当nums[i]为0的时候 直接i 2.当nums[i]不为0的时候 此时 …...
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
背景 Jetbrain IDE 支持生成 Test 类,其中选择JUnit5 和 JUnit,但是感觉这不是标准的单元测试,因为接口命名吧。 差异对比 两者生成的单测API名称同原API,没加test前缀的。使用差异主要表现在: setUp & …...
LangChain——HTML文本分割 多种文本分割
Text Splitters 文本分割器 加载文档后,您通常会想要对其进行转换以更好地适合您的应用程序。最简单的例子是,您可能希望将长文档分割成更小的块,以适合模型的上下文窗口。 LangChain 有许多内置的文档转换器,可以轻松地拆分、组…...
Spring事务和事务传播机制
博主主页: 码农派大星. 数据结构专栏:Java数据结构 数据库专栏:数据库 JavaEE专栏:JavaEE 软件测试专栏:软件测试 关注博主带你了解更多知识 目录 1.事务 1.1 什么是事务? 1.2 为什么需要事务? 1.3 事务的操作 2. Spring 中的事务 1. 编程式事务 2. 声明式事务Tra…...
STM32F103外部中断配置
一、外部中断 在上一节我们介绍了STM32f103的嵌套向量中断控制器,其中包括中断的使能、失能、中断优先级分组以及中断优先级配置等内容。 1.1 外部中断/事件控制器 在STM32f103支持的60个可屏蔽中断中,有一些比较特殊的中断: 中断编号13 EXTI…...
ChatGPT的应用场景:开启无限可能的大门
ChatGPT的应用场景:开启无限可能的大门 随着人工智能技术的快速发展,自然语言处理领域迎来了前所未有的突破。其中,ChatGPT作为一款基于Transformer架构的语言模型,凭借其强大的语言理解和生成能力,在多个行业和场景中…...
计算机毕业设计 | SpringBoot+vue社区医院管理系统(附源码+论文)
1,绪论 1.1 研究背景 互联网概念的产生到如今的蓬勃发展,用了短短的几十年时间就风靡全球,使得全球各个行业都进行了互联网的改造升级,标志着互联网浪潮的来临。在这个新的时代,各行各业都充分考虑互联网是否能与本行…...
直播实时美颜平台开发详解:基于视频美颜SDK的技术路径
视频美颜SDK作为实现实时美颜的关键技术,为开发者提供了高效、灵活的解决方案。本篇文章,小编将以“基于视频美颜SDK的技术路径”为主题,深入解析直播实时美颜平台的开发要点。 一、视频美颜SDK的作用与优势 视频美颜SDK是一种集成化的开发工…...
一个专为云原生环境设计的高性能分布式文件系统
大家好,今天给大家分享一款开源创新的分布式 POSIX 文件系统JuiceFS,旨在解决海量云存储与各类应用平台(如大数据、机器学习、人工智能等)之间高效对接的问题。 项目介绍 JuiceFS 是一款面向云原生设计的高性能分布式文件系统&am…...
【Elasticsearch】开启大数据分析的探索与预处理之旅
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
【算法】欧几里得与拓展欧几里得算法
目录 一、欧几里得算法 二、拓展欧几里得算法 2.1 裴蜀定理 2.2 拓展欧几里得算法 2.3 例题 三、线性同余方程 3.1 概念 3.2 例题 一、欧几里得算法 欧几里得算法又称辗转相除法,可用于求解两个数的最大公约数 其思路: gcd(a, b) gcd(b, a%b…...
组合数的求法
1.如果是多组查询的话,需要用数组去储存阶乘的值 n!/(m!(n-m)!) P4071 [SDOI2016] 排列计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<cstdio> #include<iostream> #include<map> #include<cstring> #include<cmath&g…...
【环境搭建】更新Docker Compose到v2.x版本以支持--profile选项
Docker版本陈旧也是搭建的环境起不来的一个重要原因,比如 --profile 选项是 Docker 20.10.0 版本及以上版本才开始支持的,在 Docker Compose v2.1(及以上版本)中引入用于对服务进行分组和按需启动。 更新 Docker Compose 到 v2.x…...
解决 java -jar 报错:xxx.jar 中没有主清单属性
问题复现 在使用 java -jar xxx.jar 命令运行 Java 应用程序时,遇到了以下错误: xxx.jar 中没有主清单属性这个错误表示 JAR 文件缺少必要的启动信息,Java 虚拟机无法找到应用程序的入口点。本文将介绍该错误的原因以及如何通过修改 pom.xm…...
AIGC-----AIGC在虚拟现实中的应用前景
AIGC在虚拟现实中的应用前景 引言 随着人工智能生成内容(AIGC)的快速发展,虚拟现实(VR)技术的应用也迎来了新的契机。AIGC与VR的结合为创造沉浸式体验带来了全新的可能性,这种组合不仅极大地降低了VR内容的…...
【博主推荐】C#的winfrom应用中datagridview常见问题及解决方案汇总
文章目录 1.datagridview绘制出现鼠标悬浮数据变空白2.datagridview在每列前动态添加序号2.1 加载数据集完成后绘制序号2.2 RowPostPaint事件绘制 3.datagridview改变行样式4.datagridview后台修改指定列数据5.datagridview固定某个列宽6.datagridview某个列的显示隐藏7.datagr…...
Selenium 自动化测试demo
场景描述: 模拟用户登录页面操作,包括输入用户名、密码、验证码。验证码为算数运算,如下: 使用到的工具和依赖: 1. Selenium:pip install selenium 2. 需要安装浏览器驱动:这里使用的是Edge 3…...
深度神经网络模型压缩学习笔记二:离线量化算法和工具、实现原理和细节
文章目录 一、离线量化基础概念二、离线量化难点三、离线量化算法介绍四、离线量化工具介绍五、离线量化工具整体设计结构六、离线量化工具代码解读七、实践:Dipoorlet量化MobileNet 一、离线量化基础概念 二、离线量化难点 三、离线量化算法介绍 四、离线量化工…...
uni-app运行 安卓模拟器 MuMu模拟器
最近公司开发移动端系统,使用真机时每次调试的时候换来换去的麻烦,所以使用模拟器来调试方便。记录一下安装和连接的过程 一、安装MuMu模拟器 百度搜索MuMu模拟器并打开官网或者点这里MuMu模拟器官网 点击下载模拟器 安装模拟器,如果系统…...
网络安全,文明上网(6)网安相关法律
列举 1. 《中华人民共和国网络安全法》: - 这是中国网络安全的基本法律,于2017年6月1日开始实施。该法律明确了网络运营者的安全保护义务,包括采取数据分类、重要数据备份和加密等措施。 2. 《中华人民共和国数据安全法》: …...
Perforce Automation With Python
11/2024 出版 MP4 |视频:h264, 19201080 |音频:AAC,44.1 KHz 语言:英语 |大小: 2.65 GB |时长: 5 小时 18 分钟 使用 Python 脚本简化与 Perforce 版本控制系统相关的生产流程 您将学 到什么 …...
卷积神经网络学习记录
目录 神经网络基础定义: 基本组成部分 工作流程 卷积层(卷积定义)【CONV】: 卷积层(Convolutional Layer) 特征提取:卷积层的主要作用是通过卷积核(或滤波器)运算提…...
Spring Cloud Alibaba
What is SCA Spring Cloud Alibaba致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过Spring Cloud编程模型轻松使用这些组件来开发分布式应用服务。 依托Spring Cloud Alibaba,您只需要添加一些注解和少量…...
【AI绘画】Midjourney进阶:色调详解(上)
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: AI绘画 | Midjourney 文章目录 💯前言💯Midjourney中的色彩控制为什么要控制色彩?为什么要在Midjourney中控制色彩? 💯色调白色调淡色调明色调 💯…...
【滑动窗口】找到字符串中所有字母异位词
文章目录 找到字符串中所有字母异位词 class Solution { public:vector<int> findAnagrams(string s, string p) {vector<int> ret;int sLen s.size(), pLen p.size(), validChar;// 母串长度比子串长度还小 直接返回空vectorif (sLen < pLen)return ret;// …...
C++:final 关键字用于阻止类被继承或阻止虚函数被进一步重写
final 关键字的作用 C11 引入了 final 关键字,用于阻止类被继承或阻止虚函数被进一步重写。 防止类被继承:在类声明后添加 final,表示该类不能被继承。防止虚函数被重写:在虚函数声明后添加 final,表示该虚函数在派生…...
sql漏洞
目录 SQL漏洞产生的原因 未对用户输入进行验证和过滤: 动态SQL语句的拼接: 不安全的数据库配置: 缺乏安全意识和培训: 使用过时的技术或框架: 如何避免SQL漏洞产生 使用参数化查询: 对用户输入进行…...
SQL 复杂查询
目录 复杂查询 一、目的和要求 二、实验内容 (1)查询出所有水果产品的类别及详情。 查询出编号为“00000001”的消费者用户的姓名及其所下订单。(分别采用子查询和连接方式实现) 查询出每个订单的消费者姓名及联系方式。 在…...
在 PyTorch 训练中使用 `tqdm` 显示进度条
在 PyTorch 训练中使用 tqdm 显示进度条 在深度学习的训练过程中,实时查看训练进度是非常重要的,它可以帮助我们更好地理解训练的效率,并及时调整模型或优化参数。使用 tqdm 库来为训练过程添加进度条是一个非常有效的方式,本文将…...
PYNQ 框架 - 时钟系统 + pl_clk 时钟输出不准确问题
目录 1. 简介 2. PS 时钟计算 2.1 计算框架 2.2 KV260 的参考时钟 2.3 PL_CLK 设置 3. 测试 3.1 Block design 3.2 引脚绑定 3.3 使用 AD2 测量 3.4 调整分频 4. PYNQ 时钟驱动 4.1 源码解析 4.2 查看 PL_CLK 4.3 配置 PL_CLK 5. 总结 1. 简介 ZYNQ MPSoC 具有…...
【Reinforcement Learning】强化学习下的多级反馈队列(MFQ)算法
📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅…...
Cocos编辑器
1、下载 下载地址:https://www.cocos.com/creator-download 2、编辑器界面介绍 官方链接:https://docs.cocos.com/creator/3.8/manual/zh/editor/ 3、项目结构 官方链接:https://docs.cocos.com/creator/3.8/manual/zh/getting-started/…...
Linux kernel 堆溢出利用方法(三)
前言 本文我们通过我们的老朋友heap_bof来讲解Linux kernel中任意地址申请的其中一种比赛比较常用的利用手法modprobe_path(虽然在高版本内核已经不可用了但ctf比赛还是比较常用的)。在通过两道道近期比赛的赛题来讲解。 Arbitrary Address Allocation…...
文心一言与千帆大模型平台的区别:探索百度AI生态的双子星
随着人工智能技术的迅猛发展,越来越多的公司开始投入资源开发自己的AI解决方案。在中国,百度作为互联网巨头之一,不仅在搜索引擎领域占据重要位置,还在AI领域取得了显著成就。其中,“文心一言”和“千帆大模型平台”便…...
JavaWeb——SpringBoot原理
10.1. 配置优先级 10.1.1. 配置文件 properties > yml(推荐) > yaml 10.1.2. Java系统属性、命令行参数 命令行参数 > Java系统属性 > 配置文件 10.2. Bean管理 10.2.1. 手动获取bean ApplicationContext,IOC容器对象 10.2.2. bean作用域 10.2.3.…...
【算法】连通块问题(C/C++)
目录 连通块问题 解决思路 步骤: 初始化: DFS函数: 复杂度分析 代码实现(C) 题目链接:2060. 奶牛选美 - AcWing题库 解题思路: AC代码: 题目链接:687. 扫雷 -…...
Oracle RAC 环境下数据文件误建在本地目录的处理过程
问题描述 在 Oracle RAC 环境中,有时会误将数据文件创建在本地目录,导致其他节点无法访问该数据文件,从而报出 ORA-01157 和 ORA-01110 错误。 问题分析 错误日志 Mon Nov 16 19:02:38 2021 Errors in file /u01/app/oracle/diag/rdbms/orc…...
使用R语言绘制简单地图的教程
今天主要讲的部分是绘制静态地图,使用的R语言绘图包是tmap,关于介绍就不多讲,下面开始代码的讲解,小白也可以放心食用。 1、绘制简单的单幅地图,这里以新西兰地区为例 #导入必要的包 library(tmap) library(sp) libr…...
【人工智能】基于PyTorch的深度强化学习入门:从DQN到PPO的实现与解析
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 深度强化学习(Deep Reinforcement Learning)是一种结合深度学习和强化学习的技术,适用于解决复杂的决策问题。深度Q网络(DQN)和近端策略优化(PPO)是其中两种经典的算法,被广泛应用于游戏、机器人控…...
学习threejs,使用设置lightMap光照贴图创建阴影效果
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.MeshLambertMaterial…...
类和对象(中)
文章目录 目录1. 类的6个默认成员函数2. 构造函数3. 析构函数4. 拷贝构造函数5. 赋值运算符重载5.1 运算符重载5.2 赋值运算符重载5.3 日期类实现 6. const成员函数7. 取地址及const取地址操作符重载 目录 类的6个默认成员函数构造函数析构函数拷贝构造函数赋值运算符重载cons…...
编程之路,从0开始:预处理详解(完结篇)
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路! 我的博客:<但凡. 我的专栏:编程之路 这一篇预处理详解是我们C语言基础内容学习的最后一篇,也是我们的专栏ÿ…...
[chrome]黑色界面插件,PDF合并插件
Dark Reader_chrome插件下载,最新浏览器扩展,crx离线安装包 - 插件小屋 合并 PDF_chrome插件下载,最新浏览器扩展,crx离线安装包 - 插件小屋 下载的zip包解压成crx,然后把后缀名改为rar,然后解压,再导入解压的目录。...
【c语言】文件操作详解 - 从打开到关闭
文章目录 1. 为什么使用文件?2. 什么是文件?3. 如何标识文件?4. 二进制文件和文本文件?5. 文件的打开和关闭5.1 流和标准流5.1.1 流5.1.2 标准流 5.2 文件指针5.3 文件的打开和关闭 6. 文件的读写顺序6.1 顺序读写函数6.2 对比一组…...
AIGC--AIGC与人机协作:新的创作模式
AIGC与人机协作:新的创作模式 引言 人工智能生成内容(AIGC)正在以惊人的速度渗透到创作的各个领域。从生成文本、音乐、到图像和视频,AIGC使得创作过程变得更加快捷和高效。然而,AIGC并非完全取代了人类的创作角色&am…...
刷题日常(数据流中的中位数,逆波兰表达式求值,最长连续序列,字母异位词分组)
数据流中的中位数 描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()…...
Redis突然变慢,有哪些原因?
目录 一、存在bigkey 二、如果Redis 实例设置了内存上限 maxmemory,有可能导致 Redis 变慢 三、开启了内存大页 四、使用了Swap 五、网络带宽过载 六、频繁短连接 一、存在bigkey 如果Redis实例中存储了 bigkey,那么在淘汰删除 bigkey 释放内存时&…...
Qt入门1——认识Qt的几个常用头文件和常用函数
1.头文件 ① #include <QPushButton>——“按钮”头文件; ② #include <QLabel>——“标签”头文件; ③ #include <QFont>——“字体”头文件; ④#include <QDebug>——输出相关信息; 2. 常用函数/类的基…...
Banana Pi BPI-CanMV-K230D-Zero 采用嘉楠科技 K230D RISC-V芯片设计
概述 Banana Pi BPI-CanMV-K230D-Zero 采用嘉楠科技 K230D RISC-V芯片设计,探索 RISC-V Vector1.0 的前沿技术,选择嘉楠科技的 Canmv K230D Zero 开发板。这款创新的开发板是由嘉楠科技与香蕉派开源社区联合设计研发,搭载了先进的勘智 K230D 芯片。 K230…...