RKNPU2从入门到实践 ---- 【9】使用RKNPU2的C API接口将RKNN模型部署在RK3588开发板上

news2024/9/28 21:19:00

注:作者使用的平台为Ubuntu20.04虚拟系统,开发板为RK3588,开发板上面的系统为Ubuntu22.04。 

前言

      本博文我们要学习使用 RKNPU2 提供的 C API 接口将RKNN模型部署在RK3588开发板上,完成测试图片在开发板上的推理工作。C API接口可以根据帧数据的更新方式分为通用API和零拷贝API。而这一篇博文主要介绍通用 API 接口。

项目文件包

项目文件包以百度网盘链接的形式给出
链接:https://pan.baidu.com/s/1n9M3BwMKDO3NhyfJzBvORQ 
提取码:1234
,整体文件夹如下图所示:

进入到该文件夹中,如下图所示:

一、cmake架构

      由于在后续程序编写的过程中,会涉及到一些第三方库,且瑞芯微提供的例程都是以cmake自动化构建工具来生成可执行文件、库和其他构建目标的。
      因此,在此之前,我们需要先了解一下cmake架构。
      打开Ubuntu虚拟系统,打开终端,创建一个work目录,用来存放后续的cmake工程。

然后将 01_Cmake工程示例 中的 00_example文件夹拷贝到work目录下。

使用 vscode 软件打开 work 这个工程。
打开work工程,如下图所示:

该工程下有两个目录,一个是model目录,另一个是src目录。
model目录存放了测试图片以及适用于RK3568和RK3588的RKNN模型。
src目录下存放了要编译的源码。
build.sh 脚本文件中设置了一些基本的环境变量,以及开始cmake的构建。

注意编译器的路径:

交叉编译器设置步骤如下所示:

1 安装 gcc 交叉编译器,拷贝 gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.gz 到 Ubuntu虚拟系统 的/usr/local/arm64/目录下,这里拷贝的路径要和作者保持一致,后面要用到交叉编译器的绝对路径。如下图所示:

2 解压交叉编译器压缩包 tar -vxf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.gz ,解压完成后即可!


      CMakeLists.txt 文件表示cmake构建的配置文件,如下所示:

在上图中(CMakeLists.txt 文件中),我们截取第11、17行的内容,如下图所示:


我们发现,在设置第三方库rknn_api、opencv的CMAKE_SOURCE_DIR时,需要用到3rdparty这个文件夹。
因此我们将3rdparty这个文件夹放至work目录下,如下图所示。


这一步结束后,work目录下的情况如下图所示:

接下来我们运行build.sh脚本进行cmake工程的构建,运行结果如下:

生成新的目录,如下所示:

      从上图可以看到,多出了build目录和install目录,build目录用来存放工程构建过程中生成的中间文件。install目录用来存放编译完成之后的可执行程序、运行所用到的库以及RKNN模型和推理图片。
      到这里,关于cmake工程结构就结束了,接下来我们来学习通用API部署RKNN模型。

二、通用API部署RKNN模型

RKNPU2通用API使用流程如下所示: 

2.1 前奏工作 

回到vscode软件中,在work工程目录中创建 01_resnet18 目录。

将 00_example目录下的四个文件拷贝到 01_resnet18 目录下,如下所示:


首先,将CmakeLists.txt中的项目名称由:

修改为:

然后将main.cc代码清空,根据RKNPU2通用API使用流程图从零开始编写代码:

程序编写到该步骤(第7行)时可能会报错,这是因为我们还没有添加rknn的头文件。
我们在3rdparty目录中找到rknn头文件rknn_api.h所在的文件夹include的路径,如下图所示:

点击复制路径,然后使用快捷键 ctrl+shift+p 打开搜索框搜索
C/C++: Edit ConFigurations(JSON) ,如下所示:

如果输入C/C++: Edit ConFigurations(JSON)后并没有上图中红色框中的内容弹出,大概率是你的vscode中没有装C/C++ debug拓展,此时需要去拓展库装debug,如下图所示:

进入到 josn 文件后,内容如下所示:

我们需要将刚刚复制的RKNN库文件的路径添加在如下所示的位置中:

添加之后,返回代码处添加rknn头文件,如下图所示:

此时我们发现rknn_context从原先的灰色变为高亮色了,如下图所示:

