【C++】使用ort推理yolov10

前言:由于笔者是编导专业,想玩玩yolo模型,搜来搜去全是python,所以在学会之后写一篇文章帮助和笔者同样情况的人

环境

Windows 10

C++17

onnxruntime18.1(DML版本)

opencv4.9

visual studio2022

1. 环境配置

1.1 OpenCV环境配置

1.1.1 OpenCV 配置

  1. 访问 OpenCV 的 GitHub 页面 https://github.com/opencv/opencv
  2. 在 Releases 页面找到最新的 .exe 文件并下载
  3. 运行安装程序,选择默认安装路径

1.1.2 在 Visual Studio 中配置 OpenCV

  1. 打开 Visual Studio,新建一个控制台项目
  2. 右键点击项目,选择“属性”
  3. 在属性页中,选择“VC++ 目录”
  4. 在“包含目录”中添加 OpenCV 安装目录下的 include 文件夹路径,如:C:\opencv\build\include
  5. 在“库目录”中添加 OpenCV 安装目录下的 x64\vc16\lib 文件夹路径,如:C:\opencv\build\x64\vc16\lib

运行下面的代码,如果环境配置没有问题,则会正确显示图片

如果显示无法打开文件lib.obj,则把代码中的#pragma comment(lib,"opencv_world490.lib")去掉

改成在 "项目” → "链接器” → “输入” 附加依赖项中写opencv_world490.lib

#include <opencv2/opencv.hpp>
#pragma comment(lib,"opencv_world490.lib")
//注意!!我这里是opencv_world490.lib,不同版本的OpenCV跟的数字是不一样,如果是按照默认位置安装,则可以在这个文件夹看你的lib叫什么
// C:\opencv\build\x64\vc16\lib
int main()
{
cv::Mat image = cv::imread("path/to/your/image.jpg"); // 替换为你的图像路径
if (image.empty())
{
std::cout << "无法加载图像!" << std::endl;
return -1;
} cv::imshow("图像", image);
cv::waitKey(0); return 0;
}

1.2 onnxruntime1.18.1(DML版本)配置

使用 NuGet 安装 ONNX Runtime

  1. 在 Visual Studio 中,右键点击项目,选择“管理 NuGet 程序包”。
  2. 在搜索框中输入 “onnxruntime”。
  3. 选择 “Microsoft.ML.OnnxRuntime.DirectML”,点击“安装”。

1.3 把环境改成C++17

  1. 打开项目属性页: 右键点击项目,选择“属性”。
  2. 选择 C/C++ 选项卡: 在左侧菜单中选择“C/C++”。
  3. 选择“高级”选项: 在右侧菜单中选择“高级”。
  4. 更改编译器语言标准: ****在“编译器语言标准”下拉菜单中选择“ISO C++17 标准 (/std:c++17)”。

2. 需要了解的知识

2.1 onnxruntime基本认识

首先我们对onnxruntime建立一些基本认识,onnxruntime就是一个推理框架,可以把模型摆进去,输入数据,模型输出数据,不止可以跑yolo,还可以跑其他模型,同样是推理框架的有Pytorch,OpenVINO,tensorRT,NCNN(据说神中神,还没开始玩),关于为什么选择ORT,不知道,群友说ORT也神中神,所以就决定是他了

2.2 yolo模型基本认识

推理一个yolo模型,我们一般把他分为三个步骤: 1. 前处理 2.推理 3. 后处理

前处理要做什么: 把不同宽高的图片处理成模型能推理的宽高,一般yolo模型都是支持(640,640)的图片的推理,如果输入的是(1920,1080)的图片就进行缩放+填充,把他变成(640,640)的图片

推理在做什么: 推理就是拿推理框架推理,一般就是一行代码,也许就是session.run(),就推理完了

后处理在做什么:模型输出的数据是非常杂乱的,因此需要对数据进行处理,比如说置信度过滤,nms,这是非常耗时的,但是在yolov10中,可以省去这一步,这也是yolov10效率高的原因

2.3 onnxruntime 是怎么进行推理的

onnxruntime执行推理依靠一行代码:auto output_tensors = session->Run(...)

