onnxruntime模型部署(二)C++部署手写数字识别

news2025/1/19 16:21:19

导出onnx模型

模型链接:
夸克网盘链接
百度网盘链接,提取码:8fkb
在这里插入图片描述

环境配置

OpenCV配置

自行百度

onnxruntime C++版配置

有两种方法,一种是下载源码自己编译,还有一种是使用预编译好的文件。众说周知,编译总是一件令人头痛的事情,所以我建议,使用预编译好的。
step1:打开github项目的release页面,按照自己的电脑架构和cuda版本,选择合适的版本下载
step2:解压缩

tar -xvf onnxruntime-xxx-xxx-xxx.tgz

step3:将解压好的文件夹mv到一个合适的位置保存,例如

mv ./onnxruntime-xxx-xxx-xxx /opt/onnxruntime
# opt文件夹是用于存放用户安装的应用的,
# 也可以放到/usr/local/文件夹下

step3:在/usr/local/include和/usr/local/lib文件夹下建立软链接

sudo ln -sf your_onnxruntime_path/include /usr/local/include/onnxruntime
sudo ln -sf your_onnxruntime_path/lib /usr/local/lib/onnxruntime

step4:在/etc/ld.so.conf.d/下创建链接配置文件onnxruntime.conf(步骤可选)

sudo echo "your_onnxruntime_path" > onnxruntime.conf
ldconfig

大功告成!

C++推理

#include<iostream>
#include<onnxruntime/onnxruntime_cxx_api.h>
#include<array>
#include<cmath>
#include<algorithm>
#include<opencv2/opencv.hpp>

#pragma comment(lib, "onnxruntime.lib")
//用于配置链接器参数,但是我试了一下好像没有用,不知道为什么。

//实现softmax激活函数
template<typename T>
static void softmax(T& input) {
    float rowmax = *std::max_element(input.begin(), input.end());
    std::vector<float> y(input.size());
    float sum = 0.0f;
    for(size_t i=0; i!=input.size(); ++i) {
        y[i] = std::exp(input[i] - rowmax);
        sum += y[i];
    }
    for(size_t i=0; i!=input.size(); ++i) {
        input[i] = y[i] / sum;
    }
}

//用于推理
struct MINIST 
{
    MINIST() {
        auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
        input_tensor_ = Ort::Value::CreateTensor<float>(memory_info, input_image_.data(), input_image_.size(),
                                                        input_shape_.data(), input_shape_.size());
        output_tensor_ = Ort::Value::CreateTensor<float>(memory_info, results_.data(), results_.size(),
                                                        output_shape_.data(), output_shape_.size());
    }
	
	//std::ptrdiff_t是C++中用来描述两个指针差的类型,可以简单理解为有符号整型,存储指针偏差
    std::ptrdiff_t Run() {
        const char* input_names[] = {"Input3"};
        const char* output_names[] = {"Plus214_Output_0"};
		//inputname和outputname,一个inputnode对应一个inputname
		//可以用session_.GetInputCount()来获取输入节点数量
		//AllocatorWithDefaultOptions allocator;
		//可以用ort_session->GetInputNameAllocated(i, allocator)来获取第i个输入的名字
		
        Ort::RunOptions run_options;
        
        session_.Run(run_options, input_names, &input_tensor_, 1, output_names, &output_tensor_, 1);
        //inputname和input_tensor要对应,只有一个输入的情况下不用关心
        softmax(results_);
        for(auto i : results_){
            std::cout << i << " ";
        }
        result_ = std::distance(results_.begin(), std::max_element(results_.begin(), results_.end()));
        //获取可能性最大的数字的指针偏移量,对于本模型来说,偏移量就正好是推理结果
        return result_;
    }

    static constexpr const int width_ = 28;
    static constexpr const int height_ = 28;
    
    //一维数组,用于存放输入image的数据
    std::array<float, width_ * height_> input_image_;
    std::array<float, 10> results_;
    int64_t result_{0};
    //注意results_和result_,results_是指模型输出,result_是经过后处理后的最终结果

    private:
        Ort::Env env_{ORT_LOGGING_LEVEL_WARNING, "test"};
        Ort::Session session_{env_, "/home/wyq/hobby/model_deploy/onnx/export_onnx/MINIST/mnist.onnx",Ort::SessionOptions{nullptr}};
        
        Ort::Value input_tensor_{nullptr};
        std::array<int64_t,4> input_shape_{1, 1, width_, height_};
        
        Ort::Value output_tensor_{nullptr};
        std::array<int64_t,2> output_shape_{1, 10};
};

以上是与推理有关的类的代码,接下来是配套的用于实习手写数字的代码,与该blog主题无太大关系,不需要认真解读