这说明已经配置成功了。
接下来,就要按流程图进行编写代码了,请看下面

2.2 第一步:调用rknn_init接口创建rknn_context对象、加载 RKNN模型

2.2.1 rknn_init API函数介绍 

rknn_init 初始化函数将创建 rknn_context 对象、加载 RKNN 模型以及根据 flag 和 rknn _init_extend 结构体执行特定的初始化行为。

示例代码如下:

2.2.2 实际代码编写

      在实际代码编写中,调用rknn_init函数时,flag 和rknn_init_extend 目前用不到,因此将flag参数赋值为0,将rknn_init_extend赋值为NULL。

2.3 第二步:调用rknn_query接口查询获取到模型输入输出属性、推理时间、SDK版本等信息

2.3.1 rknn_query API函数介绍

具体介绍后续更新!!在本项目中,确实也用到了这个接口函数,如下图所示:

但由于介绍起来内容较多,因此后续以博文的形式单独介绍这个接口函数!!

2.4 第三步:调用rknn_inputs_set接口设置模型的输入数据

2.4.1 opencv读取输入数据

根据下图中的步骤配置好opencv库文件的路径,如下图所示: 


添加opencv的头文件,如下图所示:

使用opencv读取要推理的图像,如下图所示:

至此,opencv部分就结束了。

2.4.2 rknn_inputs_set API介绍

      通过 rknn_inputs_set 函数可以设置模型的输入数据。该函数能够支持多个输入,其中每个输入是 rknn_input 结构体对象,在传入之前用户需要设置该对象。(注:RV1106/RV1103 不支持这个接口)

示例代码如下:

2.4.3 rknn_inputs_set 实际代码编写

 

2.5 第四步:调用rknn_run接口执行模型推理

2.5.1 rknn_run API介绍

      rknn_run 函数将执行一次模型推理,调用之前需要先通过 rknn_inputs_set 函数或者零拷贝的接口设置输入数据。

示例代码如下:

2.5.2 rknn_run 接口实际代码编写
 ​​​​​​

 

2.6 第五步:调用rknn_outputs_get接口获取模型推理的输出数据

2.6.1 API介绍

      rknn_outputs_get 函数可以获取模型推理的输出数据。该函数能够一次获取多个输出数据。 其中每个输出是 rknn_output 结构体对象,在函数调用之前需要依次创建并设置每个 rknn_output 对象。
      对于输出数据的 buffer 存放可以采用两种方式:一种是用户自行申请和释放,此时 rknn_output 对象的 is_prealloc 需要设置为 1,并且将 buf 指针指向用户申请的 buffer;另一种是由 rknn 来进行分配,此时 rknn_output 对象的 is_prealloc 设置为 0 即可,函数执行之后 buf 将指向输出数据。(注:RV1106/RV1103 不支持这个接口) 


示例代码如下:

2.6.2 实际代码编写


      至此,rknn模型推理图像的过程就已经完成了,输出数据会保存到output结构体中的buffer成员之中。 为了得到我们常见的概率信息,还需要经过后处理部分,后处理的代码如下:

static 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;
}



   // Post Process
  for (int i = 0; i < io_num.n_output; i++) 
  {
    uint32_t MaxClass[5];
    float    fMaxProb[5];
    float*   buffer = (float*)output[i].buf;
    uint32_t sz     = output[i].size / 4;

    rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5);

    printf(" --- Top5 ---\n");
    for (int i = 0; i < 5; i++) {
      printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]);
    }
  }


 

后处理完成之后,就需要释放前面所创建的资源了。请看下面。

2.7 第六步:调用rknn_outputs_release接口释放推理输出的相关资源

2.7.1 rknn_outputs_release API介绍

rknn_outputs_release 函数将释放 rknn_outputs_get 函数得到的输出的相关资源。 


示例代码如下所示:

2.7.2 实际代码编写


2.8 第七步:调用rknn_destroy释放传入的rknn_context及其相关资源

2.8.1 rknn_destroy API介绍

rknn_destroy 函数将释放传入的 rknn_context 及其相关资源。 


示例代码如下:

2.8.2 实际代码编写

到此,使用通用 API 加载RKNN模型并推理的程序就编写完成了。 

2.8.3 最终代码

