onnx 1.16.0模型部署(python -- C++)

环境:

  • anaconda

    • python 3.9

    • torch符合自己要求即可

    • onnx1.16.0

    • onnxruntime1.16.0(选择gpu版本,pip install onnxruntime-gpu==1.16.0

  • C++(vs2019)

    • onnxruntime1.16.0(下载版本与conda一致,也使用gpu版本)

    • opencv4.5.1(根据自己实际需求是否需要)VS2022+opevcv4.5.1-CSDN博客


第一步:将pth模型转为onnx

import torch
import torchvision.models as models
import onnx
from model import get_model
import os
import numpy as np
import onnxruntime as ort
​
def export_model_to_onnx(model, input_tensor, output_file):
    """
    将PyTorch模型转换为ONNX格式。
​
    参数:
    - model: PyTorch模型。
    - input_tensor: 用于模型推理的输入张量。
    - output_file: ONNX模型的输出文件路径。
    """
    # 设置模型为评估模式
    model.eval()
​
    # 导出模型
    torch.onnx.export(model,  # 运行模型
                      input_tensor,  # 模型输入 (或一个元组对多个输入)
                      output_file,  # 输出文件路径
                      export_params=True,  # 存储训练后的参数权重
                      opset_version=10,  # ONNX版本
                      do_constant_folding=True,  # 是否执行常量折叠优化
                      input_names=['input'],  # 输入名
                      output_names=['output'],  # 输出名
                      dynamic_axes={'input': {0: 'batch_size'},  # 输入的动态轴
                                    'output': {0: 'batch_size'}})  # 输出的动态轴
​
    print(f"Model has been converted to ONNX and saved as {output_file}.")
​
def test_onnx_model(onnx_path, input_tensor):
    """
    使用ONNX Runtime加载并运行ONNX模型,返回输出。
​
    参数:
    - onnx_path: ONNX模型文件的路径。
    - input_tensor: PyTorch张量,作为输入。
    """
    providers = ['CUDAExecutionProvider']  # 根据实际环境选择合适的执行提供者
    ort_session = ort.InferenceSession(onnx_path, providers=providers)
    ort_inputs = {ort_session.get_inputs()[0].name: input_tensor.numpy()}
    ort_outputs = ort_session.run(None, ort_inputs)
    return ort_outputs
​
​
# 主程序
if __name__ == '__main__':
    #----------------------------pth转onnx-----------------------------------#
    # 加载预训练模型,导入模型,根据你自己的模型加载
    model = get_model() 
​
    weights_path = "vgg3d_checkpoint.pth"
    assert os.path.exists(weights_path), f"file: '{weights_path}' does not exist."
    model.load_state_dict(torch.load(weights_path))
​
    # 创建一个示例输入(假设输入是一个1x42x42x42的3D图像)(batch_size, channel, w, h, d) ,根据实际调整,这里是三维数据,二维数据格式:(batch_size, channel, w, h)
    dummy_input = torch.randn(1, 1, 42, 42, 42, requires_grad=True)
​
    # 指定ONNX模型输出文件路径
    onnx_output_file = 'model.onnx'
​
    # 导出模型到ONNX
    export_model_to_onnx(model, dummy_input, onnx_output_file)
    # ----------------------------pth转onnx-----------------------------------#
​
    # ----------------------------验证onnx-----------------------------------#
    for i in range(2):
        # 创建输入数据
        dummy_input = torch.randn(10, 1, 42, 42, 42)
​
        # ONNX模型路径
        onnx_model_path = 'model.onnx'
​
        # 使用ONNX Runtime测试ONNX模型
        onnx_outputs = test_onnx_model(onnx_model_path, dummy_input)
​
        print("ONNX Model Output:", onnx_outputs)
    # ----------------------------验证onnx-----------------------------------#

第二步:将onnx模型部署C++

首先打开vs,创建一个空白项目,创建一个DeepModel.h头文件和DeepModel.cpp源文件

DeepModel.h文件添加以下代码:

#pragma once
#include <onnxruntime_cxx_api.h>
#include <iostream>
#include <assert.h>
#include <vector>
#include <numeric>
#include <cassert>
#include <memory>
#include <opencv2/core.hpp>
​
class DeepModel
{
public:
    DeepModel();
    ~DeepModel();
​
private:
    // 变量
    float* predict;     // 推理结果
​
    // 模型加载
    Ort::Session load_model(const std::wstring& model_path);
​
    // 模型推理
    void  model_inference(Ort::Session* session, const cv::Mat& input_image);
​
public: 
    // 获取模型
    Ort::Session GetModel(const std::wstring& model_path);
​
    // 获取推理结果
    float* GetPredict(Ort::Session* session, const cv::Mat& input_image);
};
​

DeepModel.cpp文件添加以下代码:

#include "DeepModel.h"
​
// 初始化
DeepModel::DeepModel()
{
}
​
// 析构
DeepModel::~DeepModel()
{
    predict = nullptr;
}
​
/****************************模型加载**************************************/
Ort::Session DeepModel::load_model(const std::wstring& model_path)
{
    try {
        Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNX_C_API");
        Ort::SessionOptions session_options;
       /* OrtCUDAProviderOptions cuda_options;
        cuda_options.device_id = 0;
        session_options.AppendExecutionProvider_CUDA(cuda_options);*/
        Ort::Session session(env, model_path.c_str(), session_options);
        return session;
    }
    catch (const std::exception& e) {
        std::cerr << "An error occurred: " << e.what() << std::endl;
        throw; // rethrow the exception to handle it in calling function
    }
}
// 模型推理
void DeepModel::model_inference(Ort::Session* session, const cv::Mat& input_image)
{
    /********** 预处理图片 **********/
    // 维度和数据按自己模型修改
    int batch_size = input_image.size[0];
    int int_samples_shape[5] = { batch_size, 1, 42, 42, 42 };
​
    size_t input_data_size = batch_size * 1 * 42 * 42 * 42;
    
    // 这个+5需要根据自己输入数据维度修改,batch_size, 1, 42, 42, 42 共五个维度
    std::vector<int64_t> samples_shape(int_samples_shape, int_samples_shape + 5);
​
    float* input_data = reinterpret_cast<float*>(input_image.data);
​
    /********** 从数据值创建输入张量对象 **********/
    Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
        memory_info,
        input_data,
        input_data_size,
        samples_shape.data(),
        samples_shape.size()
        );
​
    /********** 设计输入和输出的名字 **********/
    Ort::AllocatorWithDefaultOptions allocator;
    std::vector<std::string> input_node_names;
    for (int i = 0; i < session->GetInputCount(); i++)
    {
        Ort::AllocatedStringPtr input_name = session->GetInputNameAllocated(i, allocator);
        input_node_names.push_back(input_name.get());  // 将其转换为 std::string 存储
    }
​
    std::vector<std::string> output_node_names;
    for (int i = 0; i < session->GetOutputCount(); i++)
    {
        Ort::AllocatedStringPtr output_name = session->GetOutputNameAllocated(i, allocator);
        output_node_names.push_back(output_name.get());  // 将其转换为 std::string 存储
    }
​
    std::vector<const char*> input_node_names_cstr;
    for (const auto& name : input_node_names)
    {
        input_node_names_cstr.push_back(name.c_str());  // 将 std::string 转换为 const char*
    }
​
    std::vector<const char*> output_node_names_cstr;
    for (const auto& name : output_node_names)
    {
        output_node_names_cstr.push_back(name.c_str());  // 将 std::string 转换为 const char*
    }
​
    /********** 输入图片数据,运行模型获取预测结果 **********/
    std::vector<Ort::Value> output_tensors = session->Run(
        Ort::RunOptions{ nullptr },
        input_node_names_cstr.data(),
        &input_tensor,
        1,
        output_node_names_cstr.data(),
        output_node_names_cstr.size()
    );
​
    predict = output_tensors[0].GetTensorMutableData<float>();
}
​
/****************************获取模型**************************************/
Ort::Session DeepModel::GetModel(const std::wstring& model_path)
{
    return load_model(model_path);
}
​
// 获取模型预测结果
float* DeepModel::GetPredict(Ort::Session* session, const cv::Mat& input_image)
{
    model_inference(session, input_image);
    return predict;
}

