RKNN_C++版本-YOLOV5

news2025/1/26 4:45:36

1.背景

        为了实现低延时,所以开始看看C版本的rknn的使用,确实有不足的地方,请指正(代码借鉴了rk官方的仓库文件)。

2.基本的操作流程

1.读取模型初始化

    // ======================= 设置基本信息 ===================
    // 在postprocess.h文件中定义,详见include/postprocess.h文件
    const float nms_threshold = NMS_THRESH;      // 默认的NMS阈值
    const float box_conf_threshold = BOX_THRESH; // 默认的置信度阈值
    // 默认的模型输入输出信息letterbox,默认使用LetterBox的预处理
    std::string option = "letterbox";
    // 默认的图片输出路径,根据路径设置
    std::string out_path = "/home/ubuntu/unet/unet_rknn_c++/rknn_api_test/result/out.png";
    // 默认的模型路径,根据路径设置
    const char model_path[] = "../weights/yolov5s-640-640.rknn";
    // ======================= 设置rknn模型的基本信息 ===================
    //初始化rknn_context对象,数据类型:rknn_context,成员变量:ctx
    rknn_context ctx = 0;
    int ret;
    // RKNN模型的二进制数据或者RKNN模型路径。当参数size大于0时,model表示二进制数据;当参数size等于0时,model表示RKNN模型路径。
    int model_len = 0;
    unsigned char *model;

    // ======================= 初始化RKNN模型 ===================
    model = load_model(model_path, &model_len);
    ret = rknn_init(&ctx, model, model_len, 0, NULL);
    if (ret < 0)
    {
        printf("rknn_init fail! ret=%d\n", ret);
        return -1;
    }
    else{
        printf("model load success\n");
    }
    if (ctx == 0)
    {
        printf("rknn_init fail! ret=%d\n", ret);
        return -1;
    }

    rknn_core_mask core_mask = RKNN_NPU_CORE_0_1_2;
    ret = rknn_set_core_mask(ctx, core_mask);

 2.获取输入和输出的相关信息

// ======================= SDK的版本信息 ===================
    rknn_sdk_version version;
    ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, sizeof(rknn_sdk_version));
    if (ret < 0)
    {
        printf("rknn_init error ret=%d\n", ret);
        return -1;
    }
    printf("sdk version: %s driver version: %s\n", version.api_version, version.drv_version);

    // ======================= 获取模型输入输出信息 ===================
    /*调佣rknn_query接口查询tensor输入输出个数*/
    rknn_input_output_num io_num;
    ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
    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表示模型的tensor的属性 ===================
    // (1)input的tensor信息
    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 < 0)
        {
            printf("rknn_init error ret=%d\n", ret);
            return -1;
        }
        dump_tensor_attr(&(input_attrs[i]));
    }
    //(2)output的tensor信息
    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));
        dump_tensor_attr(&(output_attrs[i]));
    }

3.设置输入格式

// 查看模型的输入格式NCHW或者NHWC,获取输入的宽高和通道数
    int channel = 3;
    int width = 0;
    int height = 0;
    if (input_attrs[0].fmt == RKNN_TENSOR_NCHW)
    {
        printf("model is NCHW input fmt\n");
        channel = input_attrs[0].dims[1];
        height = input_attrs[0].dims[2];
        width = input_attrs[0].dims[3];
    }
    else
    {
        printf("model is NHWC input fmt\n");
        height = input_attrs[0].dims[1];
        width = input_attrs[0].dims[2];
        channel = input_attrs[0].dims[3];
    }

    printf("model input height=%d, width=%d, channel=%d\n", height, width, channel);
    // ======================= 设置模型输入 ===================
    // rknn_input 结构体,用于描述输入数据,包括索引、类型、大小、格式等。1表示输入的数量,可换成io_num.n_input
    rknn_input inputs[1];
    memset(inputs, 0, sizeof(inputs));
    // 该输入的索引位置
    inputs[0].index = 0;
    // 输入数据的类型
    inputs[0].type = RKNN_TENSOR_UINT8;
    // 输入数据所占内存大小
    inputs[0].size = width * height * channel;
    // 输入数据的格式
    inputs[0].fmt = RKNN_TENSOR_NHWC;
    // 输入数据是否透传
    inputs[0].pass_through = 0;

