yolov8-seg通过onnxruntime部署-c++

一、

本文引用博主的方法进行了二次封装,使模型加载和预测进行分开,更方便后续工程的调用。https://blog.youkuaiyun.com/zhenzhidemaoyi/article/details/135168405?ops_request_misc=&request_id=&biz_id=102&utm_term=yolov8-seg%E5%88%A9%E7%94%A8onnxruntime%E6%8E%A8%E7%90%86&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-135168405.142^v102^pc_search_result_base1&spm=1018.2226.3001.4187

 二、环境配置

opencv使用的是4.5.2版本,解压到固定文件夹后,加入环境变量

onnxruntime-gpu使用的是 1.15.1,(如果使用GPU加速的话建议下载onnxruntime-gpu版本,个人建议直接下载GPU版本的(如果想使用GPU加速的话一定要控制cuda和cudnn的版本具体可可以借鉴说明),因为GPU版本也兼容CPU运行,而基础版本只可以使用CPU不可以使用GPU)需要的去网址上下载固定版本即可(onnxruntime版本一定大于等于你转换成onnx模型的版本,否则会出现识别不出模型,而报错)

onnxruntime版本安装可以借鉴

https://blog.youkuaiyun.com/caip12999203000/article/details/125516627?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170193457416800180627787%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170193457416800180627787&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-4-125516627-null-null.142%5Ev96%5Epc_search_result_base9&utm_term=VS%20onnxruntime&spm=1018.2226.3001.4187

E:\projects\opencv\build\include
E:\projects\opencv\build\include\opencv2
E:\YOLOdll\onnxruntime-win-x64-gpu-1.15.0\include

E:\projects\opencv\build\x64\vc15\lib
E:\YOLOdll\onnxruntime-win-x64-gpu-1.15.0\lib

opencv_world452.lib
onnxruntime.lib
onnxruntime_providers_cuda.lib
onnxruntime_providers_shared.lib
onnxruntime_providers_tensorrt.lib

全部代码如下:

三、

yolo.h

#pragma once
#include <opencv2/opencv.hpp>
#include "utils.h"
#include <string>
#include <onnxruntime_cxx_api.h>
#include <onnxruntime_c_api.h>
#include "cpu_provider_factory.h"




class YOLO
{
public:

	void init(std::string engine_path);
	void detect_img(cv::Mat& frame, cv::Mat& mask);
	


private:
	int input_h = 0;//模型的输入维度
	int input_w = 0;
	Ort::Env env;                        // 环境对象作为成员变量
	Ort::SessionOptions session_options;
	Ort::Session session_{ nullptr };       // 会话对象作为成员变量(初始化为空)
	std::vector<std::string> input_node_names;
	std::vector<std::string> output_node_names;

	
};

yolo.cpp 

#include"yolo.h"
#include<vector>

