onnxruntim的使用方法

news2024/9/23 13:30:25

onnxruntime是谁发明的?

ONNX Runtime 是由微软公司开发和维护的深度学习推理框架。ONNX Runtime 的前身是 Microsoft Cognitive Toolkit (CNTK),它是微软公司开发的一个深度学习框架,支持多种硬件平台和操作系统,具有高性能和易用性的特点。CNTK 最初是微软公司内部使用的深度学习框架,后来在 2016 年开源,成为一个开源项目。

随着深度学习技术的快速发展,越来越多的深度学习框架涌现,CNTK 开始面临着来自 TensorFlow、PyTorch 等框架的激烈竞争。为了更好地适应这种竞争态势,微软公司决定将 CNTK 的核心部分与 ONNX 标准进行整合,推出一个新的深度学习推理框架 ONNX Runtime。ONNX Runtime 继承了 CNTK 的高性能和易用性特点,同时支持 ONNX 标准,可以轻松地与其他深度学习框架进行集成,具有更广泛的适用性。

因此可以说,ONNX Runtime 是由微软公司开发和维护的深度学习推理框架,是基于微软公司的 CNTK 框架和 ONNX 标准整合而成的。

runtimeonnx 和tensorrt openvion的区别是什么?

ONNX Runtime、TensorRT 和 OpenVINO 都是深度学习推理框架,用于在硬件平台上执行深度学习模型的推理操作。它们都具有高性能和易用性的特点,同时也各有特点。

ONNX Runtime 是一个跨平台、高性能的深度学习推理框架,支持多种硬件平台和操作系统,包括 CPU、GPU、FPGA 等。ONNX Runtime 可以加载 ONNX 格式的模型,并在不同的硬件平台上执行推理操作,同时提供了丰富的 API 和工具,方便用户进行模型优化、部署和调试。与 TensorRT 和 OpenVINO 相比,ONNX Runtime 更加通用,可以支持更多的硬件平台和操作系统,同时也具有更高的灵活性和可扩展性。

TensorRT 是英伟达公司推出的一个深度学习推理库,主要用于在英伟达 GPU 上加速深度学习推理。TensorRT 可以执行各种优化,如网络剪枝、量化、层融合等,从而提升推理性能和减小模型大小。TensorRT 与 ONNX Runtime 相比,更加专注于在英伟达 GPU 上的深度学习推理加速,具有更高的性能和更好的可伸缩性。

OpenVINO 是英特尔公司推出的一个深度学习推理框架,主要用于在英特尔 CPU、GPU、VPU 等硬件上执行深度学习推理操作。OpenVINO 可以对深度学习模型进行多种优化,如模型剪枝、量化、分层优化等,从而提高推理性能和减小模型大小。与 ONNX Runtime 和 TensorRT 相比,OpenVINO 更加专注于在英特尔硬件上的深度学习推理加速,具有更高的性能和更好的可伸缩性。

因此,ONNX Runtime、TensorRT 和 OpenVINO 都是优秀的深度学习推理框架,各有其优点和适用场景。用户可以根据自己的需求和硬件平台选择合适的框架进行深度学习推理操作。

什么是onnxruntime?

ONNX Runtime 是一个用于高效推理 ONNX 模型的开源引擎。ONNX 是一种开放的深度学习模型交换格式,可以用于将深度学习模型从一个框架转换到另一个框架,从而实现跨平台和跨框架的模型部署和推理。

ONNX Runtime 提供了一个高度优化的推理引擎,可在多种硬件平台上以低延迟和高吞吐量运行 ONNX 模型。它支持多种编程语言和平台,包括 Python、C++、C#、Java、JavaScript 和 .NET 等,并可以与各种深度学习框架(包括 PyTorch、TensorFlow、Keras、Caffe2、ONNX、OpenVINO 等)无缝集成。

ONNX Runtime 的优点包括:

  1. 高效性:ONNX Runtime 提供了一系列高效的优化技术,包括图优化、节点融合、内存重用等,可以在多种硬件平台上以低延迟和高吞吐量运行 ONNX 模型。

  2. 跨平台:ONNX Runtime 支持多种硬件平台,包括 CPU、GPU、FPGA、DSP 等,可以在各种设备上进行模型推理。

  3. 跨框架:ONNX Runtime 可以与各种深度学习框架无缝集成,可以将模型从一个框架转换到另一个框架,并在各种框架之间进行模型部署和推理。

  4. 易用性:ONNX Runtime 提供了一个简单易用的 API,可以轻松加载和运行 ONNX 模型,同时支持多种编程语言和平台。

总之,ONNX Runtime 是一个功能强大、高效、跨平台和跨框架的深度学习模型推理引擎,可以帮助开发者在多种硬件平台和深度学习框架之间轻松部署和推理模型。

如何使用onnxruntime?