整体代码如下所示:

#include<stdio.h>
#include "rknn_api.h"
#include "opencv2/core.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;

static 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;
}


int main(int argc, char *argv[]){
  /*要求程序传入的第一个参数为RKNN模型,第二个参数为要推理的图片*/
  char *model_path = argv[1];
  char *image_path = argv[2];
  /*调用rknn_init接口将RKNN模型的运行环境和相关信息赋予到context变量中*/
  rknn_context context;
  rknn_init(&context,model_path,0,0,NULL);

  /*使用opencv读取要推理的图像数据*/
  cv::Mat img = cv::imread(image_path); 
  /*使用cvtColor进行通道转换*/
  cv::cvtColor(img,img,cv::COLOR_BGR2RGB);

  /*调用rknn_query接口查询tensor输入输出个数*/
  rknn_input_output_num io_num;
  rknn_query(context,RKNN_QUERY_IN_OUT_NUM,&io_num,sizeof(io_num));
  printf("model input num:%d,output num:%d\n",io_num.n_input,io_num.n_output);

  /*调用rknn_inputs_set接口设置输入数据*/
  rknn_input input[1];
  memset(input,0,sizeof(rknn_input));
  input[0].index = 0;
  input[0].buf = img.data;
  input[0].size = img.rows*img.cols*img.channels()*sizeof(uint8_t);
  input[0].pass_through = 0;
  input[0].type = RKNN_TENSOR_UINT8;
  input[0].fmt = RKNN_TENSOR_NHWC;
  rknn_inputs_set(context,1,input);

  /*调用rknn_run接口进行模型推理*/
  rknn_run(context,NULL);
  /*调用rknn_outputs_get接口获取模型推理结果*/
  rknn_output output[1]; 
  memset(output,0,sizeof(rknn_output));
  output[0].index = 0;
  output[0].is_prealloc = 0;
  output[0].want_float = 1; // 表示将输出数据转换为浮点类型
  rknn_outputs_get(context,1,output,NULL);

   // Post Process
  for (int i = 0; i < io_num.n_output; i++) 
  {
    uint32_t MaxClass[5];
    float    fMaxProb[5];
    float*   buffer = (float*)output[i].buf;
    uint32_t sz     = output[i].size / 4;

    rknn_GetTop(buffer, fMaxProb, MaxClass, sz, 5);

    printf(" --- Top5 ---\n");
    for (int i = 0; i < 5; i++) {
      printf("%3d: %8.6f\n", MaxClass[i], fMaxProb[i]);
    }
  }


  /*调用rknn_outputs_release接口释放推理输出的相关资源*/
  rknn_outputs_release(context,1,output);

  /*调用rknn_destory接口销毁context变量*/
  rknn_destroy(context);

  return 0;
}



2.9 运行build.sh文件进行cmake工程的构建 

      运行build.sh前后注意观察该文件夹内容变化,左图为没有运行build.sh文件之前的,右图为运行build.sh文件之后的。 


运行结束后终端输出信息如下:

      我们可以看到运行build.sh文件后多出了两个目录,一个build目录和install目录。install目录就是我们要放在开发板上运行测试的文件夹。这在博文刚开始的时候已经介绍过了,这里就不再赘述。

2.10 启动开发板、将生成的install目录拷贝到开发板系统上

2.10.1 开发板与电脑相连 

      将开发板与电脑连接好之后,启动开发板会在虚拟系统上弹出如下界面,按照下图选择并点击确定按键。

点击确定之后,若得到如下图:

      即在虚拟系统任务栏处出现了手机的标识,那么就说明开发板的adb工具已经成功连接至虚拟系统上了。

2.10.2 将生成的install目录拷贝到开发板系统上

      在这一操作中,有很多种方式,例如:用优盘拷贝等,但在这里,有一种更为简单的方式,即使用开发板的adb工具。
打开终端,如下图所示:

进入到 01_resnet18 目录,如下图所示:

使用adb push [xxx] [xxx] ,将install目录拷贝到开发板系统的根目录上,如下图所示:

      我们可以使用 adb shell 命令来进入到开发板的系统中,并查看install目录是否已经拷贝完成,如下图所示:

发现install已经放至开发板系统的根目录上去了。
进入 install目录中,如下图:

