当前位置: 首页 > news >正文

C版本的-Unet-rknn推理

1. 前言

        之前就想着使用rknn的c版本的api做推理看看,找了一个简单的,那就unet吧,本来想着用rk的demo文件,但是里面是mobilenet,相关的函数没有,卡这也卡了好久,突然发现tengine相关的后处理,拿来用用,终于调试好了!!!(环境自己配置)

2. c代码修改

2.1前处理

const char* img_path = "/home/ubuntu/npu_test/unet/img/01_test.tif";const char* roi_mask_path = "/home/ubuntu/npu_test/unet/img/01_test_mask.png";const char *model_path =  "/home/ubuntu/npu_test/unet/model/eyes_unet-sim-3588.rknn";// Load ROI maskMat roi_img = imread(roi_mask_path, IMREAD_GRAYSCALE);if (roi_img.empty()) {cout << "Image not found: " << roi_mask_path << endl;return -1;}// Load imageMat original_img = imread(img_path);if (original_img.empty()) {cout << "Image not found: " << img_path << endl;return -1;}// Convert image to RGBcvtColor(original_img, original_img, COLOR_BGR2RGB);// Expand batch dimension// Mat img = original_img.reshape(1, 1);Mat img = original_img;

2.2 rknn的模型加载

const int MODEL_IN_WIDTH = 565;const int MODEL_IN_HEIGHT = 584;const int MODEL_IN_CHANNELS = 3;rknn_context ctx = 0;int ret;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;}// ======================= 获取模型输入输出信息 ===================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;}// ======================= 设置模型输入 ===================// 使用rknn_input结构体存储模型输入信息, 表示模型的一个数据输入,用来作为参数传入给 rknn_inputs_set 函数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;                                        // 设置模型输入格式:NHWCinputs[0].buf = img.data;                                                // 设置模型输入数据// 使用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;}// ======================= 推理 ===================printf("rknn_run\n");ret = rknn_run(ctx, nullptr);if (ret < 0){printf("rknn_run fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输出 ===================// 使用rknn_output结构体存储模型输出信息rknn_output outputs[1];memset(outputs, 0, sizeof(outputs));outputs[0].want_float = 1;// 使用rknn_outputs_get函数获取模型输出ret = rknn_outputs_get(ctx, 1, outputs, NULL);if (ret < 0){printf("rknn_outputs_get fail! ret=%d\n", ret);return -1;}

2.3 后处理

float *output_data = (float *)outputs[0].buf;int output_size = outputs[0].size / sizeof(uint32_t);// cout << "channel: " << channel << endl;// cout << "res: " << res << endl;// cout << "size: " << output_size << endl;int img_h = 584;int img_w = 565;int channel = output_size / img_h / img_w;int res = output_size % (img_h * img_w);cout << "channel: " << channel << endl;cout << "res: " << res << endl;int* label_data = new int[img_h * img_w];if (res != 0){fprintf(stderr, "output shape is not supported.\n");}else{/* multi-class segmentation */for (int i = 0; i < img_h; ++i){for (int j = 0; j < img_w; ++j){int argmax_id = -1;float max_conf = std::numeric_limits<float>::min();for (int k = 0; k < channel; ++k){float out_value = output_data[k * img_w * img_h + i * img_w + j];if (out_value > max_conf){argmax_id = k;max_conf = out_value;}}label_data[i * img_w + j] = argmax_id;if (label_data[i * img_w + j] == 1) {label_data[i * img_w + j] = 255;}}}}// 将图像数据存储到一维数组中int* roi_array = new int[img_h * img_w];for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {roi_array[i * img_w + j] = static_cast<int>(roi_img.at<uchar>(i, j));}}for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {if (roi_array[i * img_w + j] == 0) {label_data[i * img_w + j] = roi_array[i * img_w + j];}}}Mat result_img(img_h, img_w, CV_8UC1);for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {result_img.at<uchar>(i, j) = static_cast<uchar>(label_data[i * img_w + j]);}}imwrite("result.png", result_img);

2.4 rknn资源释放

