c++调用实现yolov5转onnx介绍

 更新时间:2021年12月22日 09:05:48   作者:小军军军军军军  
大家好,本篇文章主要讲的是c++调用实现yolov5转onnx介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览

介绍

现在很多开发都是需要用c++做最后一步的移植部署,手写吧,先不说你会不会浪费时间,网上找吧,问题千奇百怪,所以给大家出这篇文章,做雷锋教学,话不多说,开始

训练模型.pt转onnx

训练部分根据呼声再决定要不要写一份博客吧!!
注意事项:
1.训练代码一定要选择yolov5 5.0版本
2. 进入models/exprort.py;

请添加图片描述

3.将红框区域换成你自己的训练完的模型

请添加图片描述

4.将版本换成12;

请添加图片描述

5.直接运行即可,会生成出onnx的模型出来。

c++代码解析

博主使用的是opencv4.5.3版本,已经编译好的,需要直接扫码加我发你

main函数部分

请添加图片描述

读取模型利用的是dnn::readNet,opencv其实挺强大的博主已经读过tf模型,torch模型后续都会出对应博客,这里总共有三点改,输入图片path,输入类别名class_names,net部分改成自己的模型

net.set这些参数都固定就好,有兴趣的同学可以研究研究DNN_TARGET_CPU这个地方,是可以使用gpu和cuda的,但是博主还没复现过

推理部分讲解

void postprocess(cv::Mat& cv_src, std::vector<cv::Mat>& outs, const std::vector<std::string>& classes, int net_size)
{
	float confThreshold = 0.1f;
	float nmsThreshold = 0.1f;
	std::vector<int> classIds;
	std::vector<float> confidences;
	std::vector<cv::Rect> boxes;
	int strides[] = { 8, 16, 32 };
	std::vector<std::vector<int> > anchors =
	{
		{ 10,13, 16,30, 33,23 },
		{ 30,61, 62,45, 59,119 },
		{ 116,90, 156,198, 373,326 }
	};
	for (size_t k = 0; k < outs.size(); k++)
	{
		float* data = outs[k].ptr<float>();
		int stride = strides[k];
		int num_classes = outs[k].size[4] - 5;
		for (int i = 0; i < outs[k].size[2]; i++)
		{
			for (int j = 0; j < outs[k].size[3]; j++)
			{
				for (int a = 0; a < outs[k].size[1]; ++a)
				{
					float* record = data + a * outs[k].size[2] * outs[k].size[3] * outs[k].size[4] +
						i * outs[k].size[3] * outs[k].size[4] + j * outs[k].size[4];
					float* cls_ptr = record + 5;
					for (int cls = 0; cls < num_classes; cls++)
					{
						float score = sigmoid(cls_ptr[cls]) * sigmoid(record[4]);
						if (score > confThreshold)
						{
							float cx = (sigmoid(record[0]) * 2.f - 0.5f + (float)j) * (float)stride;
							float cy = (sigmoid(record[1]) * 2.f - 0.5f + (float)i) * (float)stride;
							float w = pow(sigmoid(record[2]) * 2.f, 2) * anchors[k][2 * a];
							float h = pow(sigmoid(record[3]) * 2.f, 2) * anchors[k][2 * a + 1];
							float x1 = std::max(0, std::min(cv_src.cols, int((cx - w / 2.f) * (float)cv_src.cols / (float)net_size)));
							float y1 = std::max(0, std::min(cv_src.rows, int((cy - h / 2.f) * (float)cv_src.rows / (float)net_size)));
							float x2 = std::max(0, std::min(cv_src.cols, int((cx + w / 2.f) * (float)cv_src.cols / (float)net_size)));
							float y2 = std::max(0, std::min(cv_src.rows, int((cy + h / 2.f) * (float)cv_src.rows / (float)net_size)));
							classIds.push_back(cls);
							confidences.push_back(score);
							boxes.push_back(cv::Rect(cv::Point(x1, y1), cv::Point(x2, y2)));
						}
					}
				}
			}
		}
	}
	std::vector<int> indices;
	cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
	for (size_t i = 0; i < indices.size(); i++)
	{
		int idx = indices[i];
		cv::Rect box = boxes[idx];
		drawPred(classIds[idx], confidences[idx], box.x, box.y,
			box.x + box.width, box.y + box.height, cv_src, classes);
	}
}