进入resnet18_Linux目录下:

接下来,使用./resnet18运行模型,第一个参数为rknn模型的路径,第二个参数为要推理的图片路径,如下所示:

当准备输入model时,会自动弹到如下界面的形式,并继续输入第二个参数剩余部分即可:

输入完成之后,按下回车键,得到运行结果:

我们看到812号的值(具体是什么值,目前有争议,等待后续更新)最大,而812号正是太空飞船,故推理成功。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【书生3.2】Lagent 自定义你的 Agent 智能体

Lagent 自定义你的 Agent 智能体 1 环境准备2 启动模型3 部署lagent4 可视化页面及配置5 自定义工具的智能体 1 环境准备 直接复用之前的xtuner或者llamaindex环境 # 安装其他依赖包 conda activate llamainde pip install termcolor2.4.0 pip install lmdeploy0.5.2pip inst…

Gazebo Harmonic gz-harmonic 和 ROS2 Jazzy 思考题 建图和导航 SLAM Navigation

仿真 效果还挺好的。 SLAM建图 SLAM 仿真测试录屏 配置环境 Gazebo Harmonic ROS Jazzy 导航 …… 提示 这篇文档详细介绍了如何在ROS 2环境中使用SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;即同时定位与地图构建&#xff09;和Nav2&#xff08;N…

XXE-labs靶场通关攻略

环境地址自行查找 1.寻找靶机地址 使用工具goby进行扫描 因为我的靶场是搭在ubuntu上 直接查找系统是Ubuntu的就可以找到 靶机IP 172.16.1.183 2.访问靶场 3.使用目录扫描工具进行扫描 使用kali自带的dirsearch进行扫描 可以看到一个robots.txt文件 4.访问robots.txt文件 …

白夜剧场再出爆款《边水往事》,这次能“带飞”优酷吗?

这个暑期档&#xff0c;优酷靠着白夜剧场悬疑新作《边水往事》再下一城。 谁能想到刚开播时无人问津的《边水往事》竟然“爆”了&#xff0c;8月16日首播当天市占率仅仅1.73%&#xff0c;播放量不到700万&#xff0c;而在上线第十天&#xff0c;《边水往事》终于逆袭夺冠&…

#QT 笔记一

重点&#xff1a;面试考试大概率涉及&#xff0c;需要不借助任何资料掌握。掌握&#xff1a;面试考试可能涉及&#xff0c;需要不借助任何资料掌握。熟悉&#xff1a;面试考试可能涉及&#xff0c;可以稍微参考资料掌握。了解&#xff1a;面试考试小概率涉及&#xff0c;面试拔…

Navicat连接SqlServer

一、前提条件 本地已经安装好了SqlServer和navicat 二、操作 打开sqlserver配置管理器 tcp默认关闭&#xff0c;我们右击打开 需要重启服务才能生效 三、效果

【ACM出版,EIScopus快检索-高录用】2024年数字经济与计算机科学国际学术会议(DECS2024,9月20-22)

欢迎参加2024年数字经济与计算机科学国际学术会议&#xff08;DECS2024&#xff09;&#xff0c;本次大会得到了马来西亚理工大学、北京科技大学经济管理学院、南京信息工程大学、马来西亚敦胡先翁大学的大力支持&#xff01; 旨在汇聚全球在数字经济与计算机科学领域内的研究者…

《黑神话:悟空》爆火,对程序员的 5 点启示(1)

# 前言 2024年8月&#xff0c;被誉为 “首部国产 3A 大作” 的《黑神话&#xff1a;悟空》一段13分钟的实机演示视频&#xff0c;简直像是给全球玩家投下了一颗冲击弹&#xff0c;瞬间炸开了一片热闹场面&#xff0c;点燃了海内外游戏和西游文化爱好者的热情&#xff0c;迅速地…

How to work with OpenAI maximum context length is 2049 tokens?

题意&#xff1a;"如何处理OpenAI的最大上下文长度为2049个tokens的问题&#xff1f;" 问题背景&#xff1a; Id like to send the text from various PDFs to OpenAIs API. Specifically the Summarize for a 2nd grader or the TL;DR summarization APIs. "…

重谈地址空间