// Release resourcesrknn_outputs_release(ctx, 1, outputs);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);}

 2.5 完整代码

#include <iostream>
#include <opencv2/core/hal/interface.h>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
#include "rknn_api.h"
#include <chrono>using namespace std;
using namespace cv;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() {auto start = std::chrono::high_resolution_clock::now();const char* img_path = "/home/ubuntu/npu_test/unet/img/01_test.tif";const char* roi_mask_path = "/home/ubuntu/npu_test/unet/img/01_test_mask.png";const char *model_path =  "/home/ubuntu/npu_test/unet/model/eyes_unet-sim-3588.rknn";// Load ROI maskMat roi_img = imread(roi_mask_path, IMREAD_GRAYSCALE);if (roi_img.empty()) {cout << "Image not found: " << roi_mask_path << endl;return -1;}// Load imageMat original_img = imread(img_path);if (original_img.empty()) {cout << "Image not found: " << img_path << endl;return -1;}// Convert image to RGBcvtColor(original_img, original_img, COLOR_BGR2RGB);// Expand batch dimension// Mat img = original_img.reshape(1, 1);Mat img = original_img;const int MODEL_IN_WIDTH = 565;const int MODEL_IN_HEIGHT = 584;const int MODEL_IN_CHANNELS = 3;rknn_context ctx = 0;int ret;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;}// ======================= 获取模型输入输出信息 ===================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;}// ======================= 设置模型输入 ===================// 使用rknn_input结构体存储模型输入信息, 表示模型的一个数据输入,用来作为参数传入给 rknn_inputs_set 函数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;                                        // 设置模型输入格式:NHWCinputs[0].buf = img.data;                                                // 设置模型输入数据// 使用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;}// ======================= 推理 ===================printf("rknn_run\n");ret = rknn_run(ctx, nullptr);if (ret < 0){printf("rknn_run fail! ret=%d\n", ret);return -1;}// ======================= 获取模型输出 ===================// 使用rknn_output结构体存储模型输出信息rknn_output outputs[1];memset(outputs, 0, sizeof(outputs));outputs[0].want_float = 1;// 使用rknn_outputs_get函数获取模型输出ret = rknn_outputs_get(ctx, 1, outputs, NULL);if (ret < 0){printf("rknn_outputs_get fail! ret=%d\n", ret);return -1;}float *output_data = (float *)outputs[0].buf;int output_size = outputs[0].size / sizeof(uint32_t);// cout << "channel: " << channel << endl;// cout << "res: " << res << endl;// cout << "size: " << output_size << endl;int img_h = 584;int img_w = 565;int channel = output_size / img_h / img_w;int res = output_size % (img_h * img_w);cout << "channel: " << channel << endl;cout << "res: " << res << endl;int* label_data = new int[img_h * img_w];if (res != 0){fprintf(stderr, "output shape is not supported.\n");}else{/* multi-class segmentation */for (int i = 0; i < img_h; ++i){for (int j = 0; j < img_w; ++j){int argmax_id = -1;float max_conf = std::numeric_limits<float>::min();for (int k = 0; k < channel; ++k){float out_value = output_data[k * img_w * img_h + i * img_w + j];if (out_value > max_conf){argmax_id = k;max_conf = out_value;}}label_data[i * img_w + j] = argmax_id;if (label_data[i * img_w + j] == 1) {label_data[i * img_w + j] = 255;}}}}// 将图像数据存储到一维数组中int* roi_array = new int[img_h * img_w];for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {roi_array[i * img_w + j] = static_cast<int>(roi_img.at<uchar>(i, j));}}for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {if (roi_array[i * img_w + j] == 0) {label_data[i * img_w + j] = roi_array[i * img_w + j];}}}Mat result_img(img_h, img_w, CV_8UC1);for (int i = 0; i < img_h; ++i) {for (int j = 0; j < img_w; ++j) {result_img.at<uchar>(i, j) = static_cast<uchar>(label_data[i * img_w + j]);}}imwrite("result.png", result_img);// Release resourcesrknn_outputs_release(ctx, 1, outputs);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);}auto end = std::chrono::high_resolution_clock::now();auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);std::cout << "Execution time: " << duration.count() << " milliseconds" << std::endl;return 0;
}

        感觉代码还是不优美,很多的for循环看着难受,但是已经实现了,后续再修改吧