4.读取图片进行预处理

    string input_path= "../images/bus.jpg";
    // ======================= 读取图片 ===================
    printf("Read %s ...\n", input_path.c_str());
    cv::Mat orig_img = cv::imread(input_path, 1);
    if (!orig_img.data)
    {
        printf("cv::imread %s fail!\n", input_path.c_str());
        return -1;
    }
    cv::Mat img;
    cv::cvtColor(orig_img, img, cv::COLOR_BGR2RGB);
    int img_width = img.cols;
    int img_height = img.rows;
    printf("img width = %d, img height = %d\n", img_width, img_height);


    // 这里去除了rga的操作,
    // 指定目标大小和预处理方式,默认使用LetterBox的预处理
    BOX_RECT pads;
    memset(&pads, 0, sizeof(BOX_RECT));
    cv::Size target_size(width, height);
    cv::Mat resized_img(target_size.height, target_size.width, CV_8UC3);
    // 计算缩放比例
    float scale_w = (float)target_size.width / img.cols;
    float scale_h = (float)target_size.height / img.rows;

    if (img_width != width || img_height != height) {
        if (option == "letterbox") {
            printf("resize image with letterbox\n");
            float min_scale = std::min(scale_w, scale_h);
            scale_w = min_scale;
            scale_h = min_scale;
            letterbox(img, resized_img, pads, min_scale, target_size);
            // 保存预处理图片
            cv::imwrite("letterbox_input.jpg", resized_img);
        } else {
            fprintf(stderr, "Invalid resize option. Use 'resize' or 'letterbox'.\n");
            return -1;
        }
        inputs[0].buf = resized_img.data;
    }
    else{
        inputs[0].buf = img.data;
    }

5.模型输入和推理

    // 使用rknn_inputs_set函数设置模型输入
    ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
    if (ret < 0)
    {
        printf("rknn_input_set fail! ret=%d\n", ret);
        return -1;
    }

    // ======================= rknn模型推理 ===================

    printf("rknn_run\n");
    ret = rknn_run(ctx, nullptr);
    if (ret < 0)
    {
        printf("rknn_run fail! ret=%d\n", ret);
        return -1;
    }

6.获取输出

    // 多输出
    rknn_output outputs[io_num.n_output];
    memset(outputs, 0, sizeof(outputs));
    // 为每个输出设置属性,这里假设我们希望所有输出都转换为浮点数
    for (int i = 0; i < 3; ++i) {
        // want_float标识是否需要将输出数据转为float类型输出,0表示不需要,1表示需要
        outputs[i].want_float = 0;
    }
    // 使用rknn_outputs_get函数获取模型输出
    ret = rknn_outputs_get(ctx, 3, outputs, NULL);
    if (ret < 0) {
        printf("rknn_outputs_get fail! ret=%d\n", ret);
        return -1;
    }
    /***
     int8数据格式(int占用1字节),查看模型输出的shape: (1*255*80*80),(1*255*40*40),(1*255*40*40)
        output[0] shape: 1632000
        output[1] shape: 408000
        output[2] shape: 102000
     float数据格式(float占用4字节),查看模型输出的shape: (1*255*80*80),(1*255*40*40),(1*255*40*40)
        output[0] shape: 1632000*4
        output[1] shape: 408000*4
        output[2] shape: 102000*4
     ***/
//    int8_t *pblob[3];
//    for (int i = 0; i < io_num.n_output; ++i)
//    {
//        cout << "output[" << i << "] shape: " << outputs[i].size << endl;
//        pblob[i] = (int8_t*)outputs[i].buf;
//    }

7.后处理

    // 后处理
    detect_result_group_t detect_result_group;
    std::vector<float> out_scales;
    std::vector<int32_t> out_zps;
    // rknn量化的零点和缩放因子
    for (int i = 0; i < io_num.n_output; ++i)
    {
        out_scales.push_back(output_attrs[i].scale);
        out_zps.push_back(output_attrs[i].zp);
    }
    // 后处理
    post_process((int8_t *)outputs[0].buf, (int8_t *)outputs[1].buf, (int8_t *)outputs[2].buf, height, width,
                 box_conf_threshold, nms_threshold, pads, scale_w, scale_h, out_zps, out_scales, &detect_result_group);
    // 画框和概率
    char text[256];
    for (int i = 0; i < detect_result_group.count; i++)
    {
        detect_result_t *det_result = &(detect_result_group.results[i]);
        sprintf(text, "%s %.1f%%", det_result->name, det_result->prop * 100);
        printf("%s @ (%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top,
               det_result->box.right, det_result->box.bottom, det_result->prop);
        int x1 = det_result->box.left;
        int y1 = det_result->box.top;
        int x2 = det_result->box.right;
        int y2 = det_result->box.bottom;
        rectangle(orig_img, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(256, 0, 0, 256), 3);
        putText(orig_img, text, cv::Point(x1, y1 + 12), cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(255, 255, 255));
    }
    imwrite(out_path, orig_img);
    printf("save detect result to %s\n", out_path.c_str());