bool draw_{false};
bool clear_{false};
void draw_callback(int event, int x, int y, int flags, void* userdata) {
    if(event == cv::EVENT_LBUTTONDOWN) {
        draw_ = true;
    } else if(event == cv::EVENT_LBUTTONUP) {
        draw_ = false;
    }else if(event == cv::EVENT_RBUTTONDOWN) {
        clear_ = true;
    } else if(event == cv::EVENT_RBUTTONUP) {
        clear_ = false;
    }
    if(draw_) {
        cv::circle(*static_cast<cv::Mat*>(userdata), cv::Point(x, y), 2, cv::Scalar(255), -1);

    }
    if(clear_) {
        cv::Mat image(drawing_area_height_*2, drawing_area_width_*2, CV_8UC1, cv::Scalar(0));
        *static_cast<cv::Mat*>(userdata) = image;
    }
}

下面是main函数

const constexpr int drawing_area_inset_{4};
const constexpr int drawing_area_scale_{4};
const constexpr int drawing_area_width_{MINIST::width_ * drawing_area_scale_};
const constexpr int drawing_area_height_{MINIST::height_ * drawing_area_scale_};

int main(){
	std::unique_ptr<MINIST> minist_;
	//cpp11中引入的智能指针
    minist_ = std::make_unique<MINIST>();
    cv::Mat image(drawing_area_height_*2, drawing_area_width_*2, CV_8UC1, cv::Scalar(0));
    cv::namedWindow("MINIST", cv::WINDOW_GUI_EXPANDED);
    cv::setMouseCallback("MINIST", draw_callback, &image);
    std::fill(minist_->input_image_.begin(), minist_->input_image_.end(), 0.0f);
    while(true) {
        cv::imshow("MINIST", image);
        if(cv::waitKey(1) == 27) {
            break;
        }
        cv::Mat image_scaled;
        cv::resize(image, image_scaled, cv::Size(MINIST::width_, MINIST::height_));
        for(int i=0; i!=MINIST::height_; ++i) {
            for(int j=0; j!=MINIST::width_; ++j) {
                minist_->input_image_[i * MINIST::width_ + j] = image_scaled.at<uchar>(i, j) / 255.0f;
            }
        }
        minist_->Run();
        std::cout << "The number is: " << minist_->result_ << std::endl;
    }
    return 0;
}

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

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

相关文章

Vue深度教程

一、Vue简介 1.简介 2.快速上手 二、基础 1.创建一个Vue应用 2.模板语法 3.响应式基础 4.计算属性 5.Class与 Style绑定 6.条件渲染 7.列表渲染 8.事件处理 9.表单输入绑定 10.生命周期钩子 11.侦听器 12.模板引用 13.组件基础 三、深入组件 1.组件注册 2.Props 3.组件事件 …

xinput1_3.dll丢失都有什么办法可以有效的解决、xinput1_3.dll导致游戏不能启动怎么办?

使用电脑的过程中是不是会遇到关于某个dll文件丢失的提示&#xff0c;今天想和大家聊的是xinput1_3.dll文件&#xff0c;如果电脑提示xinput1_3.dll丢失有什么办法可以有效的解决&#xff0c;解决办法都有哪些&#xff0c;如果xinput1_3.dll丢失会对电脑有什么影响。&#xff0…

详解高质量增长的关键动力:ABM、数据、AI与业财融合

企业要穿越周期&#xff0c;不能仅靠节衣缩食&#xff0c;增长与盈利仍是必须。当盲目做大规模无法带来可持续发展&#xff0c;高质量增长便成为必须。在降本增效之上&#xff0c;企业需要变革增长模式。 在纷享销客的《领创者》开年直播上&#xff0c;纷享销客联合创始人、经…

OpenHarmony下musl编译工具链普法

OpenHarmony下musl编译工具链普法 引言 欠的债总是要还的&#xff0c;这不前面欠的关于OpenHarmony下musl相关的还是要还的。这里我对其中的相关知识点&#xff0c;梳理&#xff0c;归纳重新消化下&#xff01; 一.GCC/Clang/LLVM的区别与联系 说实话&#xff0c;这块我现在都…

streamlit学习-如何播放HLS视频(streamlit嵌入html)