3. 结果展示

C++的测试结果

time: 497 ms

python的测试结果

time:660ms

相关文章:

C版本的-Unet-rknn推理

1. 前言 之前就想着使用rknn的c版本的api做推理看看&#xff0c;找了一个简单的&#xff0c;那就unet吧&#xff0c;本来想着用rk的demo文件&#xff0c;但是里面是mobilenet&#xff0c;相关的函数没有&#xff0c;卡这也卡了好久&#xff0c;突然发现tengine相关的后处理&…...

Transformer的前世今生 day04(ELMO、Attention注意力机制)

ELMO 前情回顾 NNLM模型&#xff1a;主要任务是在预测下一个词&#xff0c;副产品是词向量Word2Vec模型&#xff1a;主要任务是生成词向量 CBOW&#xff1a;训练目标是根据上下文预测目标词Skip-gram&#xff1a;训练目标是根据目标词预测上下文词 ELMO模型的流程 针对Wor…...

稀碎从零算法笔记Day19-LeetCode:相交链表

题型&#xff1a;链表基本操作 链接&#xff1a;160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&…...

AI系统性学习03—ChatGPT开发教程

文章目录 1、OpenAI关键概念⭐️2、OpenAI SDK介绍3、OpenAI API KEY&API 认证3.1 REST API安全认证 4、OpenAI模型⭐️4.1 模型分类4.2 GPT44.3 GPT-3.54.4 Embeddings 5、OpenAI快速入门6、Function calling(函数调用)⭐️⭐️⭐️6.1 应用场景6.2 支持function calling的…...

每日一练 | 华为认证真题练习Day201

1、BGP Notification报文Error Code为2时表示open消息错误&#xff0c;其中包含如下哪些错误子码?&#xff08;多选&#xff09; A. 1-不支持的版本号 B. 2-错误的对等体AS号 C. 2-错误的对等体AS号 D. 4-错误的属性列表 2、A greate命令&#xff08;aggregate ipy4-addre…...

nginx日志统计qps

1.QPS QPS全称为Queries Per Second&#xff0c;即每秒钟处理的请求数量。对于一个高并发应用来说&#xff0c;QPS是非常重要的性能指标&#xff0c;它反映了应用处理请求的能力。在实际应用中&#xff0c;QPS的大小取决于应用的负载和应用本身的性能。 QPS req/sec 请求数/…...

9.登入页面

登入页面 在pages中新建页面login 修改代码 <template><view></view> </template><script setup></script><style lang"scss"></style>添加头像组件 官网 https://vkuviewdoc.fsq.pub/components/avatar.html …...

js封装SDK 在VUE、小程序、公众号直接调用js调用后端接口(本文以vue项目为例)

1.封装一个js文件msgSdk.js 注意&#xff1a;需要修改这个请求地址 apiServiceAddress ;(function () {if (window.msgSdk) {return}var msgSdk (function () {var m_msgSdk thisvar apiServiceAddress"http://172.12.14.5:8000"this.I_SendHTTPRequest functi…...

ideaSSM社区二手交易平台C2C模式开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 idea ssm 社区二手交易平台系统是一套完善的完整信息管理系统&#xff0c;结合SSM框架完成本系统SpringMVC spring mybatis &#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码…...

利用子类化技术拦截win32窗口各种消息(包括但不限于鼠标键盘消息)

创建子类化函数&#xff1a; 首先&#xff0c;您需要编写一个子类化函数&#xff0c;该函数将用于处理编辑框的消息。这个函数通常会拦截并处理您感兴趣的消息&#xff0c;比如鼠标消息。 子类化编辑框&#xff1a; 在窗口程序中找到编辑框的句柄&#xff08;HWND&#xff09;…...

HCIP—OSPF课后练习一