8.资源释放

    // 释放rknn_outputs_get函数得到的输出的相关资源
    ret = rknn_outputs_release(ctx, io_num.n_output, outputs);
    // 释放传入的rknn_context及其相关资源
    ret = rknn_destroy(ctx);
    if (model)
    {
        free(model);
    }

3.后记

详细的内容我已经上传到yolov5-rk文件中,可以详细的研究

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

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

相关文章

H3C-防火墙IPSec配置案例(主模式)

目录 1.IPSec简述:2.IPSec应用场景:3.网络拓扑及说明:4.案例背景:5.网络配置:5.1 基础网络配置:5.1.1 总部防火墙基础配置:5.1.2 分部防火墙基础配置:5.1.3 互联网路由器基础配置:5.1.4 总部服务器基础配置:5.1.5 总部PC基础配置: 5.2 IPSec配置:5.2.1 总部防火墙IPSec配置:5.2…

windows下本地部署安装hadoop+scala+spark-【不需要虚拟机】

注意版本依赖【本实验版本如下】 Hadoop 3.1.1 spark 2.3.2 scala 2.11 1.依赖环境 1.1 java 安装java并配置环境变量【如果未安装搜索其他教程】 环境验证如下&#xff1a; C:\Users\wangning>java -version java version "1.8.0_261" Java(TM) SE Runti…

vim如何显示行号

:set nu 显示行号 :set nonu 不显示行号 &#xff08;vim如何使设置显示行号永久生效&#xff1a;vim如何使相关设置永久生效-CSDN博客&#xff09;

国产编辑器EverEdit - 命令窗口应用详解

1 命令窗口应用详解 1.1 应用场景 有时需要在EverEdit中执行一些命令行工具&#xff0c;甚至想把当前文档做为参数&#xff0c;传递给命令进行一些文本分析&#xff0c;比如&#xff1a;一些常用的文本处理工具&#xff0c;gawk.exe等。 1.2 使用方法 命令窗口的使用在官方手…

Linux C\C++编程-文件位置指针与读写文件数据块

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客 《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 Linu…

vue2使用flv.js在浏览器打开flv格式视频

组件地址&#xff1a;GitHub - bilibili/flv.js: HTML5 FLV Player flv.js 仅支持 H.264 和 AAC/MP3 编码的 FLV 文件。如果视频文件使用了其他编码格式就打不开。 flv.vue <template><div><el-dialog :visible.sync"innerVisibleFlv" :close-on-pre…

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到 这次在windows的WSL2中遇到了一个非常奇怪的错误&#xff0c;就是 CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):Could NOT find BLAS (missing: BLAS_LIBRAR…

仿 RabbitMQ 的消息队列3(实战项目)

七. 消息存储设计 上一篇博客已经将消息统计文件的读写代码实现了&#xff0c;下一步我们将实现创建队列文件和目录。 实现创建队列文件和目录 初始化 0\t0 这样的初始值. //创建队列对应的文件和目录&#xff1a;public void createQueueFile(String queueName) throws IO…

多线程杂谈:惊群现象、CAS、安全的单例

引言 本文是一篇杂谈&#xff0c;帮助大家了解多线程可能会出现的面试题。 目录 引言 惊群现象 结合条件变量 CAS原子操作&#xff08;cmp & swap&#xff09; 线程控制&#xff1a;两个线程交替打印奇偶数 智能指针线程安全 单例模式线程安全 最简单的单例&…

腾讯 Hunyuan3D-2: 高分辨率3D 资产生成