然后创建一个main.cpp文件,添加一下测试代码:

#pragma once
#include "DeepModel.h"
#include <Windows.h>
​
int main() {
    
    DeepModel model;
    try {
        // 加载模型,onnx模型路径放在项目文件夹下
        auto session = model.GetModel(L"model.onnx");
​
        if (slice_session) {
            std::cout << "slice model load success!" << std::endl;
        }
​
        if (chong_session) {
            std::cout << "chong model load success!" << std::endl;
        }
​
       int batch_size = 1;
        // 根据自己的输入修改
        cv::Mat input_image(batch_size, 1 * 42 * 42 * 42, CV_32F);
​
        input_image = cv::Scalar(1);
​
        float* predict = model.GetPredict(&session, input_image);
        
        // 根据自己的分类数量修改 batch_size * 2,我的是两个类别
        for (int j = 0; j < batch_size * 2; ++j) {
            std::cout << j << "::" << predict[j] << std::endl;
        }
        predict = nullptr;
        return 0;
    }
    catch (const std::exception& e) {
        std::cerr << "An error occurred: " << e.what() << std::endl;
    }
​
    return 0;
}

第三步:配置vs的配置(release x64)根据自己需求修改,Debug配置一样

  • VC++目录

到这里以及完成。运行代码即可。

在 Popper.js 版本 1.16.0 中,`right-start` 是一个有效的 `placement` 参数值。它表示将弹出框放置在目标元素的右侧并对齐到目标元素的顶部。 以下是一个示例代码,演示如何在 Popper.js 中使用 `right-start`: ```html <button type="button" id="myButton">点击我</button> <div id="popoverContent" style="display: none;">Popover 内容</div> <script> document.addEventListener("DOMContentLoaded", function() { var button = document.getElementById("myButton"); var popoverContent = document.getElementById("popoverContent"); new Popper(button, popoverContent, { placement: "right-start", modifiers: { preventOverflow: { enabled: false }, // 可选,用于禁用溢出修饰器 }, }); button.addEventListener("click", function() { popoverContent.style.display = "block"; }); document.addEventListener("click", function(event) { if (!button.contains(event.target)) { popoverContent.style.display = "none"; } }); }); </script> ``` 在上面的示例中,我们创建了一个按钮和一个弹出框内容元素。通过使用 Popper.js,我们将弹出框内容元素定位到按钮元素的右侧,并且顶部对齐。当点击按钮时,弹出框内容会显示出来。而当点击按钮以外的区域时,弹出框内容会隐藏。 请注意,Popper.js 的版本可能会有所不同,因此特定版本的支持可能会有所差异。确保你使用的是正确的 Popper.js 版本,并根据文档中的说明使用正确的参数值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值