当前位置:   article > 正文

【教程】在RK3568上部署(C++)语义分割算法BiSeNetv1/v2_rk3568 yolo c++

rk3568 yolo c++

引言

在本篇教程中,博主将记录国庆假期前在RK3568上部署分割算法的步骤以及代码。首先说一下,RK3568这个开发板本身的算力大概是0.8T(在实际开发中还会用到额外的计算卡,额外的计算卡后面文章再说,本篇文章主要记录在RK3568上的部署过程)。

一、获取rknn模型

1、这步不是很难,我之前也写过BiSeNet的教程,官方提供的代码也很好理解,并且提供了onnx模型的导出代码。教程--从零开始使用BiSeNet(语义分割)网络训练自己的数据集_计算机幻觉的博客-CSDN博客为了从图片分割出我们想要的特征,我们采用BiSeNet作为分割模型,并且在自己制作的数据集上进行训练测试。注:训练是在linux环境下的,Win下训练可能会有点问题。_bisenethttps://blog.csdn.net/qq_39149619/article/details/131882664?spm=1001.2014.3001.55012、将导出的onnx代码进行rknn转换,RK3568需要用到rknn-toolkit2,该环境的安装之前也写过:

RKNN-ToolKit2 1.5.0安装教程_rknn安装-CSDN博客由于种种原因需要用到开发版RK3568,需要预先安装RKNN-Toolkit2进行模型转化等,博主安装的版本是1.5.0,Ubuntu版本是20.04,python版本3.6。1、原本准备采取docker安装,但是文件有点大再加上网速不行,于是我们采用pip方法进行安装。,直接点击下载,得到rknn-toolkit2-master.zip,并且解压到任意文件夹中。_rknn安装https://blog.csdn.net/qq_39149619/article/details/131694631?spm=1001.2014.3001.5501转换代码:mean_values和std_values需要根据自己训练集修改

  1. from rknn.api import RKNN
  2. ONNX_MODEL = '/home/zw/Prg/Pycharm/file/RKNN3568/onnx/yolov5-seg/best_480x480.onnx'
  3. platform = "rk3568"
  4. RKNN_MODEL = '/home/zw/Prg/Pycharm/file/RKNN3568/rknn/BiSeNetV2/BiSeNetv2_320x320_min4_{}_out_opt.rknn'.format(platform)
  5. if __name__ == '__main__':
  6. # Create RKNN object
  7. rknn = RKNN(verbose=False)
  8. # pre-process config
  9. print('--> config model')
  10. rknn.config(mean_values=[82.9835, 93.9795, 82.1893], std_values=[54.02, 54.804, 54.0225], target_platform='rk3568') #BiSeNet
  11. print('done')
  12. # Load tensorflow model
  13. print('--> Loading model')
  14. ret = rknn.load_onnx(model=ONNX_MODEL, outputs=['preds']) # 这里一定要根据onnx模型修改
  15. if ret != 0:
  16. print('Load onnx model failed!')
  17. exit(ret)
  18. print('done')
  19. # Build model
  20. print('--> Building model')
  21. ret = rknn.build(do_quantization=False, dataset='/home/zw/Prg/Pycharm/file/RKNN3568/dataset.txt')
  22. if ret != 0:
  23. print('Build rkmodel failed!')
  24. exit(ret)
  25. print('done')
  26. # rknn.export_rknn_precompile_model(RKNN_MODEL)
  27. rknn.export_rknn(RKNN_MODEL)
  28. rknn.release()

二、在RK3568上进行C++部署

1、首先,从官网下载rknpu2相关文件,官网地址:GitHub - rockchip-linux/rknpu2,该文件包含了rknn相关的接口文件以及提供的示例代码。同时,我们创建bisenetv2的例子,如下图:

model存放转换好的rknn文件,main是推理C++代码。

2、想在3568上部署,需要对程序进行编译,详细的参考官方提供的说明pdf,讲解的简单易懂,这里就不多说。编译需要用到交叉编译工具,这里提供下载地址:Firefly-Linux / prebuilts / gcc / linux-x86 / aarch64 / gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linux-gnu · GitLab