他需要传入的参数有这些

  • Ort::RunOptions{ nullptr }: 推理选项,这里使用默认选项。
  • input_node_names_cstr.data(): 输入节点名称数组。
  • &input_tensor, 1: 输入张量指针和数量。
  • output_node_names_cstr.data(): 输出节点名称数组。
  • output_node_names.size(): 输出节点数量。

按照这些参数我们再继续

首先input_node_names_cstr.data() 这个参数的值怎么得到,笔者不知道,选择抄代码,代码如下

  session = std::make_unique<Ort::Session>(*env, model_path.c_str(), session_options);
allocator = std::make_unique<Ort::AllocatorWithDefaultOptions>(); auto num_input_nodes = session->GetInputCount();
auto num_output_nodes = session->GetOutputCount();
input_node_names.resize(num_input_nodes);
output_node_names.resize(num_output_nodes); for (size_t i = 0; i < num_input_nodes; i++)
{
auto input_name = session->GetInputNameAllocated(i, *allocator);
input_node_names[i] = input_name.get();
input_node_names_cstr.push_back(input_node_names[i].c_str());
}

紧接着input_tensor 的值要怎么得到,这个笔者知道

  1. 我们要先处理图片,把图片处理成640*640
  2. 将图片转成RGB格式
  3. 把图片归一化到[0,1]
  4. 把图片用split函数分成三个通道,分别存进大小为(640,640,1)的Mat的数组中(mat就是OpenCV提供的,用来储存图片信息的结构)
  5. 用for循环把三个通道的值分别用memcpy存到input_tensor_value
  6. Ort::Value::CreateTensor<float>();input_tensor_value 变成input_tensor

接下来是output_node_names_cstr.data() 怎么得到,经典抄代码环节

    for (size_t i = 0; i < num_output_nodes; i++)
{
auto output_name = session->GetOutputNameAllocated(i, *allocator);
output_node_names[i] = output_name.get();
output_node_names_cstr.push_back(output_node_names[i].c_str());
}

接下来是output_node_names ,也是抄代码,就上面一段一起的,哦这些代码都是在官方仓库抄的

这些参数都有了,就可以推理了

3. 敲代码环节

3.1 预处理图片(把图片改成模型接受的shape,然后用灰度填充)

#include <opencv2/opencv.hpp>
// 我这里出现莫名奇妙的报错,然后去掉#pragma comment(lib,"opencv_world490.lib"),
// 在visual studio中的链接器中添加opencv_world490.lib就好了
void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape,
bool autoShape, bool scaleFill, bool scaleUp, int stride, const cv::Scalar& color)
{ cv::Size shape = image.size();
float r = std::min((float)newShape.height / (float)shape.height,
(float)newShape.width / (float)shape.width);
if (!scaleUp)
r = std::min(r, 1.0f); float ratio[2]{ r, r };
int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) }; auto dw = (float)(newShape.width - new_un_pad[0]);
auto dh = (float)(newShape.height - new_un_pad[1]); if (autoShape)
{
dw = (float)((int)dw % stride);
dh = (float)((int)dh % stride);
}
else if (scaleFill)
{
dw = 0.0f;
dh = 0.0f;
new_un_pad[0] = newShape.width;
new_un_pad[1] = newShape.height;
ratio[0] = (float)newShape.width / (float)shape.width;
ratio[1] = (float)newShape.height / (float)shape.height;
} dw /= 2.0f;
dh /= 2.0f; if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
{
cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
}
else {
outImage = image.clone();
} int top = int(std::round(dh - 0.1f));
int bottom = int(std::round(dh + 0.1f));
int left = int(std::round(dw - 0.1f));
int right = int(std::round(dw + 0.1f));
params[0] = ratio[0];
params[1] = ratio[1];
params[2] = left;
params[3] = top;
cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
} int main()
{
auto img = cv::imread("./zidane.jpg");
// 目标尺寸
cv::Size newShape = cv::Size(640, 640);
cv::Vec4d temp_param = { 1,1,0,0 };
cv::Scalar color = cv::Scalar(114, 114, 114);
cv::Mat outImage;
LetterBox(img, outImage, temp_param, newShape, false, false, true, 32, color);
return 0;
}

运行起来应该是下面这个效果

3.2紧接着为了要session.run(),我们需要先创建session,同时要创建session的参数