using namespace cv;
std::string labels_txt_file = "E:\\YOLOdll\\yolo-cpu\\x64\\Release\\classes.txt";
void YOLO::init(std::string onnxpath)
{
	//std::string onnxpath = "E:\\YOLOdll\\best.onnx";
	std::wstring modelPath = std::wstring(onnxpath.begin(), onnxpath.end());
	std::cout << "Attempting to load model from: " << onnxpath << std::endl;



	env = Ort::Env(ORT_LOGGING_LEVEL_ERROR, "yolov8seg-onnx");

	session_options.SetGraphOptimizationLevel(ORT_ENABLE_BASIC);//启动图形优化 加快运算速度

	bool isCuda = true;
	std::vector<std::string> available_providers = Ort::GetAvailableProviders();

	std::cout << "Available providers: ";
	for (const auto& provider : available_providers) {
		std::cout << provider << "\n";
	}
	std::cout << std::endl;

	auto cuda_available = std::find(available_providers.begin(), available_providers.end(), "CUDAExecutionProvider");

	if (isCuda && (cuda_available == available_providers.end()))
	{
		std::cout << "Your ORT build without GPU. Change to CPU." << std::endl;
		std::cout << "************* Infer model on CPU! *************" << std::endl;
	}
	else if (isCuda && (cuda_available != available_providers.end()))
	{
		std::cout << "************* Infer model on GPU! *************" << std::endl;
		//#if ORT_API_VERSION < ORT_OLD_VISON
		OrtCUDAProviderOptions cudaOption;
		cudaOption.device_id = 0;
		session_options.AppendExecutionProvider_CUDA(cudaOption);//第一种使用GPU的方法
		//#else
		//	OrtSessionOptionsAppendExecutionProvider_CUDA(session_options, 0);
		//#endif
	}
	else
	{
		std::cout << "************* Infer model on CPU! *************" << std::endl;
		OrtSessionOptionsAppendExecutionProvider_CPU(session_options, 0);//CPU版本
	}


	session_ = Ort::Session(env, modelPath.c_str(), session_options);
	std::wcout << L"Model loaded successfully." << std::endl;

	// get input and output info
	int input_nodes_num = session_.GetInputCount();
	int output_nodes_num = session_.GetOutputCount();
	//std::vector<std::string> input_node_names;
	//std::vector<std::string> output_node_names;
	Ort::AllocatorWithDefaultOptions allocator;


	// query input data format
	for (int i = 0; i < input_nodes_num; i++) {
		auto input_name = session_.GetInputNameAllocated(i, allocator);
		input_node_names.push_back(input_name.get());
		auto inputShapeInfo = session_.GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape();
		int ch = inputShapeInfo[1];
		input_h = inputShapeInfo[2];
		input_w = inputShapeInfo[3];
		std::cout << "input format: " << "channls" << ch << "x:" << input_h << "y:" << input_w << std::endl;
	}

	// query output data format, 25200x85
	//int num = 116;
	//int nc = 8400;
	for (int i = 0; i < output_nodes_num; i++) {
		auto output_name = session_.GetOutputNameAllocated(i, allocator);
		output_node_names.push_back(output_name.get());
		auto outShapeInfo = session_.GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape();
		//std::cout << "output format: " << (CLASSES + 4 + _segChannels) << "x" << Num_box << std::endl;
	}

}




float sigmoid_function(float a)
{
	float b = 1. / (1. + exp(-a));
	return b;
}

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;
}