抬头部分是两大目标检测的阈值设置,然后anchors这些都不建议动,除非你在训练的时候用了你自己生成的anchors的话,就改成你自己的,然后根据训练推理后,会生成我们所对应的坐标框以及分数,将分数和狂送到容器中,dnn中有nms等底层函数哦
cv::dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
对应输入就可以了,然后得到我们的Box,index,和置信度,接下来就到了我们的画图环节。

darpred部分

void drawPred(int classId, float conf, int left, int top, int right, int bottom, cv::Mat& frame,
	const std::vector<std::string>& classes)
{
	cv::rectangle(frame, cv::Point(left, top), cv::Point(right, bottom), cv::Scalar(0, 255, 0), 3);
	std::string label = cv::format("%.2f", conf);
	if (!classes.empty()) {
		CV_Assert(classId < (int)classes.size());
		label = classes[classId] + ": " + label;
	}
	int baseLine;
	cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
	top = std::max(top, labelSize.height);
	cv::rectangle(frame, cv::Point(left, top - round(1.5 * labelSize.height)), cv::Point(left + round(1.5 * labelSize.width), top + baseLine), cv::Scalar(0, 255, 0), cv::FILLED);
	cv::putText(frame, label, cv::Point(left, top), cv::FONT_HERSHEY_SIMPLEX, 0.75, cv::Scalar(), 2);
}

sigmod部分

inline float sigmoid(float x)
{
	return 1.f / (1.f + exp(-x));
}

结尾

到此这篇关于c++调用实现yolov5转onnx介绍的文章就介绍到这了,更多相关c++ yolov5转onnx内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言 如何求两整数的最大公约数与最小公倍数

    C语言 如何求两整数的最大公约数与最小公倍数

    这篇文章主要介绍了C语言中如何求两整数的最大公约数与最小公倍数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 详解C语言之文件操作下)

    详解C语言之文件操作下)

    这篇文章主要介绍了关于C语言文件操作方法的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助
    2021-11-11
  • C++中std::thread线程用法

    C++中std::thread线程用法

    本文主要介绍了C++中std::thread线程用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • C++ class和struct到底有什么区别详解

    C++ class和struct到底有什么区别详解

    这篇文章主要介绍了C++ class和struct到底有什么区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • C++中不能被重载的运算符介绍

    C++中不能被重载的运算符介绍

    其实在C/C++ 里大多数运算符都可以在C++中被重载的。C 的运算符中只有 . 和 ?:(以及 sizeof,技术上可以看作一个运算符)不可以被重载
    2013-10-10
  • C语言如何实现顺序表(数据结构)

    C语言如何实现顺序表(数据结构)

    这篇文章主要介绍了C语言如何实现顺序表(数据结构)问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 浅析C语言中sscanf 的用法

    浅析C语言中sscanf 的用法

    以下是对C语言中sscanf函数的使用方法进行了详细的分析介绍,需要的朋友参考下
    2013-07-07
  • C++矩阵运算的实现简单

    C++矩阵运算的实现简单

    本文主要介绍了C++矩阵运算的实现简单,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • C++简单又好用的基本运算符重载

    C++简单又好用的基本运算符重载

    继友元知识过后,就到了今天的C++运算符重载的内容了,运算符重载是C++里比较重要的内容。这篇博文不会一下子讲完各种运算符重载,因为太多了了也不好吸收掌握,所以运算符重载我准备分多次记录和分享,那么接下来进入正文
    2022-06-06
  • C++ const的各种用法详解

    C++ const的各种用法详解

    const名叫常量限定符,用来限定特定变量,以通知编译器该变量是不可修改的。习惯性的使用const,可以避免在函数中对某些不应修改的变量造成可能的改动。本文主要谈谈const的用法,感兴趣的同学可以参考阅读
    2023-04-04

最新评论