【深度学习】【OpenVINO】【C++】模型转化、环境搭建以及模型部署的详细教程

【深度学习】【OpenVINO】【C++】模型转化、环境搭建以及模型部署的详细教程

提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论

文章目录

  • 【深度学习】【OpenVINO】【C++】模型转化、环境搭建以及模型部署的详细教程
  • 前言
  • 模型转换--pytorch转onnx
  • Windows平台搭建依赖环境
    • 安装OpenVINO
    • 安装OpenCV
    • 简化部署
  • OpenVINO调用onnx模型
    • OpenVINO推理核心流程
    • OpenVINO推理代码
  • 总结


前言

Intel OpenVINO(Open Visual Inference and Neural Network Optimization)是由英特尔推出的一款开源深度学习推理优化库,专门针对英特尔的CPU、GPU、FPGA、ASIC等硬件进行优化。它能够将深度学习模型转换为优化的推理引擎,从而在保持精度的同时提高推理速度和效率。OpenVINO专注于优化和加速机器学习模型的推理阶段,特别是对于大规模部署和实时应用场合。OpenVINO的设计目的是为了提供一个高度优化的执行环境,利用英特尔硬件的特性来实现极致的性能优化。
OpenVINO是对 Intel 硬件最原生的支持。


模型转换–pytorch转onnx

Pytorch模型转ENGINE并推理的步骤如下:

  1. 将PyTorch预训练模型文件( .pth 或 .pt 格式)转换成ONNX格式的文件(.onnx格式),这一转换过程在PyTorch环境中进行。
  2. 将转换得到的 .onnx 文件再次转换成ENGINE格式的文件(.engine格式),这一转换过程在安装的TensorRT版本的bin目录下通过trtexec.exe转化而成。
  3. 将转换得到的 .engine文件随后作为输入,调用TensorRT的C++ API来执行模型的推理。

博主使用AlexNet图像分类(五种花分类)进行演示,需要安装pytorch环境,对于该算法的基础知识,可以参考博主【AlexNet模型算法Pytorch版本详解】博文

conda create --name AlexNet python==3.10
conda activate AlexNet
# 根据自己主机配置环境
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# 假设模型转化出错则降级为指定1.16.1版本
pip install onnx==1.16.1

然后把训练模型好的AlexNet.pth模型转成AlexNet.onnx模型,pyorch2onnx.py转换代码如下:

import torch
from model import AlexNet
model = AlexNet(num_classes=5)
weights_path = "./AlexNet.pth"
# 加载模型权重
model.load_state_dict(torch.load(weights_path))
# 模型推理模式
model.eval()
model.cpu()
# 虚拟输入数据
dummy_input1 = torch.randn(1, 3, 224, 224)
# 模型转化函数
torch.onnx.export(model, (dummy_input1), "AlexNet.onnx", verbose=True, opset_version=11)


【AlexNet.pth百度云链接,提取码:ktq5 】直接下载使用即可。


Windows平台搭建依赖环境

安装OpenVINO

官网下载安装文件地址,根据自己的情况选择合适的版本。

推荐优先选择的2024.4版本,和博主一致。


选择w_openvino_toolkit_windows_2024.4.0.16579.c3152d32c9c_x86_64下载,双击运行解压后即可:

在C++工程的目录下新建一个OpenVINO2024.4.0(推荐根据当前版本命名) ,将runtime文件夹中 include、lib 、bin和version.txt 复制到OpenVINO2024.4.0。

打开VS 2019:新建新项目---->空项目---->配置项目---->项目路径以及勾选“将解决方案和项目放在同一目录中---->点击创建。

在解决方案–>源文件–>右键添加新建项。这里暂时可以默认空着不做处理。

配置openvino:项目---->属性。假设没有新建cpp文件,空项目的属性页就不会存在C/C++这一项目。

添加附加包含目录:Release | x64---->C/C+±—>常规---->附加包含目录。

D:\C++_demo_openvino\OpenVINO2024.4.0\include

链接器:Release | x64---->链接器---->常规---->附加库目录。

D:\C++_demo_openvino\OpenVINO2024.4.0\lib\intel64\Release

链接器:Release | x64---->链接器---->输入---->附加依赖项。

在D:\C++_demo_openvino\OpenVINO2024.4.0\lib\intel64\Release下找到附加依赖项的文件(.lib文件),这里根据需求添加。

openvino.lib
openvino_c.lib
openvino_onnx_frontend.lib

安装OpenCV

官网下载安装文件地址,博主使用opencv-4.8.0-windows.exe版本

双击运行解压后即可,博主重命名为opencv4.8.0:

直接下载的opencv-4.8.0-windows.exe部分功能不完整,如读取视频这类功能,假如需要完整的功能则需要自己编译,参考windows10下opencv4.8.0-cpu C++版本源码编译教程,这里博主也提供了编译好的opencv4.8.0-cpu.rar【百度网盘,提取码:22r3】。目录格式仿造着opencv-4.8.0-windows.exe的目录格式。

添加附加包含目录:Release | x64---->C/C++—>常规---->附加包含目录。

D:\C++_demo_openvino\opencv4.8.0\build\include

链接器:Release | x64---->链接器---->常规---->附加库目录。

D:\C++_demo_openvino\opencv4.8.0\build\x64\vc16\lib

链接器:Release | x64---->链接器---->输入---->附加依赖项。

opencv_world480.lib

简化部署

在Release x64模式下测试时,需要将TensorRT所需的.dll文件,以及OpenCV的.dll文件复制到自己项目的Release下。

D:\C++_demo_openvino\OpenVINO2024.4.0\bin\intel64\Release
D:\C++_demo_openvino\opencv4.8.0\build\x64\vc16\bin
===>
D:\C++_demo_openvino\tensorrt\x64\Release

没有Release目录时,需要在Release | x64模式下运行一遍代码,代码部分在下面提供,读者可以先行新建文件复制代码。

将所有的.dll文件和.exe文件放在同一个目录下可以简化应用程序的部署过程。用户无需手动配置环境变量或安装额外的组件即可运行程序。


OpenVINO调用onnx模型

OpenVINO推理核心流程

初始化运行时核心
负责模型的初始化以及设备的管理。

ov::Core core;

编译模型
在指定的设备(GPU/CPU)上准备一个模型以供执行。

ov::CompiledModel model = core.compile_model(modelPath, "CPU", config);
ov::CompiledModel model = core.compile_model(modelPath, "GPU", config);
ov::CompiledModel model = core.compile_model(modelPath, "AUTO", config);
compile_model参数modeldevice_nameproperties
内容指定需要编译的模型指定用于执行推理的设备类型进一步配置模型的执行环境包括各种配置选项,如多线程数、日志级别等

创建推理请求
封装了模型的输入和输出,以及执行推理所需的所有资源。

ov::InferRequest request = model.create_infer_request();

获取模型输入输出信息
从ov::InferRequest对象中获取模型输入和输出的详细信息,包括数量、名称、类型和形状。

const std::vector<ov::Output<const ov::Node>> inputs = model.inputs();
const std::vector<ov::Output<const ov::Node>> outputs = model.outputs();
auto input_name = inputs[i].get_any_name();
auto output_name = outputs[i].get_any_name();
ov::Tensor input_tensor = request.get_input_tensor(i);
ov::Tensor output_tensor = request.get_output_tensor(i);
ov::Shape input_shape = input_tensor.get_shape();
ov::Shape output_shape = output_tensor.get_shape();

预处理输入数据
对输入数据进行颜色空间转换,尺寸缩放、标准化以及形状维度扩展操作。

cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);
cv::resize(rgb, blob, cv::Size(input_w, input_h));
blob.convertTo(blob, CV_32F);
blob = blob / 255.0;
cv::subtract(blob, cv::Scalar(0.485, 0.456, 0.406), blob);
cv::divide(blob, cv::Scalar(0.229, 0.224, 0.225), blob);
cv::Mat tensor = cv::dnn::blobFromImage(blob);

这部分不是OpenVINO核心部分,根据任务需求不同,代码略微不同。

设置输入
将数据写入张量中,具体的操作取决于张量的数据类型和形状。

int image_size = input_w * input_h;
	float* data = input_tensor.data<float>();
	for (size_t row = 0; row < input_h; row++) {
		for (size_t col = 0; col < input_w; col++) {
			for (size_t c = 0; c < ch; c++) {
				data[image_size * c + row * input_w + col] = blob.at<Vec3f>(row, col)[c];
			}
		}
	}

执行推理
执行推理,前向传播过程,将输入数据传递给模型并生成输出结果。

request.infer();

后处理推理结果
推理完成后,从输出张量中获取结果数据,根据需要对结果进行后处理,以获得最终的预测结果。

cv::Mat prob(num, cnum, CV_32F, (float*)output_tensor.data());
cv::minMaxLoc(probmat, &minv, &maxv, &minL, &maxL);

这部分不是OpenVINO核心部分,根据任务需求不同,代码基本不同。


OpenVINO推理代码

需要配置flower_classes.txt文件存储五种花的分类标签,并将其放置到工程目录下(推荐)。