使用 ONNX Runtime 进行模型推理需要以下步骤:

  1. 安装 ONNX Runtime:首先需要在本地计算机上安装 ONNX Runtime。可以通过 ONNX Runtime 的官方网站(https://www.onnxruntime.ai/)下载适合自己系统的二进制文件,或者使用 pip 命令安装 ONNX Runtime。

  2. 加载模型:使用 ONNX Runtime 的 API 可以加载 ONNX 格式的深度学习模型。可以通过 onnxruntime.InferenceSession 类来加载模型。例如:

import onnxruntime
sess = onnxruntime.InferenceSession('model.onnx')
  1. 准备输入数据:在进行模型推理之前,需要将输入数据转换成模型需要的格式。可以使用 numpy 数组或者 Python 原生的列表来表示输入数据。例如:
import numpy as np
input_data = np.random.rand(1, 3, 224, 224).astype(np.float32)
  1. 进行模型推理:使用 InferenceSession 的 run 方法进行模型推理,并获取输出结果。例如:
output = sess.run(None, {'input': input_data})

其中,第一个参数 None 表示获取所有输出结果,第二个参数是一个字典,指定输入数据的名称和对应的数据。

  1. 解析输出结果:根据具体模型的输出结构和含义,可以对输出结果进行解析和处理。输出结果通常是一个或多个 numpy 数组,表示模型对输入数据的预测结果。例如:
output1, output2 = output

需要注意的是,使用 ONNX Runtime 进行模型推理需要对模型进行一定的优化和转换,以便在 ONNX Runtime 上高效地运行。可以使用 ONNX 官方提供的 onnxruntime-tools 工具集进行模型优化和转换。同时,由于 ONNX Runtime 支持多种硬件平台和优化技术,因此在实际使用中需要根据具体场景进行调优和优化。

开源项目

在这里插入图片描述

https://github.com/hpc203/nanodet-plus-opencv
使用OpenCV部署NanoDet-Plus,包含C++和Python两个版本的程序

在本仓库里,分别包含了使用opencv,使用onnxruntime部署NanoDet-Plus目标检测的程序,并且都是包含C++和Python两种版本的源码

ort_session`:ONNX Runtime 的会话

ort_session 是 ONNX Runtime 的会话对象,在 C++ 和 Python 中都可以使用。会话对象是 ONNX Runtime 的核心组件之一,负责加载和执行 ONNX 模型。使用 ort_session 对象,可以将 ONNX 模型加载到内存中,并执行推理操作,得到模型的输出结果。

在 C++ 中,可以通过以下方式创建会话对象:

Ort::SessionOptions session_options;
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
std::wstring model_path = L"model.onnx";
Ort::Session ort_session(env, model_path.c_str(), session_options);

在 Python 中,可以通过以下方式创建会话对象:

import onnxruntime as ort

model_path = "model.onnx"
ort_session = ort.InferenceSession(model_path)

无论是在 C++ 还是 Python 中,都可以使用会话对象的 Run() 方法执行推理操作,得到模型的输出结果。在执行推理之前,还需要将输入数据转换成 ONNX 模型所需的格式,并将其封装成 Tensor 对象。在 C++ 中,可以使用 Ort::Value 类来封装 Tensor 对象;在 Python 中,可以使用 ort.InferenceSession 对象的 run() 方法来封装 Tensor 对象。

使用 ort_session 对象可以灵活地进行模型推理,包括设置会话参数、加载模型、输入数据转换、推理操作、输出结果解析等。这使得 ONNX Runtime 成为一个非常强大、易用的深度学习推理框架,被广泛应用于各种场景中,如计算机视觉、自然语言处理、语音识别等。

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
//#include <cuda_provider_factory.h>
#include <onnxruntime_cxx_api.h>

using namespace cv;
using namespace std;
using namespace Ort;
/**
这是一个自定义的结构体类型 BoxInfo,用于存储目标检测模型的输出结果中的矩形框信息。该结构体包含以下字段:

- `x1`:矩形框左上角的 x 坐标。
- `y1`:矩形框左上角的 y 坐标。
- `x2`:矩形框右下角的 x 坐标。
- `y2`:矩形框右下角的 y 坐标。
- `score`:矩形框的置信度得分。
- `label`:矩形框所属的目标类别标签。

该结构体可以用于存储目标检测模型的输出结果中的每个矩形框的信息。在实际应用中,可以使用该结构体来方便地处理和显示检测结果。例如,可以定义一个 vector<BoxInfo> 类型的变量来存储所有检测到的矩形框的信息,并使用 OpenCV 的 rectangle 和 putText 函数将其绘制到图像上,以便进行可视化和分析。
**/
typedef struct BoxInfo
{
	float x1;
	float y1;
	float x2;
	float y2;
	float score;
	int label;
} BoxInfo;

/**
这是一个 NanoDet_Plus 类的声明,用于实现目标检测模型的推理功能。类中包含了一些私有成员变量和函数,具体说明如下:

- `score_threshold`:目标检测模型的置信度阈值,默认为 0.5。
- `nms_threshold`:非极大值抑制的阈值,默认为 0.5。
- `class_names`:目标检测模型的类别名称列表。
- `num_class`:目标检测模型的类别数目。
- `input_image_`:输入图像的数据。
- `keep_ratio`:是否保持图像宽高比。
- `inpWidth`:输入图像的宽度。
- `inpHeight`:输入图像的高度。
- `reg_max`:检测框的最大尺寸。
- `num_stages`:检测模型的阶段数目。
- `stride`:每个阶段的步长。
- `mean`:图像均值。
- `stds`:图像标准差。
- `env`:ONNX Runtime 的执行环境。
- `ort_session`:ONNX Runtime 的会话。
- `sessionOptions`:会话选项。
- `input_names`:输入节点的名称。
- `output_names`:输出节点的名称。
- `input_node_dims`:输入节点的维度。
- `output_node_dims`:输出节点的维度。

该类包含了构造函数和 detect 函数。构造函数用于初始化模型,并加载类别名称列表和参数设置。detect 函数用于对输入图像进行目标检测,并返回检测结果。在 detect 函数中,首先将输入图像进行预处理,并通过 ONNX Runtime 进行模型推理,然后将模型输出转换成矩形框的信息,并进行非极大值抑制,最终返回检测结果。
**/
class NanoDet_Plus
{
public:
	NanoDet_Plus(string model_path, string classesFile, float nms_threshold, float objThreshold);
	void detect(Mat& cv_image);
private:
	float score_threshold = 0.5;
	float nms_threshold = 0.5;
	vector<string> class_names;
	int num_class;

	Mat resize_image(Mat srcimg, int *newh, int *neww, int *top, int *left);
	vector<float> input_image_;
	void normalize_(Mat img);
	void softmax_(const float* x, float* y, int length);
	void generate_proposal(vector<BoxInfo>& generate_boxes, const float* preds);
	void nms(vector<BoxInfo>& input_boxes);
	const bool keep_ratio = false;
	int inpWidth;
	int inpHeight;
	int reg_max;
	const int num_stages = 4;
	const int stride[4] = { 8,16,32,64 };
	const float mean[3] = { 103.53, 116.28, 123.675 };
	const float stds[3] = { 57.375, 57.12, 58.395 };

	Env env = Env(ORT_LOGGING_LEVEL_ERROR, "nanodetplus");
	Ort::Session *ort_session = nullptr;
	SessionOptions sessionOptions = SessionOptions();
	vector<char*> input_names;
	vector<char*> output_names;
	vector<vector<int64_t>> input_node_dims; // >=1 outputs
	vector<vector<int64_t>> output_node_dims; // >=1 outputs
};
/**
这是 NanoDet_Plus 类的构造函数实现部分,用于初始化模型,并加载类别名称列表和参数设置。

首先从文件中读取类别名称列表,并初始化类别数目和非极大值抑制阈值和置信度阈值。
然后将模型文件路径转换成 std::wstring 类型,并使用 ONNX Runtime 的 Session 类加载模型。通过 Session 类的 GetInputCount 和 GetOutputCount 方法获取输入节点和输出节点的数目,并分别获取它们的名称和维度信息。
最后根据输入节点的维度信息设置输入图像的宽高和检测框的最大尺寸。

值得注意的是,在这段代码中还注释掉了一个调用 CUDA 执行提供程序的代码行,该代码行用于启用 CUDA 加速。如果要使用 CUDA 加速,需要安装 CUDA 并启用它,同时需要将 ONNX Runtime 编译为支持 CUDA 的版本。
**/
NanoDet_Plus::NanoDet_Plus(string model_path, string classesFile, float nms_threshold, float objThreshold)
{
	ifstream ifs(classesFile.c_str());
	string line;
	while (getline(ifs, line)) this->class_names.push_back(line);
	this->num_class = class_names.size();
	this->nms_threshold = nms_threshold;
	this->score_threshold = objThreshold;

	std::wstring widestr = std::wstring(model_path.begin(), model_path.end());
	//OrtStatus* status = OrtSessionOptionsAppendExecutionProvider_CUDA(sessionOptions, 0);
	sessionOptions.SetGraphOptimizationLevel(ORT_ENABLE_BASIC);
	ort_session = new Session(env, widestr.c_str(), sessionOptions);
	size_t numInputNodes = ort_session->GetInputCount();
	size_t numOutputNodes = ort_session->GetOutputCount();
	AllocatorWithDefaultOptions allocator;
	for (int i = 0; i < numInputNodes; i++)
	{
		input_names.push_back(ort_session->GetInputName(i, allocator));
		Ort::TypeInfo input_type_info = ort_session->GetInputTypeInfo(i);
		auto input_tensor_info = input_type_info.GetTensorTypeAndShapeInfo();
		auto input_dims = input_tensor_info.GetShape();
		input_node_dims.push_back(input_dims);
	}
	for (int i = 0; i < numOutputNodes; i++)
	{
		output_names.push_back(ort_session->GetOutputName(i, allocator));
		Ort::TypeInfo output_type_info = ort_session->GetOutputTypeInfo(i);
		auto output_tensor_info = output_type_info.GetTensorTypeAndShapeInfo();
		auto output_dims = output_tensor_info.GetShape();
		output_node_dims.push_back(output_dims);
		/*for (int j = 0; j < output_dims.size(); j++)
		{
			cout << output_dims[j] << ",";
		}
		cout << endl;*/
	}
	this->inpHeight = input_node_dims[0][2];
	this->inpWidth = input_node_dims[0][3];
	this->reg_max = (output_node_dims[0][output_node_dims[0].size() - 1] - this->num_class) / 4 - 1;
}
/**
这是 NanoDet_Plus 类中的一个私有函数 resize_image,用于将输入图像调整为模型所需的大小。该函数的输入参数为原始图像 Mat 类型,输出参数为调整后的图像的宽高和上下左右边界的偏移量。该函数的返回值为调整后的图像 Mat 类型。

首先获取原始图像的宽高,然后根据 keep_ratio 参数判断是否需要保持宽高比。如果需要保持宽高比,根据原始图像的宽高比计算出调整后的图像的宽高和上下左右边界的偏移量,使用 OpenCV 的 resize 函数将原始图像调整到目标大小,并使用 copyMakeBorder 函数在边缘添加黑边,以保持调整后的图像大小不变。如果不需要保持宽高比,直接使用 resize 函数将原始图像调整到目标大小。

该函数的作用是将输入图像调整为模型所需的大小,以便进行模型推理。调整后的图像可以更好地匹配模型的输入大小,提高检测精度和速度。
**/
Mat NanoDet_Plus::resize_image(Mat srcimg, int *newh, int *neww, int *top, int *left)
{
	int srch = srcimg.rows, srcw = srcimg.cols;
	*newh = this->inpHeight;
	*neww = this->inpWidth;
	Mat dstimg;
	if (this->keep_ratio && srch != srcw) {
		float hw_scale = (float)srch / srcw;
		if (hw_scale > 1) {
			*newh = this->inpHeight;
			*neww = int(this->inpWidth / hw_scale);
			resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
			*left = int((this->inpWidth - *neww) * 0.5);
			copyMakeBorder(dstimg, dstimg, 0, 0, *left, this->inpWidth - *neww - *left, BORDER_CONSTANT, 0);
		}
		else {
			*newh = (int)this->inpHeight * hw_scale;
			*neww = this->inpWidth;
			resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
			*top = (int)(this->inpHeight - *newh) * 0.5;
			copyMakeBorder(dstimg, dstimg, *top, this->inpHeight - *newh - *top, 0, 0, BORDER_CONSTANT, 0);
		}
	}
	else {
		resize(srcimg, dstimg, Size(*neww, *newh), INTER_AREA);
	}
	return dstimg;
}
/**
这是 NanoDet_Plus 类中的一个私有函数 normalize_,用于将输入图像进行归一化。该函数的输入参数为输入图像 Mat 类型,输出参数为归一化后的图像数据,保存在成员变量 input_image_ 中。

首先获取输入图像的宽高和通道数,然后遍历每个像素点,将像素值减去均值,再除以标准差,最终将归一化后的像素值保存在 input_image_ 中。

该函数的作用是将输入图像进行归一化,以便与模型的输入数据相匹配。归一化后的图像数据可以更好地进行模型推理,提高检测精度和速度。
**/
void NanoDet_Plus::normalize_(Mat img)
{
	//    img.convertTo(img, CV_32F);
	int row = img.rows;
	int col = img.cols;
	this->input_image_.resize(row * col * img.channels());
	for (int c = 0; c < 3; c++)
	{
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{
				float pix = img.ptr<uchar>(i)[j * 3 + c];
				//this->input_image_[c * row * col + i * col + j] = (pix / 255.0 - mean[c] / 255.0) / (stds[c] / 255.0);
				this->input_image_[c * row * col + i * col + j] = (pix - mean[c]) / stds[c];
			}
		}
	}
}
/**
这是 NanoDet_Plus 类中的一个私有函数 softmax_,用于进行 softmax 操作。该函数的输入参数为一个长度为 length 的一维数组 x,输出参数为一个长度为 length 的一维数组 y,保存 softmax 操作后的结果。

首先计算数组 x 中每个元素的指数值,并计算它们的和。然后将每个元素除以指数和,得到 softmax 操作后的结果。

该函数的作用是将模型输出的原始得分值进行归一化,以便进行后续处理。softmax 操作可以将得分值转换成概率分布,方便进行目标检测结果的解析和可视化。
**/
void NanoDet_Plus::softmax_(const float* x, float* y, int length)
{
	float sum = 0;
	int i = 0;
	for (i = 0; i < length; i++)
	{
		y[i] = exp(x[i]);
		sum += y[i];
	}
	for (i = 0; i < length; i++)
	{
		y[i] /= sum;
	}
}
/**
这是 NanoDet_Plus 类中的一个私有函数 generate_proposal,用于根据模型输出的预测结果生成检测框。该函数的输入参数为一个保存模型输出预测结果的一维数组 preds,输出参数为一个保存生成的检测框信息的 vector<BoxInfo> 类型的容器 generate_boxes。

首先根据模型的输出格式,计算出每个预测结果的长度 len,以及每个阶段的步长 stride,以便计算检测框在原图中的位置。然后遍历每个格子,从预测结果中提取出类别得分最高的类别和其得分值,如果得分值大于阈值,则计算出对应的检测框的位置和大小。具体地,首先从预测结果中提取出位置偏移量,然后通过 softmax 操作计算出位置偏移量的概率分布,再将概率分布乘以步长得到实际的位置偏移量。最后根据中心点坐标和位置偏移量计算出检测框的左上角和右下角坐标,并将检测框信息保存到 generate_boxes 中。

该函数的作用是将模型输出的预测结果解析为检测框,以便进行后续的非极大值抑制和结果可视化。
**/

void NanoDet_Plus::generate_proposal(vector<BoxInfo>& generate_boxes, const float* preds)
{
	const int reg_1max = reg_max + 1;
	const int len = this->num_class + 4 * reg_1max;
	for (int n = 0; n < this->num_stages; n++)
	{
		const int stride_ = this->stride[n];
		const int num_grid_y = (int)ceil((float)this->inpHeight / stride_);
		const int num_grid_x = (int)ceil((float)this->inpWidth / stride_);
		cout << "num_grid_x=" << num_grid_x << ",num_grid_y=" << num_grid_y << endl;
		
		for (int i = 0; i < num_grid_y; i++)
		{
			for (int j = 0; j < num_grid_x; j++)
			{
				int max_ind = 0;
				float max_score = 0;
				for (int k = 0; k < num_class; k++)
				{
					if (preds[k] > max_score)
					{
						max_score = preds[k];
						max_ind = k;
					}
				}
				if (max_score >= score_threshold)
				{
					const float* pbox = preds + this->num_class;
					float dis_pred[4];
					float* y = new float[reg_1max];
					for (int k = 0; k < 4; k++)
					{
						softmax_(pbox + k * reg_1max, y, reg_1max);
						float dis = 0.f;
						for (int l = 0; l < reg_1max; l++)
						{
							dis += l * y[l];
						}
						dis_pred[k] = dis * stride_;
					}
					delete[] y;
					/*float pb_cx = (j + 0.5f) * stride_ - 0.5;
					float pb_cy = (i + 0.5f) * stride_ - 0.5;*/
					float pb_cx = j * stride_ ;
					float pb_cy = i * stride_;
					float x0 = pb_cx - dis_pred[0];
					float y0 = pb_cy - dis_pred[1];
					float x1 = pb_cx + dis_pred[2];
					float y1 = pb_cy + dis_pred[3];
					generate_boxes.push_back(BoxInfo{ x0, y0, x1, y1, max_score, max_ind });
				}
				preds += len;
			}
		}
	}
	
}

void NanoDet_Plus::nms(vector<BoxInfo>& input_boxes)
{
	sort(input_boxes.begin(), input_boxes.end(), [](BoxInfo a, BoxInfo b) { return a.score > b.score; });
	vector<float> vArea(input_boxes.size());
	for (int i = 0; i < int(input_boxes.size()); ++i)
	{
		vArea[i] = (input_boxes.at(i).x2 - input_boxes.at(i).x1 + 1)
			* (input_boxes.at(i).y2 - input_boxes.at(i).y1 + 1);
	}

	vector<bool> isSuppressed(input_boxes.size(), false);
	for (int i = 0; i < int(input_boxes.size()); ++i)
	{
		if (isSuppressed[i]) { continue; }
		for (int j = i + 1; j < int(input_boxes.size()); ++j)
		{
			if (isSuppressed[j]) { continue; }
			float xx1 = (max)(input_boxes[i].x1, input_boxes[j].x1);
			float yy1 = (max)(input_boxes[i].y1, input_boxes[j].y1);
			float xx2 = (min)(input_boxes[i].x2, input_boxes[j].x2);
			float yy2 = (min)(input_boxes[i].y2, input_boxes[j].y2);

			float w = (max)(float(0), xx2 - xx1 + 1);
			float h = (max)(float(0), yy2 - yy1 + 1);
			float inter = w * h;
			float ovr = inter / (vArea[i] + vArea[j] - inter);

			if (ovr >= this->nms_threshold)
			{
				isSuppressed[j] = true;
			}
		}
	}
	// return post_nms;
	int idx_t = 0;
	input_boxes.erase(remove_if(input_boxes.begin(), input_boxes.end(), [&idx_t, &isSuppressed](const BoxInfo& f) { return isSuppressed[idx_t++]; }), input_boxes.end());
}

void NanoDet_Plus::detect(Mat& srcimg)
{
	int newh = 0, neww = 0, top = 0, left = 0;
	Mat cv_image = srcimg.clone();
	Mat dst = this->resize_image(cv_image, &newh, &neww, &top, &left);
	this->normalize_(dst);
	array<int64_t, 4> input_shape_{ 1, 3, this->inpHeight, this->inpWidth };

	auto allocator_info = MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
	Value input_tensor_ = Value::CreateTensor<float>(allocator_info, input_image_.data(), input_image_.size(), input_shape_.data(), input_shape_.size());

	// ¿ªÊ¼ÍÆÀí
	vector<Value> ort_outputs = ort_session->Run(RunOptions{ nullptr }, &input_names[0], &input_tensor_, 1, output_names.data(), output_names.size());   // ¿ªÊ¼ÍÆÀí
	/generate proposals
	vector<BoxInfo> generate_boxes;
	const float* preds = ort_outputs[0].GetTensorMutableData<float>();
	generate_proposal(generate_boxes, preds);

	 Perform non maximum suppression to eliminate redundant overlapping boxes with
	 lower confidences
	nms(generate_boxes);
	float ratioh = (float)cv_image.rows / newh;
	float ratiow = (float)cv_image.cols / neww;
	for (size_t i = 0; i < generate_boxes.size(); ++i)
	{
		int xmin = (int)max((generate_boxes[i].x1 - left)*ratiow, 0.f);
		int ymin = (int)max((generate_boxes[i].y1 - top)*ratioh, 0.f);
		int xmax = (int)min((generate_boxes[i].x2 - left)*ratiow, (float)cv_image.cols);
		int ymax = (int)min((generate_boxes[i].y2 - top)*ratioh, (float)cv_image.rows);
		rectangle(srcimg, Point(xmin, ymin), Point(xmax, ymax), Scalar(0, 0, 255), 2);
		string label = format("%.2f", generate_boxes[i].score);
		label = this->class_names[generate_boxes[i].label] + ":" + label;
		putText(srcimg, label, Point(xmin, ymin - 5), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 255, 0), 1);
	}
}

int main()
{
	NanoDet_Plus mynet("onnxmodel/nanodet-plus-m_320.onnx", "onnxmodel/coco.names", 0.5, 0.5);     /// choice = ["onnxmodel/nanodet-plus-m_320.onnx", "onnxmodel/nanodet-plus-m_416.onnx",
                                 "onnxmodel/nanodet-plus-m-1.5x_320.onnx", "onnxmodel/nanodet-plus-m-1.5x_416.onnx"]
	string imgpath = "imgs/person.jpg";
	Mat srcimg = imread(imgpath);
	mynet.detect(srcimg);

	static const string kWinName = "Deep learning object detection in ONNXRuntime";
	namedWindow(kWinName, WINDOW_NORMAL);
	imshow(kWinName, srcimg);
	waitKey(0);
	destroyAllWindows();
}

main.py

import cv2
import numpy as np
import argparse
import onnxruntime as ort
import math


class my_nanodet():
    def __init__(self, model_pb_path, label_path, prob_threshold=0.4, iou_threshold=0.3):
        self.classes = list(map(lambda x: x.strip(), open(label_path, 'r').readlines()))
        self.num_classes = len(self.classes)
        self.prob_threshold = prob_threshold
        self.iou_threshold = iou_threshold
        ### normalize: [[103.53, 116.28, 123.675], [57.375, 57.12, 58.395]]
        self.mean = np.array([103.53, 116.28, 123.675], dtype=np.float32).reshape(1, 1, 3)
        self.std = np.array([57.375, 57.12, 58.395], dtype=np.float32).reshape(1, 1, 3)
        so = ort.SessionOptions()
        so.log_severity_level = 3
        self.net = ort.InferenceSession(model_pb_path, so)
        self.input_shape = (self.net.get_inputs()[0].shape[2], self.net.get_inputs()[0].shape[3])
        self.reg_max = int((self.net.get_outputs()[0].shape[-1] - self.num_classes) / 4) - 1
        self.project = np.arange(self.reg_max + 1)
        self.strides = (8, 16, 32, 64)
        self.mlvl_anchors = []
        for i in range(len(self.strides)):
            anchors = self._make_grid(
                (math.ceil(self.input_shape[0] / self.strides[i]), math.ceil(self.input_shape[1] / self.strides[i])),
                self.strides[i])
            self.mlvl_anchors.append(anchors)
        self.keep_ratio = False
    
    def _make_grid(self, featmap_size, stride):
        feat_h, feat_w = featmap_size
        shift_x = np.arange(0, feat_w) * stride
        shift_y = np.arange(0, feat_h) * stride
        xv, yv = np.meshgrid(shift_x, shift_y)
        xv = xv.flatten()
        yv = yv.flatten()
        return np.stack((xv, yv), axis=-1)
        # cx = xv + 0.5 * (stride - 1)
        # cy = yv + 0.5 * (stride - 1)
        # return np.stack((cx, cy), axis=-1)
    
    def softmax(self, x, axis=1):
        x_exp = np.exp(x)
        # 如果是列向量,则axis=0
        x_sum = np.sum(x_exp, axis=axis, keepdims=True)
        s = x_exp / x_sum
        return s
    
    def _normalize(self, img):
        img = img.astype(np.float32)
        # img = (img / 255.0 - self.mean / 255.0) / (self.std / 255.0)
        img = (img - self.mean) / (self.std)
        return img
    
    def resize_image(self, srcimg, keep_ratio=True):
        top, left, newh, neww = 0, 0, self.input_shape[0], self.input_shape[1]
        if keep_ratio and srcimg.shape[0] != srcimg.shape[1]:
            hw_scale = srcimg.shape[0] / srcimg.shape[1]
            if hw_scale > 1:
                newh, neww = self.input_shape[0], int(self.input_shape[1] / hw_scale)
                img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
                left = int((self.input_shape[1] - neww) * 0.5)
                img = cv2.copyMakeBorder(img, 0, 0, left, self.input_shape[1] - neww - left, cv2.BORDER_CONSTANT,
                                         value=0)  # add border
            else:
                newh, neww = int(self.input_shape[0] * hw_scale), self.input_shape[1]
                img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
                top = int((self.input_shape[0] - newh) * 0.5)
                img = cv2.copyMakeBorder(img, top, self.input_shape[0] - newh - top, 0, 0, cv2.BORDER_CONSTANT, value=0)
        else:
            img = cv2.resize(srcimg, self.input_shape, interpolation=cv2.INTER_AREA)
        return img, newh, neww, top, left
    
    def post_process(self, preds, scale_factor=1, rescale=False):
        mlvl_bboxes = []
        mlvl_scores = []
        ind = 0
        for stride, anchors in zip(self.strides, self.mlvl_anchors):
            cls_score, bbox_pred = preds[ind:(ind + anchors.shape[0]), :self.num_classes], preds[ind:(ind + anchors.shape[0]), self.num_classes:]
            ind += anchors.shape[0]
            bbox_pred = self.softmax(bbox_pred.reshape(-1, self.reg_max + 1), axis=1)
            # bbox_pred = np.sum(bbox_pred * np.expand_dims(self.project, axis=0), axis=1).reshape((-1, 4))
            bbox_pred = np.dot(bbox_pred, self.project).reshape(-1, 4)
            bbox_pred *= stride
            
            # nms_pre = cfg.get('nms_pre', -1)
            nms_pre = 1000
            if nms_pre > 0 and cls_score.shape[0] > nms_pre:
                max_scores = cls_score.max(axis=1)
                topk_inds = max_scores.argsort()[::-1][0:nms_pre]
                anchors = anchors[topk_inds, :]
                bbox_pred = bbox_pred[topk_inds, :]
                cls_score = cls_score[topk_inds, :]
            
            bboxes = self.distance2bbox(anchors, bbox_pred, max_shape=self.input_shape)
            mlvl_bboxes.append(bboxes)
            mlvl_scores.append(cls_score)
        
        mlvl_bboxes = np.concatenate(mlvl_bboxes, axis=0)
        if rescale:
            mlvl_bboxes /= scale_factor
        mlvl_scores = np.concatenate(mlvl_scores, axis=0)
        
        bboxes_wh = mlvl_bboxes.copy()
        bboxes_wh[:, 2:4] = bboxes_wh[:, 2:4] - bboxes_wh[:, 0:2]  ####xywh
        classIds = np.argmax(mlvl_scores, axis=1)
        confidences = np.max(mlvl_scores, axis=1)  ####max_class_confidence
        
        indices = cv2.dnn.NMSBoxes(bboxes_wh.tolist(), confidences.tolist(), self.prob_threshold,
                                   self.iou_threshold).flatten()
        if len(indices) > 0:
            mlvl_bboxes = mlvl_bboxes[indices]
            confidences = confidences[indices]
            classIds = classIds[indices]
            return mlvl_bboxes, confidences, classIds
        else:
            print('nothing detect')
            return np.array([]), np.array([]), np.array([])
    
    def distance2bbox(self, points, distance, max_shape=None):
        x1 = points[:, 0] - distance[:, 0]
        y1 = points[:, 1] - distance[:, 1]
        x2 = points[:, 0] + distance[:, 2]
        y2 = points[:, 1] + distance[:, 3]
        if max_shape is not None:
            x1 = np.clip(x1, 0, max_shape[1])
            y1 = np.clip(y1, 0, max_shape[0])
            x2 = np.clip(x2, 0, max_shape[1])
            y2 = np.clip(y2, 0, max_shape[0])
        return np.stack([x1, y1, x2, y2], axis=-1)
    
    def detect(self, srcimg):
        img, newh, neww, top, left = self.resize_image(srcimg, keep_ratio=self.keep_ratio)
        img = self._normalize(img)
        blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0)
        
        outs = self.net.run(None, {self.net.get_inputs()[0].name: blob})[0].squeeze(axis=0)
        det_bboxes, det_conf, det_classid = self.post_process(outs)
        
        # results = []
        ratioh, ratiow = srcimg.shape[0] / newh, srcimg.shape[1] / neww
        for i in range(det_bboxes.shape[0]):
            xmin, ymin, xmax, ymax = max(int((det_bboxes[i, 0] - left) * ratiow), 0), max(
                int((det_bboxes[i, 1] - top) * ratioh), 0), min(
                int((det_bboxes[i, 2] - left) * ratiow), srcimg.shape[1]), min(int((det_bboxes[i, 3] - top) * ratioh),
                                                                               srcimg.shape[0])
            # results.append((xmin, ymin, xmax, ymax, self.classes[det_classid[i]], det_conf[i]))
            cv2.rectangle(srcimg, (xmin, ymin), (xmax, ymax), (0, 0, 255), thickness=1)
            print(self.classes[det_classid[i]] + ': ' + str(round(det_conf[i], 3)))
            cv2.putText(srcimg, self.classes[det_classid[i]] + ': ' + str(round(det_conf[i], 3)), (xmin, ymin - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), thickness=1)
        #         cv2.imwrite('result.jpg', srcimg)
        return srcimg


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--imgpath', type=str, default='imgs/person.jpg', help="image path")
    parser.add_argument('--modelpath', type=str, default='onnxmodel/nanodet-plus-m_320.onnx',
                        choices=["onnxmodel/nanodet-plus-m_320.onnx", "onnxmodel/nanodet-plus-m_416.onnx",
                                 "onnxmodel/nanodet-plus-m-1.5x_320.onnx", "onnxmodel/nanodet-plus-m-1.5x_416.onnx"],
                        help="onnx filepath")
    parser.add_argument('--classfile', type=str, default='onnxmodel/coco.names', help="classname filepath")
    parser.add_argument('--confThreshold', default=0.4, type=float, help='class confidence')
    parser.add_argument('--nmsThreshold', default=0.6, type=float, help='nms iou thresh')
    args = parser.parse_args()
    
    srcimg = cv2.imread(args.imgpath)
    net = my_nanodet(args.modelpath, args.classfile, prob_threshold=args.confThreshold, iou_threshold=args.nmsThreshold)
    srcimg = net.detect(srcimg)
    
    winName = 'Deep learning object detection in ONNXRuntime'
    cv2.namedWindow(winName, cv2.WINDOW_NORMAL)
    cv2.imshow(winName, srcimg)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

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

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

相关文章

杜甫经典长诗“三吏”“三别”赏析

杜甫简介 杜甫&#xff08;公元712—公元770&#xff09;&#xff0c;原籍湖北襄阳&#xff0c;后徙河南巩县。字子美&#xff0c;自号少陵野老&#xff0c;杜少陵&#xff0c;杜工部等&#xff0c;唐代著名诗人&#xff0c;世称“诗圣”&#xff0c;生活在安史之乱(唐朝由盛转…

时间管理:瞎忙和高效的区别

前言 最近看到一个非常有价值的文章&#xff0c;内容上确实震撼到我了&#xff0c;借鉴过来用于自勉。 人和人的差距为什么这么大&#xff1f; 而且这种差距&#xff0c;并不是家庭背景、权利财富或天赋带来的&#xff0c;仅仅是我们对时间的掌控&#xff0c;人总是错把忙碌…

B860AV2.1-A/M/B_1g/2g-通刷_当贝纯净桌面-线刷固件包

ZTE_B860AV2.1-A&#xff0f;M&#xff0f;B_1g&#xff0f;2g-通刷_当贝纯净桌面-线刷固件包-内有教程及短接点 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&a…

交直流一体化电源系统-交流电源自动测试系统ATECLOUD-Power

现代社会离不开电力&#xff0c;而电力供应的质量和可靠性&#xff0c;对于各个领域的发展都有着至关重要的作用。交直流一体化电源系统作为一种新型的供电方案&#xff0c;不仅具备高度的适应性、可靠稳定性以及节能环保性等诸多优势&#xff0c;还能为各行各业的市场提供更加…

【构造】CF634div3 D. Anti-Sudoku

Problem - D - Codeforces 题意&#xff1a; 给定一个数独&#xff0c;让你更改最多9格&#xff0c;使其变成anti-数独&#xff0c;即每一行&#xff0c;每一列&#xff0c;每一个3*3大格都存在一个数的出现次数>2 思路&#xff1a; 构造题&#xff0c;要不是根据题目隐含…

初识Vue-组件

目录 组件注册 全局注册 局部注册 全局导入 按需载入 Vue组件的生命周期 动态组件 keep-alive【使用的是LRU淘汰算法】 组件注册 全局注册 全局注册的组件可以在任何地方使用 Vue.component("custom-a", {render() {return <div>custom-a</div>…

代码随想录算法训练营day30 | 332. 重新安排行程,51. N 皇后,37. 解数独

代码随想录算法训练营day30 | 332. 重新安排行程&#xff0c;51. N 皇后&#xff0c;37. 解数独&#xff08;难度大&#xff0c;先简单了解&#xff09; 332. 重新安排行程51. N 皇后解法一&#xff1a;回溯 37. 解数独解法一&#xff1a; 总结 332. 重新安排行程 没有视频题解…

乐鑫esp32-c2开发板 烧录演示

一、准备工作 数据线X 1 、 四博智联 ESPC2-12 开发板 X 1 二、环境搭建 1、进入https://code.visualstudio.com 官网下载VSCODE软件 2、安装完成后安装乐鑫插件如下图 3、插件安装完后&#xff0c;查看- 命令面板&#xff08;快捷键CtrlShiftP&#xff09;。 4、输入config…

SpringSecurity 认证流程源码详细解读

一、SpringSecurity 本质探寻 SpringSecurity 的原理其实就是一个过滤器链&#xff0c;内部包含了提供各种功能的过滤器。这样说肯定非常枯燥&#xff0c;所以接下来还是在代码中看一看。 前期工作&#xff0c;需要在代码中引入 SpringSecurity 依赖&#xff0c;这里不再赘述…

服务(第十七篇)mysql的高级语句

mysql 6大常见的约束&#xff1a; 主键约束&#xff1a;primay key 主键字段不允许有重复的记录&#xff0c;不允许为null&#xff0c;一个表只能有一个主键 唯一性约束&#xff1a;unique key 唯一键字段不允许有重复的记录&#xff0c;但允许为null&#xff0c;一…

unordered系列容器的底层——哈希

目录 unordered系列容器的底层结构 哈希概念 哈希冲突 哈希函数 常见哈希函数 哈希冲突解决 闭散列 线性探测 哈希表什么情况下进行扩容&#xff1f;如何扩容&#xff1f; 二次探测 开散列 开散列概念 开散列增容 存储string类型的解决 闭散列的实现 开散列的实…

IS210AEBIH3BEC隔离器用于变压器等高压设备

IS210AEBIH3BEC隔离器用于变压器等高压设备 隔离器可以根据在电力系统中的位置进行分类 母线侧隔离器——隔离器直接连接到主母线线路侧隔离器 - 隔离器将放置在任何馈线的线路侧Transfer bus side isolator – isolator will be directly connected with the transfer bus S…

python自动化爬虫实战

python自动化爬虫实战 偶然的一次机会再次用到爬虫&#xff0c;借此机会记录一下爬虫的学习经历&#xff0c;方便后续复用。 需求&#xff1a;爬取网站数据并存入的csv文件中&#xff0c;总体分为两步 爬取网站数据存到到csv文件中 1、配置爬虫环境 1.1、下载自动化测试驱动 …

基于transformer的Seq2Seq机器翻译模型训练、预测教程

前言 机器翻译&#xff08;Machine Translation, MT&#xff09;是一类将某种语言&#xff08;源语言&#xff0c;source language&#xff09;的句子 x x x翻译成另一种语言&#xff08;目标语言&#xff0c;target language&#xff09;的句子 y y y 的任务。机器翻译的相关…

深度学习 - 46.DIN 深度兴趣网络

目录 一.引言 二.摘要 ABSTRACT 三.介绍 INTRODUCTION 1.CTR 在广告系统的作用 2.传统 MLP 存在的问题 3.DIN 的改进 四.近期工作 RELATEDWORK 1.传统推荐算法 2.用户行为抽取 五.背景 BACKGROUD 六.深度兴趣网络 DEEP INTEREST NETWORK 1.特征表示 Feature Repres…

ChatGPT的强化学习部分介绍——PPO算法实战LunarLander-v2

PPO算法 近线策略优化算法&#xff08;Proximal Policy Optimization Algorithms&#xff09; 即属于AC框架下的算法&#xff0c;在采样策略梯度算法训练方法的同时&#xff0c;重复利用历史采样的数据进行网络参数更新&#xff0c;提升了策略梯度方法的学习效率。 PPO重要的突…

Leecode101 ——对称二叉树

对称二叉树:Leecode 101 leecode 101 对称二叉树 根据题目描述&#xff0c;首先想清楚&#xff0c;对称二叉树要比较的是哪两个节点。对于二叉树是否对称&#xff0c;要比较的是根节点的左子树与根节点的右子树是不是相互翻转的&#xff0c;其实也就是比较两个树&#xff0c;…

A+CLUB活动预告 | 2023年5月

五月立夏已至&#xff0c;万物并秀。 ACLUB五月活动预告新鲜出炉&#xff0c;本月我们将会有2场峰会活动&#xff08;深圳&#xff09;、1场沙龙活动&#xff08;深圳&#xff09;和1场管理人支持计划&#xff08;上海&#xff09;&#xff0c;期待您的参与&#xff01; 注&a…

06- 算法解读 Fast R-CNN (目标检测)

要点&#xff1a; Fast R-CNN 属于 Two-stage detector 回归损失参考&#xff1a;https://www.cnblogs.com/wangguchangqing/p/12021638.html 二 Fast R-CNN算法 Fast R-CNN 是作者 Ross Girshick 继 R-CNN 后的又一力作。同样使用 VGG16 作为网络的 backbone &#xff0c; …

您应该使用WhatsApp电子商务的3个理由

随着WhatsApp进军电子商务领域并扩大其为企业提供的服务种类&#xff0c;它正迅速成为客户参与的完美工具和对话式商务的绝佳平台。使用WhatsApp作为主要渠道向客户销售商品和服务的前景产生了WhatsApp电子商务一词。 WhatsApp已被加冕为世界领先的消息传递平台。对于最终用户来…