void YOLO::detect_img(cv::Mat& frame, cv::Mat& mask) {

	if (frame.empty()) {
		return;
	}

	cv::RNG rng; //生成随机数
	float x_factor = 0.0;
	float y_factor = 0.0;



	std::vector<std::string> labels = readClassNames();


	// 图象预处理 - 格式化操作
	int64 start = cv::getTickCount();
	int w = frame.cols;
	int h = frame.rows;
	int _max = std::max(h, w);
	cv::Mat image = cv::Mat::zeros(cv::Size(_max, _max), CV_8UC3);
	cv::Rect roi(0, 0, w, h);
	frame.copyTo(image(roi));
	//x_factor = image.cols / static_cast<float>(640);
	//y_factor = image.rows / static_cast<float>(640);
	x_factor = image.cols / static_cast<float>(input_w);
	y_factor = image.rows / static_cast<float>(input_h);

	cv::Mat blob = cv::dnn::blobFromImage(image, 1.0 / 255.0, cv::Size(input_w, input_h), cv::Scalar(0, 0, 0), true, false);
	size_t tpixels = input_h * input_w * 3;
	std::array<int64_t, 4> input_shape_info{ 1, 3, input_h, input_w };

	// set input data and inference
	auto allocator_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
	Ort::Value input_tensor_ = Ort::Value::CreateTensor<float>(allocator_info, blob.ptr<float>(), tpixels, input_shape_info.data(), input_shape_info.size());
	const std::array<const char*, 1> inputNames = { input_node_names[0].c_str() };
	const std::array<const char*, 2> outNames = { output_node_names[0].c_str(), output_node_names[1].c_str() };

	std::vector<Ort::Value> ort_outputs;
	try {
		ort_outputs = session_.Run(Ort::RunOptions{ nullptr }, inputNames.data(), &input_tensor_, 1, outNames.data(), outNames.size());
	}
	catch (std::exception e) {
		std::cout << e.what() << std::endl;
	}
	// 116x8400, 1x32x160x160
	const float* pdata = ort_outputs[0].GetTensorMutableData<float>();
	const float* mdata = ort_outputs[1].GetTensorMutableData<float>();

	auto data_detect_shape = ort_outputs[0].GetTensorTypeAndShapeInfo().GetShape();
	auto data_proto_shape = ort_outputs[1].GetTensorTypeAndShapeInfo().GetShape();

	std::cout << "output 0 format: " << data_detect_shape[1] << "x" << data_detect_shape[2] << std::endl;
	std::cout << "output 1 format: " << "_segChannels:" << data_proto_shape[1] << "_segWidth:" << data_proto_shape[2] << "_segHeigh:" << data_proto_shape[3] << std::endl;

	// 后处理, 1x116x8400, 84 - box, 80- min/max, 32 feature
	std::vector<cv::Rect> boxes;
	std::vector<int> classIds;
	std::vector<float> confidences;
	std::vector<cv::Mat> masks;
	cv::Mat dout(int(data_detect_shape[1]), int(data_detect_shape[2]), CV_32F, (float*)pdata);
	cv::Mat det_output = dout.t(); // 116x8400 => 8400x116
	cv::Mat mask1(int(data_proto_shape[1]), int(data_proto_shape[2])*int(data_proto_shape[3]), CV_32F, (float*)mdata);

	float sx = float(data_proto_shape[2]) / float(input_w);
	float sy = float(data_proto_shape[3]) / float(input_h);




	for (int i = 0; i < det_output.rows; i++) {
		cv::Mat classes_scores = det_output.row(i).colRange(4, int(data_detect_shape[1]) - int(data_proto_shape[1]));
		cv::Point classIdPoint;
		double score;
		minMaxLoc(classes_scores, 0, &score, 0, &classIdPoint);

		if (score > 0.25)
		{
			cv::Mat mask2 = det_output.row(i).colRange(int(data_detect_shape[1]) - int(data_proto_shape[1]), int(data_detect_shape[1]));
			float cx = det_output.at<float>(i, 0);
			float cy = det_output.at<float>(i, 1);
			float ow = det_output.at<float>(i, 2);
			float oh = det_output.at<float>(i, 3);
			int x = static_cast<int>((cx - 0.5 * ow) * x_factor);
			int y = static_cast<int>((cy - 0.5 * oh) * y_factor);
			int width = static_cast<int>(ow * x_factor);
			int height = static_cast<int>(oh * y_factor);
			cv::Rect box;
			box.x = x;
			box.y = y;
			box.width = width;
			box.height = height;

			boxes.push_back(box);
			classIds.push_back(classIdPoint.x);
			confidences.push_back(score);
			masks.push_back(mask2);
		}
	}

	std::vector<int> indexes;
	cv::dnn::NMSBoxes(boxes, confidences, 0.25, 0.45, indexes);
	cv::Mat rgb_mask = cv::Mat::zeros(frame.size(), frame.type());

	// 显示
	for (size_t i = 0; i < indexes.size(); i++) {
		int idx = indexes[i];
		int cid = classIds[idx];
		cv::Rect box = boxes[idx];
		int x1 = std::max(0, box.x);
		int y1 = std::max(0, box.y);
		int x2 = std::max(0, box.br().x);
		int y2 = std::max(0, box.br().y);
		cv::Mat m2 = masks[idx];
		cv::Mat m = m2 * mask1;
		for (int col = 0; col < m.cols; col++) {
			m.at<float>(0, col) = sigmoid_function(m.at<float>(0, col));
		}
		//cv::Mat m1 = m.reshape(1, 160);
		cv::Mat m1 = m.reshape(1, { int(data_proto_shape[2]), int(data_proto_shape[3]) });
		int mx1 = std::max(0, int((x1 * sx) / x_factor));
		int mx2 = std::max(0, int((x2 * sx) / x_factor));
		int my1 = std::max(0, int((y1 * sy) / y_factor));
		int my2 = std::max(0, int((y2 * sy) / y_factor));


		if (mx2 >= m1.cols) {
			mx2 = m1.cols - 1;
		}
		if (my2 >= m1.rows) {
			my2 = m1.rows - 1;
		}


		cv::Mat mask_roi = m1(cv::Range(my1, my2), cv::Range(mx1, mx2));
		cv::Mat rm, det_mask;
		cv::resize(mask_roi, rm, cv::Size(x2 - x1, y2 - y1));
		for (int r = 0; r < rm.rows; r++) {
			for (int c = 0; c < rm.cols; c++) {
				float pv = rm.at<float>(r, c);
				if (pv > 0.5) {
					rm.at<float>(r, c) = 1.0;
				}
				else {
					rm.at<float>(r, c) = 0.0;
				}
			}
		}
		rm = rm * rng.uniform(0, 255);
		rm.convertTo(det_mask, CV_8UC1);
		if ((y1 + det_mask.rows) >= frame.rows) {
			y2 = frame.rows - 1;
		}
		if ((x1 + det_mask.cols) >= frame.cols) {
			x2 = frame.cols - 1;
		}
		// std::cout << "x1: " << x1 << " x2:" << x2 << " y1: " << y1 << " y2: " << y2 << std::endl;
		//cv::Mat mask = cv::Mat::zeros(cv::Size(frame.cols, frame.rows), CV_8UC1);
		det_mask(cv::Range(0, y2 - y1), cv::Range(0, x2 - x1)).copyTo(mask(cv::Range(y1, y2), cv::Range(x1, x2)));
		add(rgb_mask, cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), rgb_mask, mask);

		//cv::rectangle(frame, boxes[idx], cv::Scalar(0, 0, 255), 2, 8, 0);
		putText(frame, labels[cid].c_str(), boxes[idx].tl(), cv::FONT_HERSHEY_PLAIN, 1.0, cv::Scalar(255, 0, 0), 1, 8);
	}


	float t = (cv::getTickCount() - start) / static_cast<float>(cv::getTickFrequency());
	putText(frame, cv::format("FPS: %.2f", 1.0 / t), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);

	//cv::Mat result;
	//cv::addWeighted(frame, 0.5, rgb_mask, 0.5, 0, result);
	//result.copyTo(frame);

	//cv::Mat ori = frame.clone();
	//cv::namedWindow("ori", 0);
	//cv::imshow("ori", ori);
	//cv::namedWindow("seg", 0);
	//cv::imshow("seg", frame);
	//cv::imwrite("E:\\YOLOdll\\seg.bmp", frame);
	//cv::waitKey(0);

	session_options.release();
	session_.release();


}