比如,打算使用几线程,打算用什么加速器,可以是cpu推理,也可以是cuda加速,也可以是dml加速,我们这里自然是选择dml加速,在选择加速器前,要判断加速器可不可用,使用的代码是auto dml_it = std::find(providers.begin(), providers.end(), "DmlExecutionProvider");

std::vector<float> input_tensor_values;
input_tensor_values.resize(3 * width * height);
env = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_ERROR, "YOLOv10Inference");
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); std::vector<std::string> providers = Ort::GetAvailableProviders();
auto cuda_it = std::find(providers.begin(), providers.end(), "CUDAExecutionProvider");
auto dml_it = std::find(providers.begin(), providers.end(), "DmlExecutionProvider"); if (cuda_it != providers.end())
{
std::cout << "CUDA Execution Provider is available" << std::endl;
}
else if (dml_it != providers.end())
{
std::cout << "DirectML Execution Provider is available" << std::endl;
OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0);
}
else
{
std::cout << "No suitable provider found, using default CPU execution" << std::endl;
} session = std::make_unique<Ort::Session>(*env, model_path.c_str(), session_options);

加了这几行之后代码应该张这个样子,并且编译应该是能通过的,如果编译不能通过,自己排查一下吧~,我是边做边写的,如果你真的和我做的一样,应该也是可以编译通过的

// yolov10示例2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define NOMINMAX
#include <iostream>
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <dml_provider_factory.h>
void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape,
bool autoShape, bool scaleFill, bool scaleUp, int stride, const cv::Scalar& color)
{ cv::Size shape = image.size();
float r = std::min((float)newShape.height / (float)shape.height,
(float)newShape.width / (float)shape.width);
if (!scaleUp)
r = std::min(r, 1.0f); float ratio[2]{ r, r };
int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) }; auto dw = (float)(newShape.width - new_un_pad[0]);
auto dh = (float)(newShape.height - new_un_pad[1]); if (autoShape)
{
dw = (float)((int)dw % stride);
dh = (float)((int)dh % stride);
}
else if (scaleFill)
{
dw = 0.0f;
dh = 0.0f;
new_un_pad[0] = newShape.width;
new_un_pad[1] = newShape.height;
ratio[0] = (float)newShape.width / (float)shape.width;
ratio[1] = (float)newShape.height / (float)shape.height;
} dw /= 2.0f;
dh /= 2.0f; if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
{
cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
}
else {
outImage = image.clone();
} int top = int(std::round(dh - 0.1f));
int bottom = int(std::round(dh + 0.1f));
int left = int(std::round(dw - 0.1f));
int right = int(std::round(dw + 0.1f));
params[0] = ratio[0];
params[1] = ratio[1];
params[2] = left;
params[3] = top;
cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
cv::imshow("1", outImage);
cv::waitKey(0);
} int main()
{
auto img = cv::imread("./zidane.jpg");
// 目标尺寸
int width = 640;
int height = 640;
const std::wstring& model_path = L"./yolov10n.onnx";
cv::Size newShape = cv::Size(width, height);
cv::Vec4d temp_param = { 1,1,0,0 };
cv::Scalar color = cv::Scalar(114, 114, 114);
cv::Mat outImage;
LetterBox(img, outImage, temp_param, newShape, false, false, true, 32, color);
Ort::SessionOptions session_options;
std::unique_ptr<Ort::Env> env;
std::unique_ptr<Ort::Session> session;
std::vector<float> input_tensor_values;
input_tensor_values.resize(3 * width * height);
env = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_ERROR, "YOLOv10Inference");
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); std::vector<std::string> providers = Ort::GetAvailableProviders();
auto cuda_it = std::find(providers.begin(), providers.end(), "CUDAExecutionProvider");
auto dml_it = std::find(providers.begin(), providers.end(), "DmlExecutionProvider"); if (cuda_it != providers.end())
{
std::cout << "CUDA Execution Provider is available" << std::endl;
}
else if (dml_it != providers.end())
{
std::cout << "DirectML Execution Provider is available" << std::endl;
OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0);
}
else
{
std::cout << "No suitable provider found, using default CPU execution" << std::endl;
} session = std::make_unique<Ort::Session>(*env, model_path.c_str(), session_options);
return 0;
}

3.3 获取输入输出节点名称输入输出节点数量(写完了报错不要怕,因为我也报错)