下载到任意位置即可,打开build-linux_RK3566_RK3568.sh文件,修改入下:gcc替换成你自己的地址。

  1. #!/bin/bash
  2. set -e
  3. TARGET_SOC="rk356x"
  4. # for aarch64
  5. # GCC_COMPILER=aarch64-linux-gnu
  6. export TOOL_CHAIN=/home/zw/Downloads/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linux-gnu-firefly
  7. GCC_COMPILER=/home/zw/Downloads/gcc-buildroot-9.3.0-2020.03-x86_64_aarch64-rockchip-linux-gnu-firefly/bin/aarch64-rockchip-linux-gnu
  8. export LD_LIBRARY_PATH=${TOOL_CHAIN}/lib64:$LD_LIBRARY_PATH
  9. export CC=${GCC_COMPILER}-gcc
  10. export CXX=${GCC_COMPILER}-g++
  11. ROOT_PWD=$( cd "$( dirname $0 )" && cd -P "$( dirname "$SOURCE" )" && pwd )
  12. # build
  13. BUILD_DIR=${ROOT_PWD}/build/build_linux_aarch64
  14. if [[ ! -d "${BUILD_DIR}" ]]; then
  15. mkdir -p ${BUILD_DIR}
  16. fi
  17. cd ${BUILD_DIR}
  18. cmake ../.. \
  19. -DTARGET_SOC=${TARGET_SOC} \
  20. -DCMAKE_C_COMPILER=${GCC_COMPILER}-gcc \
  21. -DCMAKE_CXX_COMPILER=${GCC_COMPILER}-g++
  22. make -j4
  23. make install
  24. cd -

cmakelist文件爱你没啥要改的,修改好自己项目名称即可:

  1. cmake_minimum_required(VERSION 3.4.1)
  2. project(rknn_bisenetv2_demo)
  3. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
  4. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  5. # rknn api
  6. if(TARGET_SOC STREQUAL "rk356x")
  7. set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../runtime/RK356X/${CMAKE_SYSTEM_NAME}/librknn_api)
  8. elseif(TARGET_SOC STREQUAL "rk3588")
  9. set(RKNN_API_PATH ${CMAKE_SOURCE_DIR}/../../runtime/RK3588/${CMAKE_SYSTEM_NAME}/librknn_api)
  10. else()
  11. message(FATAL_ERROR "TARGET_SOC is not set, ref value: rk356x or rk3588")
  12. endif()
  13. if (CMAKE_SYSTEM_NAME STREQUAL "Android")
  14. set(RKNN_RT_LIB ${RKNN_API_PATH}/${CMAKE_ANDROID_ARCH_ABI}/librknnrt.so)
  15. else()
  16. if (CMAKE_C_COMPILER MATCHES "aarch64")
  17. set(LIB_ARCH aarch64)
  18. else()
  19. set(LIB_ARCH armhf)
  20. endif()
  21. set(RKNN_RT_LIB ${RKNN_API_PATH}/${LIB_ARCH}/librknnrt.so)
  22. endif()
  23. include_directories(${RKNN_API_PATH}/include)
  24. include_directories(${CMAKE_SOURCE_DIR}/../3rdparty)
  25. # opencv
  26. if (CMAKE_SYSTEM_NAME STREQUAL "Android")
  27. set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/OpenCV-android-sdk/sdk/native/jni/abi-${CMAKE_ANDROID_ARCH_ABI})
  28. else()
  29. if(LIB_ARCH STREQUAL "armhf")
  30. set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/opencv-linux-armhf/share/OpenCV)
  31. else()
  32. set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/../3rdparty/opencv/opencv-linux-aarch64/share/OpenCV)
  33. endif()
  34. endif()
  35. find_package(OpenCV REQUIRED)
  36. set(CMAKE_INSTALL_RPATH "lib")
  37. add_executable(rknn_bisenetv2_demo
  38. src/main.cc
  39. )
  40. target_link_libraries(rknn_bisenetv2_demo
  41. ${RKNN_RT_LIB}
  42. ${OpenCV_LIBS}
  43. )
  44. # install target and libraries
  45. set(CMAKE_INSTALL_PREFIX ${CMAKE_SOURCE_DIR}/install/rknn_bisenetv2_demo_${CMAKE_SYSTEM_NAME})
  46. install(TARGETS rknn_bisenetv2_demo DESTINATION ./)
  47. install(DIRECTORY model DESTINATION ./)
  48. install(PROGRAMS ${RKNN_RT_LIB} DESTINATION lib)