test.cpp

#include <iostream>
#include"yolo.h"
#include <onnxruntime_cxx_api.h>
#include <onnxruntime_c_api.h>

int main()
{
	std::string model_path = "E:\\YOLOdll\\best.onnx";


	YOLO* yolo = new YOLO();
	yolo->init(model_path);


	cv::Mat img = cv::imread("E:\\YOLOdll\\1.bmp");
	//cv::imwrite("E:\\YOLOdll\\crop_Image.bmp", crop_Image);

	//创建一个与裁剪后的图像一样大小的全黑图像
	//cv::Mat mask = cv::Mat(img.rows, img.cols, img.type(), cv::Scalar(0, 0, 0));
	cv::Mat mask = cv::Mat::zeros(cv::Size(img.cols, img.rows), CV_8UC1);

	//yolo预测 将掩码绘制到mask上
	yolo->detect_img(img, mask);
	cv::imwrite("E:\\YOLOdll\\mask2.bmp", mask);

	return 0;
}


四、一些注意事项

1、需要将onnxruntime的几个dll放到对应的exe目录下

2、如果使用GPU加速的话需要将cuda、cudnn(前文已经说过加速的注意事项,可见标题二)的两个dll放到exe目录下

3、默认将opencv对应的dll放到exe目录下

<think>好的,我现在需要回答用户关于YOLOv11-segYOLOv11-detect之间的区别,特别是功能实现方面的差异。首先,我需要回忆YOLO系列模型的基本知识,尤其是分割(segmentation)和检测(detection)任务的不同之处。 用户提到的YOLOv11-segYOLOv11-detect,可能是指同一版本YOLO的不同变体,分别用于分割和检测。检测任务通常是识别图像中的物体并给出边界框和类别,而分割任务则需要更精确的像素级分类,即每个像素属于哪个物体或类别。因此,两者的模型结构可能在输出层或头部设计上有所不同。 接下来,我需要参考用户提供的引用内容。引用[1]提到了导出YOLO模型为ONNX格式,可能涉及模型的结构和输出。引用[2]讨论了使用RKNN进行部署,说明两者都需要优化以适应硬件,但具体的结构差异可能影响部署时的处理方式。例如,分割模型可能输出更多的数据(如掩膜),需要不同的后处理步骤。 然后,我需要详细比较两者的功能实现。检测模型通常输出边界框、类别置信度和类别概率,而分割模型除此之外还会输出每个实例的掩膜。因此,YOLOv11-seg可能在检测头的基础上添加了掩膜预测分支,或者使用不同的解码方式。例如,YOLO的实例分割模型如YOLACT或YOLOv5-seg,通常会在检测的基础上通过原型掩膜和系数矩阵生成实例分割结果。 另外,训练数据方面,检测模型需要边界框标注,而分割模型需要像素级的掩膜标注。这可能导致两者的训练过程和损失函数不同,分割模型的损失可能包括掩膜的交叉熵损失或Dice损失等。 用户还可能关心性能和应用场景。分割模型由于需要处理更多数据,可能计算量更大,推理速度较慢,适合需要精确轮廓的应用,如医学图像分析或自动驾驶中的场景理解。而检测模型更适用于实时性要求高的场景,如视频监控或移动端应用。 最后,我需要确保回答结构清晰,符合用户要求,使用正确的LaTeX格式,并生成相关问题。同时,引用用户提供的两个引用,但注意引用内容是否直接相关。引用[2]提到的是姿态估计的部署,可能与分割和检测的差异无直接关系,但可以说明不同任务模型在部署时的优化需求类似。需要确认引用是否适用于当前问题,或是否需要调整引用标注。</think>### YOLOv11-segYOLOv11-detect的功能实现差异 YOLOv11-seg(实例分割模型)和YOLOv11-detect(目标检测模型)在功能实现上的核心区别在于**任务目标与输出形式**。以下是具体分析: --- #### 1. **任务目标** - **YOLOv11-detect** 专注于**目标检测**,即定位图像中的物体并输出其边界框(Bounding Box)、类别标签及置信度。例如,在图像中标注“狗”的位置和类别。 **数学表示**:输出形式为$B = (x, y, w, h, c)$,其中$(x, y)$为中心坐标,$(w, h)$为宽高,$c$为类别概率。 - **YOLOv11-seg** 在检测基础上增加**实例分割**功能,需额外生成每个物体的像素级掩膜(Mask)。例如,在标注“狗”的边界框时,还需精确标记其轮廓的每个像素[^1]。 **数学表示**:输出包含掩膜矩阵$M \in \mathbb{R}^{H \times W}$,其中$H \times W$为图像分辨率,值表示像素是否属于目标。 --- #### 2. **模型结构差异** - **检测头部(Detection Head)** - **YOLOv11-detect**:通过卷积层直接预测边界框坐标和类别概率。 - **YOLOv11-seg**:在检测头部基础上增加**掩膜预测分支**,通常结合原型掩膜(Prototype Masks)和掩膜系数矩阵生成实例分割结果[^1]。例如: $$M = \sigma(P \cdot C)$$ 其中$P$为原型掩膜,$C$为检测头输出的系数矩阵,$\sigma$为激活函数。 - **后处理** - **YOLOv11-detect**:仅需非极大值抑制(NMS)筛选重叠框。 - **YOLOv11-seg**:需对掩膜进行阈值化和轮廓优化,计算量更大。 --- #### 3. **训练数据与损失函数** - **训练数据** - 检测模型:依赖标注的边界框(COCO等数据集)。 - 分割模型:需额外掩膜标注(如COCO的`segmentation`字段)。 - **损失函数** - 检测模型:边界框损失(如CIoU)、分类损失(交叉熵)。 - 分割模型:需叠加掩膜损失(如Dice Loss或二进制交叉熵)。 --- #### 4. **应用场景与性能** - **YOLOv11-detect** 适用于实时性要求高的场景,如视频监控、自动驾驶中的障碍物检测。 **优点**:推理速度快,资源占用低。 - **YOLOv11-seg** 适合需要精确物体轮廓的任务,如医学图像分析、机器人抓取。 **缺点**:计算复杂度高,需更多显存和算力[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值