allocator = std::make_unique<Ort::AllocatorWithDefaultOptions>();

auto num_input_nodes = session->GetInputCount();
auto num_output_nodes = session->GetOutputCount();
input_node_names.resize(num_input_nodes);
output_node_names.resize(num_output_nodes); for (size_t i = 0; i < num_input_nodes; i++)
{
auto input_name = session->GetInputNameAllocated(i, *allocator);
input_node_names[i] = input_name.get();
input_node_names_cstr.push_back(input_node_names[i].c_str());
} for (size_t i = 0; i < num_output_nodes; i++)
{
auto output_name = session->GetOutputNameAllocated(i, *allocator);
output_node_names[i] = output_name.get();
output_node_names_cstr.push_back(output_node_names[i].c_str());
}

3.4 把图片转成浮点型,并且归一化到[0,1],把三个通道的数据分别储存到channels中(channels是三个一维的Mat组成的数组),并把他放到input_tensor_values中,并且CreateTensor,创建input_tensor

cv::Mat float_img;
std::vector<cv::Mat> channels(3);
outImage.convertTo(float_img, CV_32FC3, 1.0 / 255.0);
cv::split(float_img, channels);
for (int i = 0; i < 3; ++i)
{
std::memcpy(input_tensor_values.data() + i * width * height, channels[i].data, width * height * sizeof(float));
}
std::vector<int64_t> input_node_dims = { 1, 3, height, width };
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info, input_tensor_values.data(), input_tensor_values.size(),
input_node_dims.data(), input_node_dims.size()
);

3.5 推理!!获取推理结果

  auto output_tensors = session->Run(Ort::RunOptions{ nullptr },
input_node_names_cstr.data(), &input_tensor, 1,
output_node_names_cstr.data(), output_node_names.size()
);

3.6 把现在的内容整合一下(这应该是编译成功的),看到那个output_tensors ,模型输出就在那里面

