导出onnx模型
环境配置
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;
}