虚拟地址是如何转化的物理地址的&#xff1f; 我们以32位计算机为例子 32 为计算机的虚拟地址就是32位。 32 位的虚拟地址 分为三个部分 为 10 10 12&#xff1b; 这是由页表内部的结构决定的。 页表分为两级 虚拟地址的前十位为一级页表对应的位置&#xff0c; 次10位表示…

鸿蒙(API 12 Beta3版)【识别本地图片】

基本概念 图片识码能力支持对图库中的码图进行扫描识别&#xff0c;并获取信息。 场景介绍 图片识码能力支持对图库中的条形码、二维码、MULTIFUNCTIONAL CODE进行识别&#xff0c;并获得码类型、码值、码位置信息。该能力可用于一图单码和一图多码的识别&#xff0c;比如条…

【HarmonyOS】模仿个人中心头像图片,调用系统相机拍照,从系统相册选择图片和圆形裁剪显示 (二)

【HarmonyOS】模仿个人中心头像图片&#xff0c;调用系统相机拍照&#xff0c;从系统相册选择图片和圆形裁剪显示 &#xff08;二&#xff09; Demo效果展示&#xff1a; 方案思路&#xff1a; 1.修改调用相机的方式&#xff0c;使用cameraKit进行相机的调用&#xff0c;拍照…

怎么扫描试卷去除笔迹?建议试试这样做

怎么扫描试卷去除笔迹&#xff1f;在现代教育和办公环境中&#xff0c;电子版试卷的管理和使用变得越来越普遍。然而&#xff0c;手写答案和批注常常使得电子版试卷难以恢复到原始的空白状态。为了满足这一需求&#xff0c;市场上涌现出许多能够扫描试卷并去除笔迹的技术和工具…

巧用 HTML 列表:<ul>、<ol>、<dl>的实用指南

目录 无序列表 容器级标签 有序列表 定义列表 一个dt配很多dd 每一个dl里面只有一个dt和dd 一个dl配多个dt 多级列表 无序列表 <ul>标签用于定义无序列表。无序列表的特点是各个列表项之间没有特定的顺序&#xff0c;通常以小圆点作为先导符号。所有主流浏览器…

模拟+思维(时间规划烧饼)

前言&#xff1a;这个题目就是我们小时候学的的活动规划烧饼&#xff0c;我们要先算出我们最大耗时是多少&#xff0c;然后再对我们的活动进行规划 题目地址 我们这个题目还要求算出k&#xff08;执行次数&#xff09;我的做法是写两遍代码&#xff0c;其实也可以存起来&#x…

“重启就能解决一切问题”,iPhone重启方法大揭秘

随着iPhone不断更新换代&#xff0c;其设计与操作方式也在不断进化。从最初的实体Home键到如今的全面屏设计&#xff0c;iPhone的操作逻辑也随之发生了改变。 对于那些习惯了传统安卓手机操作的用户来说&#xff0c;iPhone的重启方式可能会显得有些不同寻常。下面我们就来一起…

学习之SQL语句之DCL(数据控制语言)

DCL英文全称是Data Control Language(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访问 权限

滚柱导轨:数控机床高效运行的驱动力

机床制造者最关心的莫过于机床的精度&#xff0c;刚性和使用寿命&#xff0c;对导轨系统的关注甚少。但导轨为机床功能的实现奠定了可靠的基础&#xff0c;各种类型的机床工作部件&#xff0c;都是利用控制轴在指定的导轨上运动。机床设计者根据机床的类型和用途选用各种不同形…

前波士顿咨询Platinion董事总经理陈果加入望繁信科技

“很荣幸邀请果总加盟望繁信科技&#xff01;作为中国互联网可以查到的写作流程挖掘介绍文章第一人&#xff0c;他的先驱性工作为流程挖掘在中国的知识普及和应用创新做出了重要贡献&#xff01;更难能可贵的&#xff0c;是我们和果总在价值观层面高度契合&#xff01;我们非常…

Git学习尚硅谷(001 git介绍)

尚硅谷Git入门到精通全套教程&#xff08;涵盖GitHub\Gitee码云\GitLab&#xff09; 总时长 4:52:00 共45P 此文章包含第1p-第p7的内容 文章目录 git介绍课程介绍git概述 何为版本控制集中式版本控制工具分布式版本控制工具git简史工作机制代码托管中心 git的安装 git介绍 课…