// yolov10示例2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define NOMINMAX
#include <iostream>
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <dml_provider_factory.h>
void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape,
bool autoShape, bool scaleFill, bool scaleUp, int stride, const cv::Scalar& color)
{ cv::Size shape = image.size();
float r = std::min((float)newShape.height / (float)shape.height,
(float)newShape.width / (float)shape.width);
if (!scaleUp)
r = std::min(r, 1.0f); float ratio[2]{ r, r };
int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) }; auto dw = (float)(newShape.width - new_un_pad[0]);
auto dh = (float)(newShape.height - new_un_pad[1]); if (autoShape)
{
dw = (float)((int)dw % stride);
dh = (float)((int)dh % stride);
}
else if (scaleFill)
{
dw = 0.0f;
dh = 0.0f;
new_un_pad[0] = newShape.width;
new_un_pad[1] = newShape.height;
ratio[0] = (float)newShape.width / (float)shape.width;
ratio[1] = (float)newShape.height / (float)shape.height;
} dw /= 2.0f;
dh /= 2.0f; if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
{
cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
}
else {
outImage = image.clone();
} int top = int(std::round(dh - 0.1f));
int bottom = int(std::round(dh + 0.1f));
int left = int(std::round(dw - 0.1f));
int right = int(std::round(dw + 0.1f));
params[0] = ratio[0];
params[1] = ratio[1];
params[2] = left;
params[3] = top;
cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
cv::imshow("1", outImage);
cv::waitKey(0);
} int main()
{
auto img = cv::imread("./zidane.jpg");
// 目标尺寸
int width = 640;
int height = 640;
const std::wstring& model_path = L"./yolov10n.onnx";
cv::Size newShape = cv::Size(width, height);
cv::Vec4d temp_param = { 1,1,0,0 };
cv::Scalar color = cv::Scalar(114, 114, 114);
cv::Mat outImage;
LetterBox(img, outImage, temp_param, newShape, false, false, true, 32, color);
Ort::SessionOptions session_options;
std::unique_ptr<Ort::Env> env;, 1.0 / 255.0
std::unique_ptr<Ort::Session> session;
std::unique_ptr<Ort::AllocatorWithDefaultOptions> allocator;
std::vector<float> input_tensor_values;
std::vector<std::string> input_node_names;
std::vector<std::string> output_node_names;
std::vector<const char*> input_node_names_cstr;
std::vector<const char*> output_node_names_cstr;
input_tensor_values.resize(3 * width * height);
env = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_ERROR, "YOLOv10Inference");
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); std::vector<std::string> providers = Ort::GetAvailableProviders();
auto cuda_it = std::find(providers.begin(), providers.end(), "CUDAExecutionProvider");
auto dml_it = std::find(providers.begin(), providers.end(), "DmlExecutionProvider"); if (cuda_it != providers.end())
{
std::cout << "CUDA Execution Provider is available" << std::endl;
}
else if (dml_it != providers.end())
{
std::cout << "DirectML Execution Provider is available" << std::endl;
OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0);
}
else
{
std::cout << "No suitable provider found, using default CPU execution" << std::endl;
} session = std::make_unique<Ort::Session>(*env, model_path.c_str(), session_options);
allocator = std::make_unique<Ort::AllocatorWithDefaultOptions>(); auto num_input_nodes = session->GetInputCount();
auto num_output_nodes = session->GetOutputCount();
input_node_names.resize(num_input_nodes);
output_node_names.resize(num_output_nodes); for (size_t i = 0; i < num_input_nodes; i++)
{
auto input_name = session->GetInputNameAllocated(i, *allocator);
input_node_names[i] = input_name.get();
input_node_names_cstr.push_back(input_node_names[i].c_str());
} for (size_t i = 0; i < num_output_nodes; i++)
{
auto output_name = session->GetOutputNameAllocated(i, *allocator);
output_node_names[i] = output_name.get();
output_node_names_cstr.push_back(output_node_names[i].c_str());
}
cv::Mat float_img;
std::vector<cv::Mat> channels(3);
outImage.convertTo(float_img, CV_32FC3, 1.0 / 255.0);
cv::split(float_img, channels);
for (int i = 0; i < 3; ++i)
{
std::memcpy(input_tensor_values.data() + i * width * height, channels[i].data, width * height * sizeof(float));
} std::vector<int64_t> input_node_dims = { 1, 3, height, width };
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info, input_tensor_values.data(), input_tensor_values.size(),
input_node_dims.data(), input_node_dims.size()
); auto output_tensors = session->Run(Ort::RunOptions{ nullptr },
input_node_names_cstr.data(), &input_tensor, 1,
output_node_names_cstr.data(), output_node_names.size()
);
return 0;
}

3.7 后处理咯,相当之简单,因为yolov10就是没后处理,他要做的就是根据缩放函数(letterbox),把坐标缩放回去

	auto output_tensors = session->Run(Ort::RunOptions{ nullptr },
input_node_names_cstr.data(), &input_tensor, 1,
output_node_names_cstr.data(), output_node_names.size()
);
const float* raw_output = output_tensors[0].GetTensorData<float>();
auto shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();
int64_t num_boxes = shape[1];
int64_t box_data_length = shape[2];
detections.clear();
for (int64_t i = 0; i < num_boxes; i++)
{
const float* detection = raw_output + i * box_data_length;
float x1 = detection[0];
float y1 = detection[1];
float x2 = detection[2];
float y2 = detection[3];
float confidence = detection[4];
int class_id = static_cast<int>(detection[5]); if (confidence > 0.8)
{ // Adjust coordinates to remove padding
float x = (x1 + 0.5 * (x2 - x1)-temp_param[2])/temp_param[0];
float y = (y1 + 0.5 * (y2 - y1) - temp_param[3]) / temp_param[1]; float w = (x2 - x1) / temp_param[0];
float h = (y2 - y1) / temp_param[1]; int left = MAX(int(x - 0.5 * w + 0.5), 0);
int top = MAX(int(y - 0.5 * h + 0.5), 0); cv::Rect rect(left, top, int(w + 0.5), int(h + 0.5));
detections.emplace_back(rect, confidence, class_id);
}
}

3.8 根据rect绘制方框

for (const auto& detection : detections)
{
const auto& [rect, confidence, class_id] = detection;
cv::rectangle(img, rect, cv::Scalar(0, 255, 0), 2);
}
cv::imshow("1", img);
cv::waitKey(0);

4. 最终代码和实现效果

实现效果,注意!!你使用的模型yolov10n.onnx,要和你的cpp文件在一个目录下