本实验模拟了一个企业网络场景&#xff0c;R1、R2、R3为公司总部网络的路由器&#xff0c;R4、R5分别为企业分支机构1和分支机构2的路由器&#xff0c;并且都采用双上行方式与企业总部相连。整个网络都运行OSPF协议&#xff0c;R1、R2、R3之间的链路位于区域0&#xff0c;R4与R…...

Android 13.0 kenel和frameworks中修改ram运行内存的功能实现

1.前言 在13.0的系统rom产品开发定制中,在对一些产品开发中的配置需求方面,在产品后续订单中,产品提出要提高硬件配置,但是硬件方面已经定板,项目时间比较仓促,所以 来不及对硬件重新定制,就需要软件方面在ram运行内存的容量大小方面作假,修改ram真实的大小容量,所以…...

如何将应用程序发布到 App Store

憧憬blog主页 在强者的眼中&#xff0c;没有最好&#xff0c;只有更好。我们是移动开发领域的优质创作者&#xff0c;同时也是阿里云专家博主。 ✨ 关注我们的主页&#xff0c;探索iOS开发的无限可能&#xff01; &#x1f525;我们与您分享最新的技术洞察和实战经验&#xff0…...

Python进程与线程开发

目录 multiprocessing模块 线程的开发 threading模块 setDaemon 死锁 线程间的通信 multiprocessing模块 运行python的时候&#xff0c;我们都是在创建并运行一个进程&#xff0c;(linux中一个进程可以fork一个子进程&#xff0c;并让这个子进程exec另外一个程序)。在pyt…...

【3DsMax】UVW展开——以制作牙膏盒为例

效果 步骤 1. 从网上下载牙膏盒贴图&#xff0c;我下载的贴图地址为&#xff08;牙膏盒贴图链接&#xff09; 2. 打开3DsMax&#xff0c;创建一个长方体&#xff0c;设置长宽高分别为180、45、40毫米 打开材质编辑器&#xff0c;点击漫反射后的按钮 双击“位图” 将材质赋予长…...

Mysql数据库概念与安装

目录 一、数据库概述 1、数据库的基本概念 2、数据库管理系统&#xff08;DBMS&#xff09; 2.1 数据库管理系统概念 2.2 数据库管理系统工作模式 3、数据库系统&#xff08;DBS&#xff09; 3.1 数据库系统概念 3.2 数据库系统发展史 4、关系型数据库与非关系型数据库…...

【Java - 框架 - SpringMVC】(01) SpringMVC框架的简单创建与使用,快速上手

"SpringMVC"框架的简单创建与使用&#xff0c;快速上手&#xff1b; 环境 Java版本"1.8.0_202"&#xff1b;Spring Boot版本"2.5.9"&#xff1b;Windows 11 专业版_22621.2428&#xff1b;IntelliJ IDEA 2021.1.3(Ultimate Edition)&#xff1…...

框架篇常见面试题

1、Spring框架的单例bean是线程安全的吗&#xff1f; 2、什么是AOP&#xff1f; 3、Spring的事务是如何实现的&#xff1f; 4、Spring事务失效的场景 5、SpringBean的声明周期 6、Spring的循环依赖 7、SpringMVC的执行流程 8、SpringBoot自动配置原理 9、Spring常见注解 10、My…...

【刷题】滑动窗口入门

送给大家一句话&#xff1a; 那脑袋里的智慧&#xff0c;就像打火石里的火花一样&#xff0c;不去打它是不肯出来的。——莎士比亚 滑动窗口入门 认识滑动窗口Leetcode 209. 长度最小的子数组题目描述算法思路 Leetcode 3. 无重复字符的最长子串题目描述算法思路 Leetcode 1004…...

【Python 48小时速成 3】输入与输出

在 Python 中&#xff0c;输入和输出通常通过内置函数来实现。主要的输入函数是 input()&#xff0c;用于从用户获取输入&#xff0c;而输出函数则是 print()&#xff0c;用于将结果打印到控制台。以下是简单的代码示例演示了输入和输出&#xff1a; # 输入示例 name input(&…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...