Rk3566/RK3588 yolov5部署(一)Ubuntu系统烧写及cmake安装
RK3566/RK3588 yolov5部署(三)yolov5单线程部署并查看NPU占用情况
RK3566/RK3588 YoloV5部署(四) yolov5多线程部署
本文提供rk3566和rk3588平台下mobilenet测试代码
rk3566:
通过网盘分享的文件:NPU_Test_rk3566
链接: https://pan.baidu.com/s/1-onBvnai-qZkGf8aECSpdA?pwd=3566 提取码: 3566
rk3588:
通过网盘分享的文件:NPU_Test_rk3588
链接: https://pan.baidu.com/s/1cv5Lpkf4slKMoxhudFeDcQ?pwd=3588 提取码: 3588
不熟悉开发板系统烧写及cmake安装可以参考:Rk3566 yolov5部署(一)Ubuntu系统烧写及cmake安装(rk3588流程相同)
1.vscode 远程登陆开发板(以下演示使用rk3566,3588流程相同)
打开vscode 点击左侧扩展,搜索ssh,安装Remote - SSH插件
安装结束后点击左下角打开远程窗口按钮,选择连接到主机,点击添加新的主机
输入ssh orangepi@(ip地址 ifconfig查看),回车
点击第一个并在右下角弹出窗口选择连接
连接成功后右下角变成远程主机
在远程主机的vscode中进入代码文件夹,在当前文件夹中打开终端,输入
cmake -S . -B build
cmake --build build
编译完成后会在build文件夹下生成可执行文件,进入build文件夹,输入
./mobilenet ../weights/moblienetv3_demo.rknn ../images/cat_224x224.jpg
得到结果如下:
image文件夹下还有一张测试图片,可以输入以下命令测试
./mobilenet ../weights/moblienetv3_demo.rknn ../images/dog_224x224.jpg
有输出结果,则NPU可用
2.rknn_api
由于rknn_api用c语言开发,其使用面型对象思想,需要一个上下文传递信息(类似ffmpeg)
首先看src/main.cpp,从主函数开始
int ret = rknn_init(&ctx, model, model_len, 0, NULL);
初始化 RKNN 上下文,加载 RKNN 模型
&ctx
:指向rknn_context
类型变量的指针,用于存储初始化后的上下文句柄。model
:指向 RKNN 模型数据的指针,通常由load_model
函数加载得到。model_len
:RKNN 模型数据的长度(字节数)。0
:这里是初始化标志,设为 0 表示使用默认配置。NULL
:用户自定义的配置参数,设为NULL
表示使用默认配置。
若初始化成功,返回RKNN_SUCC
(通常为 0);若失败,则返回对应的错误码。
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));
ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
查询 RKNN 模型的相关信息,像输入输出数量、输入输出张量属性等。
ctx
:RKNN 上下文句柄,由rknn_init
函数初始化得到。RKNN_QUERY_IN_OUT_NUM
、RKNN_QUERY_INPUT_ATTR
、RKNN_QUERY_OUTPUT_ATTR
:查询类型,分别表示查询输入输出数量、输入张量属性、输出张量属性。&io_num
、&(input_attrs[i])
、&(output_attrs[i])
:用于存储查询结果的结构体指针。sizeof(io_num)
、sizeof(rknn_tensor_attr)
:存储查询结果的结构体的大小。
若查询成功,返回RKNN_SUCC
;若失败,则返回对应的错误码。
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
为 RKNN 模型设置输入数据。
ctx
:RKNN 上下文句柄。io_num.n_input
:输入数据的数量。inputs
:指向rknn_input
类型数组的指针,该数组包含了输入数据的相关信息,如索引、数据类型、大小、格式、数据缓冲区指针等。
ret = rknn_run(ctx, nullptr);
执行 RKNN 模型的推理操作。
ctx
:RKNN 上下文句柄。nullptr
:这里是推理时的额外参数,设为nullptr
表示不使用额外参数。
ret = rknn_outputs_get(ctx, 1, outputs, NULL);
获取 RKNN 模型的输出结果。
ctx
:RKNN 上下文句柄。1
:要获取的输出结果的数量(这里写1是应为模型输出为1,输入一张图片,输出一个rknn_output类型结构体)。outputs
:指向rknn_output
类型数组的指针,该数组用于存储输出结果的相关信息,如是否需要浮点型数据、数据缓冲区指针等。NULL
:这里是输出结果的额外参数,设为NULL
表示不使用额外参数。
代码流程比较简单,不做解释了,之后在部署yolov5时会对rknn_api简单封装。
RK3566,和RK3588区别只在cmakelist文件中DEVICE_NAME变量名称(无关紧要)和weight文件夹下rknn模型文件不同 ,模型文件需要严格对应芯片型号。
附上main.cpp及cmakelists.txt
//引入opencv,rknn等库
#include "opencv2/core/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "rknn_api.h" //rknn_api.h内部已经包含extern "C"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <fstream>
#include <iostream>
using namespace std;
using namespace cv;
int rknn_GetTop(float *pfProb, float *pfMaxProb, uint32_t *pMaxClass, uint32_t outputCount, uint32_t topNum)
{
uint32_t i, j;
#define MAX_TOP_NUM 20
if (topNum > MAX_TOP_NUM)
return 0;
memset(pfMaxProb, 0, sizeof(float) * topNum);
memset(pMaxClass, 0xff, sizeof(float) * topNum);
for (j = 0; j < topNum; j++)
{
for (i = 0; i < outputCount; i++)
{
if ((i == *(pMaxClass + 0)) || (i == *(pMaxClass + 1)) || (i == *(pMaxClass + 2)) || (i == *(pMaxClass + 3)) ||
(i == *(pMaxClass + 4)))
{
continue;
}
if (pfProb[i] > *(pfMaxProb + j))
{
*(pfMaxProb + j) = pfProb[i];
*(pMaxClass + j) = i;
}
}
}
return 1;
}
unsigned char* load_model(const char* model_path, int* model_len)
{
FILE *fp = fopen(model_path,"rb");
if(fp == nullptr)
{
cerr << "open model faile!" << endl;
return NULL;
}
fseek(fp,0,SEEK_END);
int model_size = ftell(fp);
unsigned char *model = (unsigned char *)malloc(model_size);
fseek(fp,0,SEEK_SET);
if(model_size != fread(model,1,model_size,fp))
{
cerr << "read model faile!" << endl;
return NULL;
}
*model_len = model_size;
if(fp)
{
fclose(fp);
}
return model;
}
void dump_tensor_attr_info(rknn_tensor_attr *attr)
{
cout << "index= " << attr->index << endl;
cout << "name= " << attr->name << endl;
cout << "n_dims= " << attr->n_dims << endl;
cout << "dims= " ;
for(int i = 0; i < attr->n_dims; i++)
{
cout << attr->dims[i] << " ";
}
cout << endl;
cout << "n_elems= " << attr->n_elems << endl;
cout << "size= " << attr->size << endl;
cout << "fmt= " << get_format_string(attr->fmt) << endl;
cout << "type= " << get_type_string(attr->type) << endl;
cout << "qnt_type= " << get_qnt_type_string(attr->qnt_type) << endl;
cout << "zp= " << attr->zp << endl;
cout << "scale= " << attr->scale << endl;
}
int main(int argc, char* argv[])
{
//设置输入图片宽,高,通道
const int MODEL_INPUT_HEIGHT = 224;
const int MODEL_INPUT_WIDTH = 224;
const int MODEL_INPUT_CHANNLES = 3;
rknn_context ctx = 0;
int model_len = 0;
unsigned char *model = 0;
if (argc != 3)
{
cerr <<"Usage:"<< argv[0] << "<rknn model> <image_path>" << endl;
return -1;
}
//读取图片并转换颜色通道顺序
const char *img_path = argv[2];
const char *model_path = argv[1];
cv::Mat img = imread(img_path, cv::IMREAD_COLOR);
if(!img.data)
{
cerr << "imread failed!" << endl;
return -1;
}
if (img.cols != MODEL_INPUT_WIDTH || img.rows != MODEL_INPUT_HEIGHT)
{
cout << "resize image from " << img.cols << ":" << img.rows << " to " << MODEL_INPUT_WIDTH << ":" << MODEL_INPUT_HEIGHT << endl;
cv::resize(img, img, cv::Size(MODEL_INPUT_WIDTH, MODEL_INPUT_HEIGHT), 0, 0, cv::INTER_LINEAR);
}
cv::Mat img_rgb;
cv::cvtColor(img,img_rgb, cv::COLOR_BGR2RGB);
model = load_model(model_path, &model_len);
//rknn模型初始化
int ret = rknn_init(&ctx, model, model_len,0,NULL);
if(ret < 0)
{
cerr << "rknn init fail!" << endl;
return -1;
}
//查询模型输入输出数量(mobilenet输入输出都是1)
rknn_input_output_num io_num;
ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
if(ret != RKNN_SUCC)
{
cerr << "rknn query num fail!" << endl;
return -1;
}
cout << "model input num: " << io_num.n_input << "output num: " << io_num.n_output << endl;
rknn_tensor_attr input_attrs[io_num.n_input];
memset(input_attrs, 0, sizeof(input_attrs));
//查询输入输出张量的具体信息
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));
if(ret != RKNN_SUCC)
{
cerr << "rknn query input_attr fail!" << endl;
return -1;
}
dump_tensor_attr_info(&(input_attrs[i]));
}
rknn_tensor_attr output_attrs[io_num.n_output];
memset(output_attrs, 0, sizeof(output_attrs));
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)
{
cerr << "rknn query output_attr fail!" << endl;
return -1;
}
dump_tensor_attr_info(&(output_attrs[i]));
}
//插入输入数据
rknn_input inputs[1];
memset(inputs, 0, sizeof(inputs));
inputs[0].index = 0;
inputs[0].type = RKNN_TENSOR_UINT8;
inputs[0].size = img.cols * img.rows * img.channels() * sizeof(uint8_t);
inputs[0].fmt = RKNN_TENSOR_NHWC;
inputs[0].buf = img_rgb.data;
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
if(ret < 0 )
{
cerr << "rknn_inputs_set fail!" << endl;
return -1;
}
cout << "rknn_running ..." << endl;
ret = rknn_run(ctx, nullptr);
if(ret < 0 )
{
cerr << "rknn_run fail!" << endl;
return -1;
}
//获取输出结果
rknn_output outputs[1];
memset(outputs, 0, sizeof(outputs));
outputs[0].want_float = 1;
ret = rknn_outputs_get(ctx, 1, outputs, NULL);
if (ret < 0)
{
cerr << "rknn_outputs_get fail!" << endl;
return -1;
}
uint32_t MaxClass[5];
float fMaxProb[5];
float *buffer = (float *)outputs[0].buf;
uint32_t sz = outputs[0].size / 4;
rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5);
cout << " --- Top5 ---" << endl;
for (int i = 0; i < 5; i++)
{
cout << MaxClass[i] << ":" << fMaxProb[i] << endl;
}
ret = rknn_outputs_release(ctx, 1, outputs);
if (ret < 0)
{
cerr << "rknn_outputs_release fail!" << endl;
return -1;
}
else if (ctx > 0)
{
rknn_destroy(ctx);
}
if(model)
{
free(model);
}
return 0;
}
cmake_minimum_required(VERSION 3.11 FATAL_ERROR)
project(rk3566_mobilenet VERSION 0.1 LANGUAGES CXX)
message(STATUS "System: ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(LIB_ARCH "aarch64")
set(DEVICE_NAME "RK3566")
set(RKNN_API_PATH ${CMAKE_CURRENT_SOURCE_DIR}/librknn_api)
set(RKNN_API_INCLUDE_PATH ${RKNN_API_PATH}/include)
set(RKNN_API_LIB_PATH ${RKNN_API_PATH}/${LIB_ARCH}/librknnrt.so)
set(3RDPARTY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty)
set(OpenCV_DIR ${3RDPARTY_PATH}/opencv/opencv-linux-${LIB_ARCH}/share/OpenCV)
find_package(OpenCV REQUIRED)
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
include_directories(
${OpenCV_INCLUDE_DIRS}
${RKNN_API_INCLUDE_PATH}
)
add_executable(mobilenet src/main.cpp)
target_link_libraries(mobilenet
${RKNN_API_LIB_PATH}
${OpenCV_LIBS}
)