3、废话不多说了,直接提供C++代码,需要注意的是代码中的图像尺寸这里写死了,根据自己需要来修改代码即可。

  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <stdlib.h>
  4. #include <queue>
  5. #include "rknn_api.h"
  6. #include "opencv2/core/core.hpp"
  7. #include "opencv2/imgproc/imgproc.hpp"
  8. #include "opencv2/highgui/highgui.hpp"
  9. #include <chrono>
  10. #include <sys/time.h>
  11. using namespace cv;
  12. using namespace std;
  13. /* 打印结构体rknn_tensor_attr所代表的张量信息,这个结构体包含了关于张量的信息;
  14. rknn_tensor_attr *attr:指向 rknn_tensor_attr 结构体的指针,这个结构体包含了关于张量的信息。
  15. %d 整数 %s 字符串 %f 浮点数
  16. */
  17. void printRKNNTensor(rknn_tensor_attr *attr) {
  18. printf("index=%d name=%s n_dims=%d dims=[%d %d %d %d] n_elems=%d size=%d "
  19. "fmt=%d type=%d qnt_type=%d fl=%d zp=%d scale=%f\n",
  20. attr->index, // 张量的索引
  21. attr->name, // 张量的名字
  22. attr->n_dims, //张量的维度数
  23. attr->dims[3], attr->dims[2],
  24. attr->dims[1], attr->dims[0], // 张量在每个维度上的大小
  25. attr->n_elems, // 张量中元素的总数
  26. attr->size, // 张量的大小
  27. 0,
  28. attr->type, // 张量的数据类型
  29. attr->qnt_type, // 张量的量化类型
  30. attr->fl, // 浮点层(与量化有关)
  31. attr->zp, //零点(与量化有关)
  32. attr->scale); //缩放值(与量化有关)
  33. }
  34. /*
  35. 这段代码是一个用于后处理的函数,主要是将一个输入数组 input0 中的数据转换成伪彩色图像,
  36. 然后将伪彩色图像与原始图像 resize_img 进行融合,
  37. 最后保存三张图像:伪彩色图像、原始图像、和融合后的图像。
  38. int:函数返回一个整数值作为结果。
  39. float *input0:指向 float 类型的数组,存储了后处理前的数据。
  40. cv::Mat resize_img:OpenCV 中的 cv::Mat 类型,代表原始图像。
  41. */
  42. int post_process_u8(float *input0,cv::Mat resize_img,int w,int h){
  43. //将 float 类型的数组转换为 int 类型的向量 vec_host_scores,并将 input0 数组中的数据逐个添加到这个向量中。
  44. std::vector<int> vec_host_scores;
  45. for(int i=0;i<w*h;i++){
  46. vec_host_scores.emplace_back(input0[i]);
  47. }
  48. /*
  49. 根据预设的 num_class 值(256),生成颜色映射表 color_map,
  50. 用于将 input0 中的整数值映射为伪彩色值。
  51. 在这里,每个整数值被视为一个类别标签,然后将其转换为对应的伪彩色值。
  52. 这个过程是通过位操作来实现的,根据 input0 中的整数值生成对应的 R、G、B 分量值。
  53. */
  54. int num_class = 256;//提取到外面 只执行一次即可,自己改吧
  55. vector<int> color_map(num_class * 3);
  56. for (int i = 0; i < num_class; i++) {
  57. int j = 0;
  58. int lab = i;
  59. while (lab) {
  60. color_map[i * 3] |= ((lab >> 0 & 1) << (7 - j));
  61. color_map[i * 3 + 1] |= (((lab >> 1) & 1) << (7 - j));
  62. color_map[i * 3 + 2] |= (((lab >> 2) & 1) << (7 - j));
  63. j += 1;
  64. lab >>= 3;
  65. }
  66. }
  67. /*
  68. 创建一个 cv::Mat 类型的 pseudo_img 对象,用于存储生成的伪彩色图像。
  69. 在这里,该图像的尺寸与输入的 w 和 h 相同,通道数为 3(代表 RGB 颜色通道)。
  70. 用于创建大小为 w x h 的 cv::Mat 对象 pseudo_img 并将所有像素设置为黑色。
  71. */
  72. cv::Mat pseudo_img(w, h, CV_8UC3, cv::Scalar(0, 0, 0));
  73. for (int r = 0; r < w; r++) {
  74. for (int c = 0; c < h; c++) {
  75. int idx = vec_host_scores[r*h + c];
  76. pseudo_img.at<Vec3b>(r, c)[0] = color_map[idx * 3];
  77. pseudo_img.at<Vec3b>(r, c)[1] = color_map[idx * 3 + 1];
  78. pseudo_img.at<Vec3b>(r, c)[2] = color_map[idx * 3 + 2];
  79. }
  80. }
  81. cv::Mat result;
  82. cv::Mat resize_result;
  83. cv::addWeighted(resize_img, 0.4, pseudo_img, 0.6, 0, result, 0);
  84. cv::resize(result, resize_result, cv::Size(640, 480));
  85. // cv::imshow("pseudo_img", pseudo_img);
  86. cv::imwrite("pseudo_img.jpg", pseudo_img);
  87. // cv::imshow("bgr", resize_img);
  88. cv::imwrite("resize_img.jpg", resize_img);
  89. // cv::imshow("result", result);
  90. cv::imwrite("result.jpg", resize_result);
  91. // cv::waitKey(0);
  92. return 0;
  93. }
  94. double __get_us(struct timeval t) { return (t.tv_sec * 1000000 + t.tv_usec); }
  95. int main(int argc, char **argv) {
  96. const char *img_path = argv[2];
  97. const char *model_path = argv[1];
  98. const char *post_process_type = "fp";//fp
  99. struct timeval start_time, stop_time;
  100. // const int target_width = 960;
  101. // const int target_height = 720;
  102. if (argc != 3) {
  103. printf("Usage: %s <rknn model> <image_path> \n", argv[0]);
  104. return -1;
  105. }
  106. // Load image
  107. cv::Mat bgr = cv::imread(img_path);
  108. if (!bgr.data) {
  109. printf("cv::imread %s fail!\n", img_path);
  110. return -1;
  111. }
  112. cv::Mat rgb;
  113. //BGR->RGB
  114. cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
  115. //调整rgb图像的大小
  116. cv::Mat img_resize;
  117. cv::resize(rgb,img_resize,cv::Size(320,320));
  118. int width=img_resize.cols; //获取原始bgr图像的大小
  119. int height=img_resize.rows;
  120. // Load model
  121. FILE *fp = fopen(model_path, "rb");
  122. if (fp == NULL) {
  123. printf("fopen %s fail!\n", model_path);
  124. return -1;
  125. }
  126. fseek(fp, 0, SEEK_END); // 将文件指针移动到文件的末尾
  127. int model_len = ftell(fp); // 然后使用 ftell(fp) 获取当前文件指针的位置,即文件的大小
  128. void *model = malloc(model_len); //分配大小为 model_len 字节的内存块,并将内存块的起始地址保存在指针变量 model 中
  129. fseek(fp, 0, SEEK_SET); //是将文件指针重新设置到文件的开头,以便后续读取文件数据或执行其他操作。
  130. if (model_len != fread(model, 1, model_len, fp)) { //model用于存储从文件中读取的数据,model_len这个参数指定要读取的数据的总字节数
  131. printf("fread %s fail!\n", model_path);
  132. free(model);
  133. return -1;
  134. }
  135. /*
  136. 定义了一个 rknn_context 类型的变量 ctx,并初始化为0。
  137. rknn_context 是 RKNN 提供的一个上下文对象,用于执行模型推理的各种操作。
  138. 通过将其初始化为0,表示暂时没有创建 RKNN 上下文。
  139. */
  140. rknn_context ctx = 0;
  141. /*
  142. rknn_init 函数用于创建 RKNN 上下文,并将模型数据加载到上下文中。它的参数如下:
  143. ctx: 这是一个指向 rknn_context 的指针的地址,通过传递指针的地址,函数可以在内部分配内存并创建一个新的 RKNN 上下文,
  144. 并将其地址存储在 ctx 变量中;
  145. model:这是之前通过 fread 从模型文件中读取的模型数据的指针。它包含了要加载到 RKNN 上下文的模型数据;
  146. rknn_init 函数执行成功后,会返回一个非负值,表示初始化成功,并将 RKNN 上下文的地址存储在 ctx 变量中。
  147. 如果初始化失败,返回值将是一个负数,表示初始化失败的错误码。
  148. */
  149. int ret = rknn_init(&ctx, model, model_len, RKNN_FLAG_COLLECT_PERF_MASK, NULL);
  150. if (ret < 0) {
  151. printf("rknn_init fail! ret=%d\n", ret);
  152. return -1;
  153. }
  154. /* Query sdk version
  155. 查询 Rockchip Neural Network Toolkit(RKNN)的 SDK 版本和驱动版本,并输出它们的信息。
  156. */
  157. rknn_sdk_version version;
  158. ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version,
  159. sizeof(rknn_sdk_version));
  160. if (ret < 0) {
  161. printf("rknn_init error ret=%d\n", ret);
  162. return -1;
  163. }
  164. printf("sdk version: %s driver version: %s\n", version.api_version,
  165. version.drv_version);
  166. /* Get input,output attr
  167. 查询模型的输入和输出数量,并将结果输出到控制台。
  168. io_num,用于存储模型的输入和输出数量信息;rknn_query 函数来查询模型的输入和输出数量
  169. */
  170. rknn_input_output_num io_num;
  171. ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
  172. if (ret < 0) {
  173. printf("rknn_init error ret=%d\n", ret);
  174. return -1;
  175. }
  176. printf("model input num: %d, output num: %d\n", io_num.n_input,
  177. io_num.n_output);
  178. /*
  179. 查询模型的输入属性,并将输入属性信息打印到控制台,memset 函数将 input_attrs 数组的内存清零,以确保所有属性初始值为0。
  180. */
  181. rknn_tensor_attr input_attrs[io_num.n_input];
  182. memset(input_attrs, 0, sizeof(input_attrs));
  183. for (int i = 0; i < io_num.n_input; i++) {
  184. input_attrs[i].index = i;
  185. ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]),
  186. sizeof(rknn_tensor_attr));
  187. if (ret < 0) {
  188. printf("rknn_init error ret=%d\n", ret);
  189. return -1;
  190. }
  191. printRKNNTensor(&(input_attrs[i]));
  192. }
  193. /*
  194. 查询模型的输出属性,并将输出属性信息打印到控制台
  195. */
  196. rknn_tensor_attr output_attrs[io_num.n_output];
  197. memset(output_attrs, 0, sizeof(output_attrs));
  198. for (int i = 0; i < io_num.n_output; i++) {
  199. output_attrs[i].index = i;
  200. ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]),
  201. sizeof(rknn_tensor_attr));
  202. printRKNNTensor(&(output_attrs[i]));
  203. }
  204. /*
  205. 确定模型的输入格式(NCHW或NHWC)以及输入的宽度、高度和通道数,并将这些信息输出到控制台。
  206. */
  207. int input_channel = 3;
  208. int input_width = 0;
  209. int input_height = 0;
  210. if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) {
  211. printf("model is NCHW input fmt\n");
  212. input_width = input_attrs[0].dims[0];
  213. input_height = input_attrs[0].dims[1];
  214. printf("input_width=%d input_height=%d\n", input_width, input_height);
  215. } else {
  216. printf("model is NHWC input fmt\n");
  217. input_width = input_attrs[0].dims[2];
  218. input_height = input_attrs[0].dims[1];
  219. printf("input_width=%d input_height=%d\n", input_width, input_height);
  220. }
  221. printf("model input height=%d, width=%d, channel=%d\n", input_height, input_width,
  222. input_channel);
  223. /* Init input tensor
  224. 准备模型推理所需的输入数据
  225. */
  226. rknn_input inputs[1]; //定义了一个 rknn_input 数组 inputs,其中包含一个元素
  227. memset(inputs, 0, sizeof(inputs)); // 使用 memset 函数将 inputs 数组的内存清零,以确保所有属性初始值为0。
  228. // 设置 inputs[0] 的属性。
  229. inputs[0].index = 0; //将 index 设置为0,表示这是模型的第一个输入。
  230. //将输入数据的指针 img_resize.data 赋值给 inputs[0].buf。这表示输入数据的实际内容存储在 img_resize.data 中,而 inputs[0].buf 指向该数据。
  231. inputs[0].buf = img_resize.data;
  232. inputs[0].type = RKNN_TENSOR_UINT8; //表示输入数据的数据类型为无符号8位整数(uint8)。
  233. // 将 inputs[0].size 设置为输入数据的大小,即输入数据的宽度 (input_width)、高度 (input_height) 和通道数 (input_channel) 的乘积。这表示输入数据的总字节数。
  234. inputs[0].size = input_width * input_height * input_channel;
  235. inputs[0].fmt = RKNN_TENSOR_NHWC; //表示输入数据的格式为 NHWC
  236. inputs[0].pass_through = 0; //表示在输入数据到达模型之前,不对输入数据进行任何处理。
  237. /* Init output tensor
  238. 用于进行模型的推理(inference)过程,将输入数据输入到模型中并获取输出结果。
  239. */
  240. rknn_output outputs[io_num.n_output]; //定义了一个 rknn_output 数组 outputs,用于存储模型的输出数据
  241. memset(outputs, 0, sizeof(outputs));
  242. //want_float 属性表示是否希望输出结果为浮点数(float)。将它设置为1表示希望输出为浮点数。这通常在需要对输出进行后处理时使用。
  243. for (int i = 0; i < io_num.n_output; i++) {
  244. outputs[i].want_float = 1;
  245. }
  246. printf("img.cols: %d, img.rows: %d\n", img_resize.cols, img_resize.rows);
  247. // auto t1=std::chrono::steady_clock::now(); //记录当前时间,用于计算推理时间
  248. //rknn_inputs_set 函数用于将输入数据绑定到 RKNN 上下文,以便进行推理。
  249. gettimeofday(&start_time, NULL);
  250. rknn_inputs_set(ctx, io_num.n_input, inputs);
  251. ret = rknn_run(ctx, NULL); //执行模型的推理过程
  252. if (ret < 0) {
  253. printf("ctx error ret=%d\n", ret);
  254. return -1;
  255. }
  256. ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL); //用于从 RKNN 上下文中获取输出数据
  257. //毫秒级
  258. // auto t2=std::chrono::steady_clock::now(); //获取当前时间
  259. // double dr_ms=std::chrono::duration<double,std::milli>(t2-t1).count(); //计算推理时间
  260. gettimeofday(&stop_time, NULL);
  261. printf("once run use %f ms\n", (__get_us(stop_time) - __get_us(start_time)) / 1000);
  262. // printf("%lf ms\n",dr_ms);
  263. if (ret < 0) {
  264. printf("outputs error ret=%d\n", ret);
  265. return -1;
  266. }
  267. // rknn_perf_detail perf_detail;
  268. // ret = rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, &perf_detail, sizeof(perf_detail));
  269. // printf("Perf detail:\n");
  270. // printf("process_detil : %s",perf_detail.perf_data);
  271. // printf(&perf_detail);
  272. /* Post process
  273. 后处理(post-process)模型输出;
  274. out_scales 和 out_zps,用于存储输出数据的缩放因子和零点偏移值
  275. */
  276. std::vector<float> out_scales;
  277. std::vector<uint8_t> out_zps;
  278. for (int i = 0; i < io_num.n_output; ++i) {
  279. out_scales.push_back(output_attrs[i].scale);
  280. out_zps.push_back(output_attrs[i].zp);
  281. }
  282. gettimeofday(&start_time, NULL);
  283. //通过比较 post_process_type 的值是否等于 "fp",来确定是否进行后处理。如果 post_process_type 是 "fp",则调用 post_process_u8 函数进行后处理。
  284. if (strcmp(post_process_type, "fp") == 0) {
  285. post_process_u8((float *) outputs[0].buf,img_resize,
  286. 320, 320);
  287. }
  288. gettimeofday(&stop_time, NULL);
  289. printf("process use %f ms\n", (__get_us(stop_time) - __get_us(start_time)) / 1000);
  290. /*
  291. 用于释放模型推理过程中获取的输出数据的内存,以便避免内存泄漏。
  292. */
  293. ret = rknn_outputs_release(ctx, io_num.n_output, outputs);
  294. if (ret < 0) {
  295. printf("rknn_query fail! ret=%d\n", ret);
  296. goto Error;
  297. }
  298. /*
  299. 错误处理部分,当在前面的代码执行过程中发生错误时,将会跳转到 Error 标签处进行错误处理。
  300. */
  301. Error:
  302. if (ctx > 0)
  303. rknn_destroy(ctx);
  304. if (model)
  305. free(model);
  306. if (fp)
  307. fclose(fp);
  308. return 0;
  309. }

4、运行sh文件,开始编译

编译成功!

三、在板端运行

将编译好的可执行文件(install下的文件)全部送入到板端任意位置,执行以下命令即可(根据自己的路经修改):

./rknn_bisenetv2_demo ./model/RK3566_RK3568/.rknn ./image

后续等加入3T的计算棒之后,速度应该会更快。

点个赞呗!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/229577
推荐阅读
相关标签
  

闽ICP备14008679号