// yolov10示例2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define NOMINMAX
#include <iostream>
#include <opencv2/opencv.hpp>
#include <onnxruntime_cxx_api.h>
#include <dml_provider_factory.h>
void LetterBox(const cv::Mat& image, cv::Mat& outImage, cv::Vec4d& params, const cv::Size& newShape,const cv::Scalar& color)
{ cv::Size shape = image.size();
float r = std::min((float)newShape.height / (float)shape.height,
(float)newShape.width / (float)shape.width); float ratio[2]{ r, r };
int new_un_pad[2] = { (int)std::round((float)shape.width * r),(int)std::round((float)shape.height * r) }; auto dw = (float)(newShape.width - new_un_pad[0]);
auto dh = (float)(newShape.height - new_un_pad[1]); dw /= 2.0f;
dh /= 2.0f; if (shape.width != new_un_pad[0] && shape.height != new_un_pad[1])
{
cv::resize(image, outImage, cv::Size(new_un_pad[0], new_un_pad[1]));
}
else {
outImage = image.clone();
} int top = int(std::round(dh - 0.1f));
int bottom = int(std::round(dh + 0.1f));
int left = int(std::round(dw - 0.1f));
int right = int(std::round(dw + 0.1f));
params[0] = ratio[0];
params[1] = ratio[1];
params[2] = left;
params[3] = top;
cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
} int main()
{
auto img = cv::imread("./bus.jpg");
// 目标尺寸
int width = 640;
int height = 640;
const std::wstring& model_path = L"./yolov10n.onnx";
cv::Size newShape = cv::Size(width, height);
std::vector<std::tuple<cv::Rect, float, int>> detections;
cv::Vec4d temp_param = { 1,1,0,0 };
cv::Scalar color = cv::Scalar(114, 114, 114);
cv::Mat outImage;
LetterBox(img, outImage, temp_param, newShape, color);
Ort::SessionOptions session_options;
std::unique_ptr<Ort::Env> env;
std::unique_ptr<Ort::Session> session;
std::unique_ptr<Ort::AllocatorWithDefaultOptions> allocator;
std::vector<float> input_tensor_values;
std::vector<std::string> input_node_names;
std::vector<std::string> output_node_names;
std::vector<const char*> input_node_names_cstr;
std::vector<const char*> output_node_names_cstr;
input_tensor_values.resize(3 * width * height);
env = std::make_unique<Ort::Env>(ORT_LOGGING_LEVEL_ERROR, "YOLOv10Inference");
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(ORT_ENABLE_ALL); std::vector<std::string> providers = Ort::GetAvailableProviders();
auto cuda_it = std::find(providers.begin(), providers.end(), "CUDAExecutionProvider");
auto dml_it = std::find(providers.begin(), providers.end(), "DmlExecutionProvider"); if (cuda_it != providers.end())
{
std::cout << "CUDA Execution Provider is available" << std::endl;
}
else if (dml_it != providers.end())
{
std::cout << "DirectML Execution Provider is available" << std::endl;
OrtSessionOptionsAppendExecutionProvider_DML(session_options, 0);
}
else
{
std::cout << "No suitable provider found, using default CPU execution" << std::endl;
} session = std::make_unique<Ort::Session>(*env, model_path.c_str(), session_options);
allocator = std::make_unique<Ort::AllocatorWithDefaultOptions>(); auto num_input_nodes = session->GetInputCount();
auto num_output_nodes = session->GetOutputCount();
input_node_names.resize(num_input_nodes);
output_node_names.resize(num_output_nodes); for (size_t i = 0; i < num_input_nodes; i++)
{
auto input_name = session->GetInputNameAllocated(i, *allocator);
input_node_names[i] = input_name.get();
input_node_names_cstr.push_back(input_node_names[i].c_str());
} for (size_t i = 0; i < num_output_nodes; i++)
{
auto output_name = session->GetOutputNameAllocated(i, *allocator);
output_node_names[i] = output_name.get();
output_node_names_cstr.push_back(output_node_names[i].c_str());
}
cv::Mat float_img;
std::vector<cv::Mat> channels(3);
//cv::cvtColor(img, img, cv::COLOR_BGR2RGB); outImage.convertTo(float_img, CV_32FC3, 1.0 / 255.0); cv::split(float_img, channels);
for (int i = 0; i < 3; ++i)
{
std::memcpy(input_tensor_values.data() + i * width * height, channels[i].data, width * height * sizeof(float));
} std::vector<int64_t> input_node_dims = { 1, 3, height, width };
Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
memory_info, input_tensor_values.data(), input_tensor_values.size(),
input_node_dims.data(), input_node_dims.size()
); auto output_tensors = session->Run(Ort::RunOptions{ nullptr },
input_node_names_cstr.data(), &input_tensor, 1,
output_node_names_cstr.data(), output_node_names.size()
);
const float* raw_output = output_tensors[0].GetTensorData<float>();
auto shape = output_tensors[0].GetTensorTypeAndShapeInfo().GetShape();
int64_t num_boxes = shape[1];
int64_t box_data_length = shape[2];
detections.clear();
for (int64_t i = 0; i < num_boxes; i++)
{
const float* detection = raw_output + i * box_data_length;
float x1 = detection[0];
float y1 = detection[1];
float x2 = detection[2];
float y2 = detection[3];
float confidence = detection[4];
int class_id = static_cast<int>(detection[5]); if (confidence > 0.8)
{ // Adjust coordinates to remove padding
float x = (x1 + 0.5 * (x2 - x1)-temp_param[2])/temp_param[0];
float y = (y1 + 0.5 * (y2 - y1) - temp_param[3]) / temp_param[1]; float w = (x2 - x1) / temp_param[0];
float h = (y2 - y1) / temp_param[1]; int left = MAX(int(x - 0.5 * w + 0.5), 0);
int top = MAX(int(y - 0.5 * h + 0.5), 0); cv::Rect rect(left, top, int(w + 0.5), int(h + 0.5));
detections.emplace_back(rect, confidence, class_id);
}
} for (const auto& detection : detections)
{
const auto& [rect, confidence, class_id] = detection;
cv::rectangle(img, rect, cv::Scalar(0, 255, 0), 2);
}
cv::imshow("1", img);
cv::waitKey(0);
return 0;
}