streamlit学习-如何播放HLS视频 一.效果二.直播环境搭建(仅供演示)1.生成m3u82.搭建http服务器(支持跨域)3.验证hls(VLC播放 http://localhost:8000/playlist.m3u8) 三.streamlit demo 本文演示了streamlit如何实现hls直播[streamlit中嵌入html] 一.效果 二.直播环境搭建(仅供演…

vue+Nodejs+Koa搭建前后端系统(九)-- 上传图片

web2.0的到来使网页世界正式进入了寒武纪&#xff0c;各式各样的多媒体资源屡见不鲜&#xff0c;上传资源变得刻不容缓&#xff01; 前言 本文是在该系列的基础上&#xff0c;针对前后端代码的修改。 准备 HTTP上传图片时Content-Type值常见的有2种&#xff1a;application…

spring boot3token拦截器链的设计与实现

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途。 目录 写在前面 流程分析 需要清楚的 实现步骤 1.定义拦截器 2.创建拦截器链配置类 3.配置拦截器链顺序 4.配置拦截…

OpenAI反击Elon Musk

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Ribbon实现Cloud负载均衡

安装Zookeeper要先安装JDK环境 解压 tar -zxvf /usr/local/develop/jdk-8u191-linux-x64.tar.gz -C /usr/local/develop 配置JAVA_HOME vim /etc/profile export JAVA_HOME/usr/local/develop/jdk1.8.0_191 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOM…

力扣515. 在每个树行中找最大值(BFS,DFS)

Problem: 515. 在每个树行中找最大值 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1&#xff1a;BFS 套用BFS模板&#xff0c;直接在遍历树的某一层时将当前层的最大值存入数组中 思路2&#xff1a;DFS 回溯思想&#xff0c;在递归时不断更新可选列表&#xff08;根据…

【word】引用文献如何标注右上角

一、在Word文档中引用文献并标注在右上角的具体步骤如下 1、将光标移动到需要添加文献标注的位置&#xff1a; 2、在文档上方的工具栏中选择“引用”选项&#xff1a; 3、点击“插入脚注”或“插入尾注”&#xff1a; ①如果选择的是脚注&#xff0c;则脚注区域会出现在本页的…

git:git revert 和git reset 回退版本的使用方式

目录 git revert还原某些现有提交git reset删除提交参考 git revert还原某些现有提交 中文文档&#xff1a;https://git-scm.com/docs/git-revert/zh_HANS-CN 版本会递增&#xff0c;不影响之前提交的内容 例如&#xff1a;撤销记录为 abc123 的提交 git revert abc123git r…

企微hook源码

企微hook源码已经在QQ群内开源。速度进群下载&#xff0c;避免和谐。 QQ群&#xff1a;649480745

【sgPhotoPlayer】自定义组件:图片预览,支持点击放大、缩小、旋转图片

特性&#xff1a; 支持设置初始索引值支持显示标题、日期、大小、当前图片位置支持无限循环切换轮播支持鼠标滑轮滚动、左右键、上下键、PageUp、PageDown、Home、End操作切换图片支持Esc关闭窗口 sgPhotoPlayer源码 <template><div :class"$options.name"…

【JS】关于this的使用

this 前言一、this是什么&#xff1f;二、做什么&#xff1f;1.全局环境2.函数环境3.new实例对象4.apply、bind、call绑定4.1 apply()4.2 call()4.3 bind() 三、为什么用this&#xff1f;四、如何改变this&#xff1f;五、应用场景&#xff1f;总结 前言 痛点 经常写Vue项目&a…

springboot集成logback打印彩色日志

一、logback介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站&#xff1a; logback.qos.ch。它当前分为以下三个模块&#xff1a; logback-core&#xff1a;其它两个模块的基础模块。logback-classic&#xff1a;它是log4j的一个改良版本&#xff0c;同时它完整实…

qt练习案例

记录一下qt练习案例&#xff0c;方便学习qt知识点 基本部件 案例1 需求&#xff0c;做一个标签&#xff0c;显示"你好"知识点&#xff0c;QLabel画面 4. 参考&#xff0c;Qt 之 QLabel 案例2 需求&#xff0c;做一个标签&#xff0c;显示图片 知识点&#xff0c;…

【linux进程信号(二)】信号的保存,处理以及捕捉

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; 进程信号 1. 前言2. 信号阻塞…

仅通过头部便可控制机器人实现生活自理!四肢瘫患者福音真的来了?

运动功能障碍对个体执行基本日常生活活动&#xff08;如沐浴、更衣、用餐&#xff09;以及进行工具性日常生活活动&#xff08;包括娱乐和社交互动&#xff09;造成了显著影响&#xff0c;不仅限制了他们的活动范围&#xff0c;而且削弱了他们维持独立生活的基础。受此类障碍困…

Netty架构

Netty逻辑架构 Netty 的逻辑处理架构为典型网络分层架构设计&#xff0c;网络通信层、事件调度层、服务编排层。 一、 网络通信层 网络通信层的职责是执行网络 I/O 的操作。它支持多种网络协议和 I/O 模型的连接操作。当网络数据读取到内核缓冲区后&#xff0c;会触发网络事件…