腾讯 Hunyuan3D-2&#xff1a;高分辨率 3D 资产生成的突破 前言 在当今数字化时代&#xff0c;3D 资产生成技术正变得越来越重要。无论是游戏开发、影视制作还是虚拟现实领域&#xff0c;高质量的 3D 模型和纹理都是创造沉浸式体验的关键。然而&#xff0c;传统的 3D 资产制作…

R语言学习笔记之开发环境配置

一、概要 整个安装过程及遇到的问题记录 操作步骤备注&#xff08;包含遇到的问题&#xff09;1下载安装R语言2下载安装RStudio3离线安装pacman提示需要安装Rtools4安装Rtoolspacman、tidyfst均离线安装完成5加载tidyfst报错 提示需要安装依赖&#xff0c;试错逐步下载并安装…

DRG/DIP 2.0时代下基于PostgreSQL的成本管理实践与探索(上)

一、引言 1.1 研究背景与意义 在医疗领域的改革进程中&#xff0c; DRG/DIP 2.0 时代&#xff0c;医院成本管理的重要性愈发凸显。新的医保支付方式下&#xff0c;医院的收入不再单纯取决于医疗服务项目的数量&#xff0c;而是与病种的分组、费用标准以及成本控制紧密相关。这…

【数据结构】_顺序表

目录 1. 概念与结构 1.1 静态顺序表 1.2 动态顺序表 2. 动态顺序表实现 2.1 SeqList.h 2.2 SeqList.c 2.3 Test_SeqList.c 3. 顺序表性能分析 线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串等&#xff1b…

缓存之美:万文详解 Caffeine 实现原理(下)

上篇文章&#xff1a;缓存之美&#xff1a;万文详解 Caffeine 实现原理&#xff08;上&#xff09; getIfPresent 现在我们对 put 方法有了基本了解&#xff0c;现在我们继续深入 getIfPresent 方法&#xff1a; public class TestReadSourceCode {Testpublic void doRead() …

VSCode下EIDE插件开发STM32

VSCode下STM32开发环境搭建 本STM32教程使用vscode的EIDE插件的开发环境&#xff0c;完全免费&#xff0c;有管理代码文件的界面&#xff0c;不需要其它IDE。 视频教程见本人的 VSCodeEIDE开发STM32 安装EIDE插件 Embedded IDE 嵌入式IDE 这个插件可以帮我们管理代码文件&am…

HTTP 配置与应用(局域网)

想做一个自己学习的有关的csdn账号&#xff0c;努力奋斗......会更新我计算机网络实验课程的所有内容&#xff0c;还有其他的学习知识^_^&#xff0c;为自己巩固一下所学知识&#xff0c;下次更新HTTP 配置与应用&#xff08;不同网段&#xff09;。 我是一个萌新小白&#xf…

LiteFlow Spring boot使用方式

文章目录 概述LiteFlow框架的优势规则调用逻辑规则组件定义组件内数据获取通过 DefaultContext自定义上下文 通过 组件规则定义数据通过预先传入数据 liteflow 使用 概述 在每个公司的系统中&#xff0c;总有一些拥有复杂业务逻辑的系统&#xff0c;这些系统承载着核心业务逻…

mysql学习笔记-数据库的设计规范

1、范式简介 在关系型数据库中&#xff0c;关于数据表设计的基本原则、规则就称为范式。 1.1键和相关属性的概念 超键:能唯一标识元组的属性集叫做超键。 候选键:如果超键不包括多余的属性&#xff0c;那么这个超键就是候选键 主键:用户可以从候选键中选择一个作为主键。 外…

计算机网络 (55)流失存储音频/视频

一、定义与特点 定义&#xff1a;流式存储音频/视频是指经过压缩并存储在服务器上的多媒体文件&#xff0c;客户端可以通过互联网边下载边播放这些文件&#xff0c;也称为音频/视频点播。 特点&#xff1a; 边下载边播放&#xff1a;用户无需等待整个文件下载完成即可开始播放…

60,【1】BUUCF web [RCTF2015]EasySQL1

先查看源码 1&#xff0c;changepwd&#xff08;修改密码&#xff09; <?php // 开启会话&#xff0c;以便使用会话变量 session_start();// 设置页面的内容类型为 HTML 并使用 UTF-8 编码 header("Content-Type: text/html; charsetUTF-8");// 引入配置文件&…