可以优化的地方

  1. OpenCV的mat速度好像是有点慢,自己实现一个MAT用,速度和占用都能好不少
  2. 如果确定要推理某个尺寸的图片,直接训练那个尺寸的图片,然后可以去掉letterbox,速度又能快点
  3. 如果追求极致效率,请转去ncnn,都说是神中神,快去试试,回来告诉我

最后

闷了,写的时候搜个半天搜不到,AI写出来的都是老代码,onnxruntime1.18.1都不适用,希望这篇文章能帮到你,如果文章有问题也欢迎斧正

【C++】使用ort推理yolov10的更多相关文章

  1. 【推理引擎】ONNXRuntime 的架构设计

    ONNXRuntime,深度学习领域的神经网络模型推理框架,从名字中可以看出它和 ONNX 的关系:以 ONNX 模型作为中间表达(IR)的运行时(Runtime). 本文许多内容翻译于官方文档:ht ...

  2. 【推理引擎】从源码看ONNXRuntime的执行流程

    目录 前言 准备工作 构造 InferenceSession 对象 & 初始化 让模型 Run 总结 前言 在上一篇博客中:[推理引擎]ONNXRuntime 的架构设计,主要从文档上对ONN ...

  3. 利用 onnxruntime 库同时推理多个模型的效率研究

    1. 背景 需求:针对视频形式的数据输入,对每一帧图像,有多个神经网络模型需要进行推理并获得预测结果.如何让整个推理过程更加高效,尝试了几种不同的方案. 硬件:单显卡主机. 2. 方案 由于存在多个模 ...

  4. vb小菜一枚-----了解“类型推理”

    局部类型推理 (Visual Basic) Visual Studio 2013   其他版本   Visual Basic 编译器使用类型推理来确定未使用 As 子句声明的局部变量的数据类型. 编译 ...

  5. 机器学习&数据挖掘笔记_20(PGM练习四:图模型的精确推理)

    前言: 这次实验完成的是图模型的精确推理.exact inference分为2种,求边缘概率和求MAP,分别对应sum-product和max-sum算法.这次实验涉及到的知识点很多,不仅需要熟悉图模 ...

  6. 伪Acmer的推理(dfs/bfs)

    时间限制:1000MS  内存限制:65535K 提交次数:12 通过次数:9 收入:32 题型: 编程题   语言: C++;C Description 现在正是期末,在复习离散数学的Acmer遇到 ...

  7. IQ推理:P先生和Q先生

    P先生.Q先生具有足够的推理能力.这天,他们正在接受面试. 他们知道桌子的抽屉里有16张扑克牌:红桃 A Q 4黑桃 J 8 4 2 7 3草花 K Q 5 4 6方块 A 5约瀚教授从这16张牌中挑 ...

  8. 数论 - 简单数位推理 --- NYIST 514

    Problem's Link:http://acm.nyist.net/JudgeOnline/problem.php?pid=514 Mean: 给你一个l和r,求出在这个范围内的1的个数. ana ...

  9. Interview---一道有趣的推理题

    题目描述: 一个岛上有100个人,他们的眼睛只有两种颜色,蓝色和红色.95个人是黑色,其余5人是红色. 他们有个宗教信仰,从不照镜子,所以他们自己不知道自己的眼睛的颜色.但是能看到其他人的眼睛. 他们 ...

  10. 机器学习 —— 概率图模型(推理:MAP)

    MAP 是最大后验概率的缩写.后验概率指的是当有一定观测结果的情况下,对其他随机变量进行推理.假设随机变量的集合为X ,观察到的变量为 e, W = X-e , AP = P(W|e). 后验概率和联 ...