daisy
dandelion
roses
sunflowers
tulips

这里需要将AlexNet.onnx放置到工程目录下(推荐),并且将以下推理代码拷贝到新建的cpp文件中,并执行查看结果。

#include <openvino/openvino.hpp>
#include <opencv2/opencv.hpp>
#include <fstream>

using namespace cv;
using namespace std;

// 加载标签文件获得分类标签
std::string labels_txt_file = "D:/C++_demo_openvino/openvino/flower_classes.txt";
std::vector<std::string> readClassNames();
std::vector<std::string> readClassNames()
{
	std::vector<std::string> classNames;

	std::ifstream fp(labels_txt_file);
	if (!fp.is_open())
	{
		printf("could not open file...\n");
		exit(-1);
	}
	std::string name;
	while (!fp.eof())
	{
		std::getline(fp, name);
		if (name.length())
			classNames.push_back(name);
	}
	fp.close();
	return classNames;
}

int main(int argc, char** argv) {
	// 预测的目标标签数
	std::vector<std::string> labels = readClassNames();
	// 测试图片
	cv::Mat image = cv::imread("D:/C++_demo_openvino/openvino/sunflowers.jpg");
	cv::imshow("输入图", image);
	
	// 初始化运行时核心
	ov::Core core;

	// 查询支持硬件设备
	vector<string> availableDevices = core.get_available_devices();
	for (int i = 0; i < availableDevices.size(); i++) {
		printf("supported device name : %s \n", availableDevices[i].c_str());
		std::cout << availableDevices[i] << std::endl;
	}
	// onnx训练模型文件
	std::string modelPath = "D:/C++_demo_openvino/openvino/AlexNet.onnx";

	// 编译模型,不做额外配置
	ov::CompiledModel model = core.compile_model(modelPath, "AUTO");

	// 获取实际执行的设备列表
	std::string execution_devices = model.get_property(ov::device::priorities);
	std::cout << "Execution devices: " << execution_devices << std::endl;

	// 创建推理请求
	ov::InferRequest request = model.create_infer_request();

	// 获取模型输入信息
	size_t input_h = 0;
	size_t input_w = 0;
	size_t ch = 0;
	ov::Tensor input_tensor;
	const std::vector<ov::Output<const ov::Node>> inputs = model.inputs();
	for (size_t i = 0; i < inputs.size(); ++i) {
		auto input_name = inputs[i].get_any_name();
		input_tensor = request.get_input_tensor(i);
		ov::Shape input_shape = input_tensor.get_shape();
		input_h = input_shape[2];
		input_w = input_shape[3];
		ch = input_shape[1];
		std::cout << "NCHW:" << input_shape[0] << "x" << input_shape[1] << "x" << input_h << "x" << input_w << std::endl;
	}

	// 获取模型输出信息
	size_t num = 0;
	size_t cnum = 0;
	ov::Tensor output_tensor;
	const std::vector<ov::Output<const ov::Node>> outputs = model.outputs();
	for (size_t i = 0; i < outputs.size(); ++i) {
		auto output_name = outputs[i].get_any_name();
		output_tensor = request.get_output_tensor(i);
		ov::Shape output_shape = output_tensor.get_shape();
		num = output_shape[0];
		cnum = output_shape[1];
		std::cout << "NC:" << output_shape[0] << "x" << output_shape[1]<< std::endl;
	}

	// 预处理输入数据
	cv::Mat rgb, blob;
	// 默认是BGR需要转化成RGB
	cv::cvtColor(image, rgb, cv::COLOR_BGR2RGB);
	// 对图像尺寸进行缩放
	cv::resize(rgb, blob, cv::Size(input_w, input_h));
	blob.convertTo(blob, CV_32F);
	// 对图像进行标准化处理
	blob = blob / 255.0;	// 归一化
	cv::subtract(blob, cv::Scalar(0.485, 0.456, 0.406), blob);	// 减去均值
	cv::divide(blob, cv::Scalar(0.229, 0.224, 0.225), blob);	//除以方差

	// 设置输入:转化成tensor 
	int image_size = input_w * input_h;
	float* data = input_tensor.data<float>();
	// HWC => NCHW
	for (size_t row = 0; row < input_h; row++) {
		for (size_t col = 0; col < input_w; col++) {
			for (size_t c = 0; c < ch; c++) {
				data[image_size * c + row * input_w + col] = blob.at<Vec3f>(row, col)[c];
			}
		}
	}
	// 模型推理
	request.infer();

	// 1x5 获取输出数据并包装成一个cv::Mat对象,为了方便后处理
	cv::Mat prob(num, cnum, CV_32F, (float*)output_tensor.data());

	// 后处理推理结果
	cv::Point maxL, minL;	// 用于存储图像分类中的得分最小值索引和最大值索引(坐标)
	double maxv, minv;		// 用于存储图像分类中的得分最小值和最大值
	cv::minMaxLoc(prob, &minv, &maxv, &minL, &maxL);

	int max_index = maxL.x;		// 获得最大值的索引,只有一行所以列坐标既为索引
	std::cout << "label id: " << max_index << std::endl;
	// 在测试图像上加上预测的分类标签
	cv::putText(image, labels[max_index], cv::Point(50, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, 8);
	cv::imshow("输入图像", image);
	cv::waitKey(0);
	return 0;
}

图片正确预测为向日葵:


总结

尽可能简单、详细的介绍了pytorch模型到onnx模型的转化,C++下OpenVINO环境的搭建以及onnx模型的OpenVINO部署。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

我们可以用微服务创建状态机吗?

大家好&#xff0c;我是锋哥。今天分享关于【我们可以用微服务创建状态机吗&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 我们可以用微服务创建状态机吗&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 是的&#xff0c;微服务架构可…

为什么选择 Spring data hadoop

&#x1f449; 请点赞支持这款 全新设计的脚手架 &#xff0c;让 Java 再次伟大&#xff01; spring-data-hadoop hbase 常见的操作方式有以下三种&#xff1a; Native Api 原生 api 操作繁琐&#xff0c;就像用 JDBC 操作关系型数据库一样&#xff0c;类似 flush、submit、…

Windows系统启动MongoDB报错无法连接服务器

文章目录 发现问题解决办法 发现问题 1&#xff09;、先是发现执行 mongo 命令&#xff0c;启动报错&#xff1a; error: MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017&#xff1b; 2&#xff09;、再检查 MongoDB 进程 tasklist | findstr mongo 发现没有进程&a…

【最全基础知识2】机器视觉系统硬件组成之工业相机镜头篇--51camera

机器视觉系统中,工业镜头作为必备的器件之一,须和工业相机搭配。工业镜头是机器视觉系统中不可或缺的重要组成部分,其质量和性能直接影响到整个系统的成像质量和检测精度。 目录 一、基本功能和作用 二、分类 1、按成像方式分 2、按焦距分 3、按接口类型分 4、按应用…

时间序列预测(九)——门控循环单元网络(GRU)

目录 一、GRU结构 二、GRU核心思想 1、更新门&#xff08;Update Gate&#xff09;&#xff1a;决定了当前时刻隐藏状态中旧状态和新候选状态的混合比例。 2、重置门&#xff08;Reset Gate&#xff09;&#xff1a;用于控制前一时刻隐藏状态对当前候选隐藏状态的影响程度。…

STM32实现毫秒级时间同步

提起“时间同步”这个概念&#xff0c;大家可能很陌生。一时间搞不清楚是什么意思。 我理解“时间同步”可以解决多个传感器采集数据不同时的问题&#xff0c;让多个传感器同时采集数据。 打个比方。两个人走路&#xff0c;都是100毫秒走一步&#xff08;频率相同是前提&…

2024数学分析【南昌大学】

计算极限 lim ⁡ n → ∞ 2024 n ( 1 − cos ⁡ 1 n 2 ) n 3 1 + n 2 − n \mathop {\lim }\limits_{n \to \infty } \frac{{\sqrt[n]{{2024}}\left( {1 - \cos \frac{1}{{{n^2}}}} \right){n^3}}}{{\sqrt {1 + {n^2}} - n}} n→∞lim​1+n2 ​−nn2024 ​(1−cosn21​)n3​ …

XJ02、消费金融|消费金融业务模式中的主要主体

根据所持有牌照类型的不同,消费金融服务供给方主要分为商业银行、汽车金融公司、消费金融公司和小贷公司,不同类型机构定位不同、提供消费金融服务与产品类型也各不相同。此外,互联网金融平台也成为中国消费金融业务最重要的参与方之一,虽其并非持牌金融机构,但借助其流量…

React 分装webSocket

背景 AI 实时对话 需要流式数据 React Hooks 写法。新建WebSocket.tsx 放在根目录components import { useCallback, useRef, useState } from react;type MessageHandler (message: MessageEvent) > void; type ErrorHandler (event: Event) > void;export functi…

深度学习(一)基础:神经网络、训练过程与激活函数(1/10)

深度学习基础&#xff1a;神经网络、训练过程与激活函数 引言&#xff1a; 深度学习作为机器学习的一个子领域&#xff0c;近年来在人工智能的发展中扮演了举足轻重的角色。它通过模仿人脑的神经网络结构&#xff0c;使得计算机能够从数据中学习复杂的模式和特征&#xff0c;…

List、Set、数据结构、Collections

一、数据结构 1.1 常用的数据结构 栈 栈&#xff1a;stack,又称堆栈&#xff0c;它是运算受限的线性表&#xff0c;其限制是仅允许在标的一端进行插入和删除操作&#xff0c;不允许在其他任何位置进行添加、查找、删除等操作。 简单的说&#xff1a;采用该结构的集合&#…

【网络协议栈】Tcp协议(下)的可靠性和高效性(超时重传、快速重传、拥塞控制、流量控制)

绪论: 承接上文&#xff0c;上文写到Tcp协议的结构以及对tcp协议的性能优化的滑动窗口&#xff0c;本章我们将继续了解Tcp协议的可靠性和高效性的具体展示。后面我将继续完善网络协议栈的网络层协议敬请期待&#xff01; 话不多说安全带系好&#xff0c;发车啦&#xff08;建议…

【AI绘画】Midjourney进阶:对角线构图详解

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AI绘画 | Midjourney 文章目录 &#x1f4af;前言&#x1f4af;什么是构图为什么Midjourney要使用构图 &#x1f4af;对角线构图特点应用场景提示词书写技巧测试 &#x1f4af;小结 &#x1f4af;前言 【AI绘画】Midjourney进阶&a…

Linux常用命令1

切换目录 cd [rootlocalhost menge]# cd /[rootlocalhost /]# cd: cd [-L|[-P [-e]] [-]] [目录] 查看当前的目录 pwd 浏览目录内容 ls ls浏览后颜色表示 白色&#xff1a;普通文件 蓝色&#xff1a;目录 红色&#xff1a;压缩包文件 黄色&#xff1a;设备文件 绿…

以 6502 为例讲讲怎么阅读 CPU 电路图

开篇 你是否曾对 CPU 的工作原理充满好奇&#xff0c;以及简单的晶体管又是如何组成逻辑门&#xff0c;进而构建出复杂的逻辑电路实现&#xff1f;本文将以知名的 6502 CPU 的电路图为例&#xff0c;介绍如何阅读 CPU 电路图&#xff0c;并向你演示如何从晶体管电路还原出逻辑…

.NET Core WebApi第2讲:前后端分离,Restful

动态页面&#xff1a;数据流动 / Web服务器 / Ajax / 前后端分离 / restful风格源栈课堂一起帮https://17bang.ren/Code/261 一、Ajax&#xff1a;页面可以局部刷新 1、PPT演示:使用Ajax也无法减小带宽耗用 请求第一个页面&#xff0c;用AJAX从服务器端加载了一个页头。 请求第…

Maven进阶——坐标、依赖、仓库

目录 1.pomxml文件 2. 坐标 2.1 坐标的概念 2.2 坐标的意义 2.3 坐标的含义 2.4 自己项目的坐标 2.5 第三方项目坐标 3. 依赖 3.1 依赖的意义 3.2 依赖的使用 3.3 第三方依赖的查找方法 3.4 依赖范围 3.5 依赖传递和可选依赖 3.5.1 依赖传递 3.5.2 依赖范围对传…

ollama 在 Linux 环境的安装

ollama 在 Linux 环境的安装 介绍 他的存在在我看来跟 docker 的很是相似&#xff0c;他把市面上已经存在的大语言模型集合在一个仓库中&#xff0c;然后通过 ollama 的方式来管理这些大语言模型 下载 # 可以直接通过 http 的方式吧对应的 shell 脚本下载下来&#xff0c;然…

【10天速通Navigation2】(三) :Cartographer建图算法配置:从仿真到实车,从原理到实现

前言 往期内容&#xff1a; 第一期&#xff1a;【10天速通Navigation2】(一) 框架总览和概念解释第二期&#xff1a;【10天速通Navigation2】(二) &#xff1a;ROS2gazebo阿克曼小车模型搭建-gazebo_ackermann_drive等插件的配置和说明 本教材将贯穿nav2的全部内容&#xff0c…

【Android】Kotlin教程(4)

文章目录 1.field2.计算属性3.主构造函数4.次构造函数5.默认参数6.初始化块7.初始化顺序7.延迟初始化lateinit8.惰性初始化 1.field field 关键字通常与属性的自定义 getter 和 setter 一起使用。当你需要为一个属性提供自定义的行为时&#xff0c;可以使用 field 来访问或设置…