随机推荐

  1. Git:国内用命令行访问GitHub的方法

    1 直接改Hosts文件(现在不太管用了) 如果你是Linux或Mac系统,那么可以通过命令sudo vim /etc/hosts打开Hosts文件,并加入以下内容: 140.82.114.25 al ...

  2. Hello Laravel! 准备

    Hello Laravel! 准备 目录 Hello Laravel! 准备 什么是 Laravel? 为什么选择 Laravel? 优雅的语法 丰富的功能 强大的社区支持 安全性 易于扩展 Lara ...

  3. uniapp-vue3-oadmin手机后台实例|vite5.x+uniapp多端仿ios管理系统

    原创vue3+uniapp+uni-ui跨端仿ios桌面后台OA管理模板Uni-Vue3-WeOS. uniapp-vue3-os一款基于uni-app+vite5.x+pinia等技术开发的仿ios ...

  4. 从 p12 格式 SSL 证书解出 pem 格式公钥私钥给 Postman 使用

    目的 Postman 的(非 Chrome 扩展版的)app 并不能读取系统中的客户端证书,这种调试要发送客户端证书的 https 请求的时候就得自己另外设置.系统直接导出证书有 cer 和 p12 ...

  5. OpenOCD + DAP-LINK调试ESP32的失败经历

    目的 手里有调试STM32的DAP-LINK,想试试通过JTAG调试ESP32 OpenOCD支持CMSIS-DAP DAP-LINK支持的芯片,我手上这款描述如下,应该JTAG协议的都支持 平台 w ...

  6. 创建 Vue3 项目

    前提条件 安装 Nodejs 下载:https://nodejs.org/en/download/prebuilt-installer 切换 npm 源 npm config set registry ...

  7. 【论文笔记】ResNet深度残差网络

    [深度学习]总目录 深度残差网络(ResNet)由微软研究院的何恺明.张祥雨.任少卿.孙剑提出.研究动机是为了解决深度网络的退化问题,不同于过去的网络是通过学习去拟合一个分布,ResNet通过学习去拟 ...

  8. pyenv-win 替换国内镜像源

    前情提要 今天心血来潮想学一学python 然后因为python版本众多,了解到了pyenv这个python版本管理器 在github下载好pyenv以后,打算先安装一个稳定的版本 pyenv ins ...

  9. format( )函数

    在Python中,DETAIL_URL.format(id=id) 是一个字符串格式化的表达式.它通常用于根据一个已定义的字符串模板 DETAIL_URL 来生成一个新的字符串.在这个模板中,会包含一 ...

  10. 高通lk阶段mipi 代码解析以及新屏幕添加

    参考:https://www.cnblogs.com/linhaostudy/p/9237526.html 背景 前段时间做了这块的工作,但总结完以后领导说我的认识还是过于肤浅,因此重新再看了一下. ...