基于OpenCV的YOLOv5图片检测

news2024/11/15 23:41:13

利用OpenCV的DNN模块加载onnx模型文件进行图片检测。

1、使用的yolov5工程代码,调用export.py导出onnx模型。
2、下载opencv版本,https://opencv.org/releases/
      使用opencv版本4.5.3或以上,本文使用的opencv4.6.0        
3、使用vc2015编写使用代码。

// dnnUseOnnx.cpp : 定义控制台应用程序的入口点。

#include <fstream>
#include <iostream>
#include <string>
#include <map>
#include <opencv2/opencv.hpp>

struct DetectResult
{
	int classId;
	float score;
	cv::Rect box;
};

class YOLOv5Detector
{
public:
	void initConfig(std::string onnxpath, int iw, int ih, float threshold, bool bIsEnableCuda);
	void detect(cv::Mat& frame, std::vector<DetectResult>& result);

private:
	int input_w = 640;
	int input_h = 640;
	cv::dnn::Net net;
	int threshold_score = 0.25;
};

void YOLOv5Detector::initConfig(std::string onnxpath, int iw, int ih, float threshold, bool bIsEnableCuda)
{
	this->input_w = iw;
	this->input_h = ih;
	this->threshold_score = threshold;
	try
	{
		this->net = cv::dnn::readNetFromONNX(onnxpath);

		//依据情况选定是否使用CUDA
		if (bIsEnableCuda)
		{
			std::cout << "Attempty to use CUDA\n";
			net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
			net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
		}
		else
		{
			std::cout << "Running on CPU\n";
			net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
			net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
		}
	}
	catch (cv::Exception & e) {
		printf("exception %s\n", e.err.c_str());
	}
}

void YOLOv5Detector::detect(cv::Mat& frame, std::vector<DetectResult>& results)
{
	// 图象预处理 - 格式化操作
	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);
	if (frame.channels() == 1)
	{
		cv::cvtColor(frame, frame, cv::COLOR_GRAY2BGR);
	}
	cv::Rect roi(0, 0, w, h);
	frame.copyTo(image(roi));

	float x_factor = image.cols / 640.0f;
	float y_factor = image.rows / 640.0f;

	cv::Mat blob = cv::dnn::blobFromImage(image, 1 / 255.0, cv::Size(this->input_w, this->input_h), cv::Scalar(0, 0, 0),
		true, false);
	this->net.setInput(blob);
	cv::Mat preds = this->net.forward("output0");//outputname,使用Netron看一下输出的名字,一般为output0或者output
	//如果preds里有Mat的维数大于2,那么设断点调试的时候,可以看到rows和cols都等于-1,当Mat的dims>2时,想要访问Mat的高和宽,可以通过size属性获取。如下:
	printf("output:%d,%d,%d\n", preds.size[0], preds.size[1], preds.size[2]);//打印输出:output:1,25200,85
	//YOLOV5的输出1,25200,85如何理解和解析
	//1、25200代表着检测框的数量,比如我们取出第一个检测框a,也就是[1,1,85],取出来之后我们解析85,前五个为box的中点坐标、长宽值以及置信,后面80我们取Max(80个类别)中最大值,类别的处于多少行对应于label class.txt别中的类是哪一类别。
	cv::Mat det_output(preds.size[1], preds.size[2], CV_32F, preds.ptr<float>());

	float confidence_threshold = 0.5;
	std::vector<cv::Rect> boxes;
	boxes.clear();
	std::vector<int> classIds;
	classIds.clear();
	std::vector<float> confidences;
	confidences.clear();
	for (int i = 0; i < det_output.rows; i++)
	{
		float confidence = det_output.at<float>(i, 4);
		if (confidence < 0.45)
		{
			continue;
		}
		cv::Mat classes_scores = det_output.row(i).colRange(5, preds.size[2]);//colRange(5, num_class);num_class:最大分类数
		cv::Point classIdPoint;
		double score;
		minMaxLoc(classes_scores, 0, &score, 0, &classIdPoint);

		// 置信度 0~1之间
		if (score > this->threshold_score)
		{
			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 * confidence);
		}
	}

	// NMS
	std::vector<int> indexes;
	cv::dnn::NMSBoxes(boxes, confidences, 0.25, 0.45, indexes);
	for (size_t i = 0; i < indexes.size(); i++)
	{
		DetectResult dr;
		int index = indexes[i];
		int idx = classIds[index];
		dr.box = boxes[index];
		dr.classId = idx;
		dr.score = confidences[index];
		cv::rectangle(frame, boxes[index], cv::Scalar(0, 0, 255), 2, 8);
		cv::rectangle(frame, cv::Point(boxes[index].tl().x, boxes[index].tl().y - 20),
			cv::Point(boxes[index].br().x, boxes[index].tl().y), cv::Scalar(0, 255, 255), -1);
		results.push_back(dr);
	}


	std::ostringstream ss;
	std::vector<double> layersTimings;
	double freq = cv::getTickFrequency() / 1000.0;
	double time = net.getPerfProfile(layersTimings) / freq;
	ss << "FPS: " << 1000 / time << " ; time : " << time << " ms";
	putText(frame, ss.str(), cv::Point(20, 40), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(255, 0, 0), 2, 8);
}

std::map<int, std::string> classNames = { { 0, "person" },{ 1, "bicycle" },{ 2, "car" },{ 3, "motorcycle" } ,{ 4, "airplane" } ,{ 5, "bus" },{ 6, "train" },{ 7, "truck" },{ 8, "boat" },{ 9, "traffic light" },
{ 10, "fire hydrant" },{ 11, "stop sign'" },{ 12, "parking meter" },{ 13, "bench" } ,{ 14, "bird" } ,{ 15, "cat" },{ 16, "dog" },{ 17, "horse" },{ 18, "sheep" },{ 19, "cow" }, 
{ 20, "elephant" },{ 21, "bear" },{ 22, "zebra" },{ 23, "giraffe" } ,{ 24, "backpack" } ,{ 25, "umbrella" },{ 26, "handbag" },{ 27, "tie" },{ 28, "suitcase" },{ 29, "frisbee" },
{ 30, "skis" },{ 31, "snowboard" },{ 32, "sports ball" },{ 33, "kite" } ,{ 34, "baseball bat" } ,{ 35, "baseball glove" },{ 36, "skateboard" },{ 37, "surfboard" },{ 38, "tennis racket" },{ 39, "bottle" },
{ 40, "wine glass" },{ 41, "cup" },{ 42, "fork" },{ 43, "knife" } ,{ 44, "spoon" } ,{ 45, "bowl" },{ 46, "banana" },{ 47, "apple" },{ 48, "sandwich" },{ 49, "orange" },
{ 50, "broccoli" },{ 51, "carrot" },{ 52, "hot dog" },{ 53, "pizza" } ,{ 54, "donut" } ,{ 55, "cake" },{ 56, "chair" },{ 57, "couch" },{ 58, "potted plant" },{ 59, "bed" },
{ 60, "dining table" },{ 61, "toilet" },{ 62, "tv" },{ 63, "laptop" } ,{ 64, "mouse" } ,{ 65, "remote" },{ 66, "keyboard" },{ 67, "cell phone" },{ 68, "microwave" },{ 69, "oven" },
{ 70, "toaster" },{ 71, "sink" },{ 72, "refrigerator" },{ 73, "book" } ,{ 74, "clock" } ,{ 75, "vase" },{ 76, "scissors" },{ 77, "teddy bear" },{ 78, "hair drier" },{ 79, "toothbrush" }
};

int main(int argc, char* argv[])
{
	std::shared_ptr<YOLOv5Detector> detector = std::make_shared<YOLOv5Detector>();
	detector->initConfig(R"(D:\python-project\yolov5\yolov5s.onnx)", 640, 640, 0.25f, false);

	cv::Mat frame = cv::imread(R"(D:\python-project\yolov5\data\images\bus.jpg)");

	std::vector<DetectResult> results;
	detector->detect(frame, results);
	for (DetectResult& dr : results)
	{
		cv::Rect box = dr.box;
		cv::putText(frame, classNames[dr.classId]+ " "+ std::to_string(dr.score), cv::Point(box.tl().x, box.tl().y - 10), cv::FONT_HERSHEY_SIMPLEX,
			.5, cv::Scalar(0, 0, 0));
	}
	cv::imshow("OpenCV-DNN-yolov5", frame);
	cv::waitKey();
	results.clear();
}

运行效果:

注意事项:

1)、readNetFromONNX加载onnx模型出错。 interp_mode != "asymmetric",这个错误信息表明你在使用OpenCV的readNetFromONNX函数加载ONNX模型时,模型中的某些节点的插值模式(interp_mode)不是"asymmetric"。
解决方法:使用opencv版本4.5.3或以上,本文使用的opencv4.6.0
2)、监测到目标比yolov5工程detect.py推理出来的目标少。
解决方法:det_output.row(i).colRange(5, num_class);num_class:最大分类数

cv::Mat classes_scores = det_output.row(i).colRange(5, preds.size[2]);

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

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

相关文章

4.使用 VSCode 过程中的英语积累 - View 菜单(每一次重点积累 5 个单词)

前言 学习可以不局限于传统的书籍和课堂&#xff0c;各种生活的元素也都可以做为我们的学习对象&#xff0c;本文将利用 VSCode 页面上的各种英文元素来做英语的积累&#xff0c;如此做有 3 大利 这些软件在我们工作中是时时刻刻接触的&#xff0c;借此做英语积累再合适不过&a…

STM32 使用 CubeMX 实现按键外部中断

目录 问题背景知识参考需要改什么注意尽量不要在中断函数使用 循环函数做延时中断函数中延时方法调试 问题 我想实现按钮触发紧急停止类似功能&#xff0c;需要使用按键中断功能。 背景知识 GPIO 点亮 LED。stm32cubemx hal学习记录&#xff1a;GPIO输入输出。STM32—HAL库 …

【实战篇】MySQL是怎么保证高可用的?

背景 在一个主备关系中&#xff0c;每个备库接收主库的 binlog 并执行。正常情况下&#xff0c;只要主库执行更新生成的所有 binlog&#xff0c;都可以传到备库并被正确地执行&#xff0c;备库就能达到跟主库一致的状态&#xff0c;这就是最终一致性。 但是&#xff0c;MySQL…

免费在线压缩pdf 压缩pdf在线免费 推荐简单好用

压缩pdf在线免费&#xff1f;在日常生活和工作学习中&#xff0c;处理PDF文件是常见任务。但有时PDF文件体积较大&#xff0c;给传输、存储和分享带来不便。因此&#xff0c;学习PDF文件压缩技巧十分必要。压缩PDF文件是指通过技术手段减小文件占用的存储空间&#xff0c;同时尽…

[Redis][Hash]详细讲解

目录 0.前言1.常见命令1.HSET2.HGET3.HEXISTS4.HDEL5.HKEYS6.HVALS7.HGETALL8.HMGET9.HLEN10.HSETNX11.HINCRBY12.HINCRBYFLOAT 2.内部编码1.ziplist(压缩链表)2.hashtable(哈希表) 3.使用场景4.缓存方式对比1.原⽣字符串类型2.序列化字符串类型3.哈希类型 0.前言 在Redis中&am…

CSS - 通用左边图片,右边内容,并且控制长度溢出处理模板(vue | uniapp | 微信小程序)

前言 通用模板&#xff0c;可适用于任意前端项目。 如下图所示&#xff0c;手机电脑通用。 示例代码 根据自己的需求修改即可。 <body><div class"container"><!-- 头像图片 --><img class"avatar" src"https://cdn.uviewui.com…

C++初阶学习——探索STL奥秘——标准库中的priority_queue与模拟实现

1.priority_queque的介绍 1.priority_queue中文叫优先级队列。优先队列是一种容器适配器&#xff0c;根据严格的弱排序标准&#xff0c;它的第一个元素总是它所包含的元素中最大的。 2. 此上下文类似于堆&#xff0c;在堆中可以随时插入元素&#xff0c;并且只能检索最大堆元…

学习大数据DAY59 全量抽取和增量抽取实战

目录 需求流程&#xff1a; 需求分析与规范 作业 作业2 需求流程&#xff1a; 全量抽取 增量抽取 - DataX Kettle Sqoop ... 场景: 业务部门同事或者甲方的工作人员给我们的部门经理和你提出了新的需 求 流程: 联系 > 开会讨论 > 确认需求 > 落地 需求文档( 具体…

Vue 项目中引入 Axios 详解

Vue 项目中引入 Axios 详解 在 Vue 项目中&#xff0c;axios 是一个非常流行的 HTTP 客户端&#xff0c;用于向服务器发送请求并处理响应。本文将详细说明如何在 Vue 项目中引入 Axios 插件&#xff0c;以及如何进行基本的配置&#xff0c;包括构建、配置域名、设置全局错误拦…

WEB攻防-JS项目Node.js框架安全识别审计验证绕过

知识点&#xff1a; 1、原生JS&开发框架-安全条件 2、常见安全问题-前端验证&未授权 详细点&#xff1a; 1、什么是JS渗透测试&#xff1f; 在JavaScript中也存在变量和函数&#xff0c;当存在可控变量及函数调用即可参数漏洞 2、流行的Js框架有哪些&#xff1f; …

CC1链的第二种方式-LazyMap版调用链

文章目录 CC1链的第二种方式-LazyMap版调用链LazyMap构造payloadCC1的调用链 CC1链的第二种方式-LazyMap版调用链 CC1链的第一种方式可以参考另一篇文章&#xff1a;CC1链_全网最菜的分析思路 LazyMap 在之前的CC1链中分析&#xff0c;其实是其中一种方式&#xff08;国内版本…

全面解析流量态势感知与网络性能监控:IT运维中的核心技术

在现代IT运维中&#xff0c;网络的稳定性和业务的连续性是企业赖以生存的基石。随着数字化转型的深入&#xff0c;网络流量日益复杂&#xff0c;安全威胁愈加严峻&#xff0c;运维人员不仅需要确保网络的顺畅运行&#xff0c;还必须及时发现潜在风险并快速响应。流量态势感知与…

如何查看Android设备的dpi

adb shell getprop ro.sf.lcd_density adb shell cat /system/build.prop > build_prop.txt shell cat system/build.prop 结果&#xff1a;参考&#xff1a; 如何查看Android设备的dpi_安卓 查看手机dpi-CSDN博客

ABAP-Swagger 一种公开 ABAP REST 服务的方法

ABAP-Swagger An approach to expose ABAP REST services 一种公开 ABAP REST 服务的方法 Usage 1: develop a class in ABAP with public methods 2: implement interface ZIF_SWAG_HANDLER, and register the public methods(example method zif_swag_handler~meta) 3: …

ElementUI 用span-method实现循环el-table组件的合并行功能

需要把指定列的相同数据合并起来&#xff08;项目中用的是updateTime&#xff09; 后端返回的数据格式&#xff1a; html&#xff1a; <el-tab-pane label"执行记录概览" name"fourth" v-loading"loading"><el-timeline v-if"re…

单元测试和unittest框架(超详细总结)

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;薪资嘎嘎涨 单元测试的定义 1. 什么是单元测试&#xff1f; 单元测试是指&#xff0c;对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作&am…

电子烟智能化创新体验:WTK6900P语音交互芯片方案,融合频谱计算、精准语音识别与流畅音频播报

一&#xff1a;开发背景 在这个科技日新月异的时代&#xff0c;每一个细节的创新都是对传统的一次超越。今天&#xff0c;我们自豪地宣布一项革命性的融合——将先进的语音识别技术与电子烟相结合&#xff0c;通过WTK6900P芯片的卓越性能&#xff0c;为您开启前所未有的个性化…

【有啥问啥】摄像头成像质量量化标准解读与测试方法

摄像头成像质量量化标准解读与测试方法 在自动驾驶和智能驾驶舱领域&#xff0c;摄像头是关键的感知设备&#xff0c;直接关系到系统的环境感知能力。为确保摄像头在实际应用中表现出色&#xff0c;需明确了解其成像质量标准和测试方法。本文将围绕成像质量的核心指标、测试方…

【机器学习(九)】分类和回归任务-多层感知机 (MLP) -Sentosa_DSML社区版

文章目录 一、算法概念二、算法原理&#xff08;一&#xff09;感知机&#xff08;二&#xff09;多层感知机1、隐藏层2、激活函数sigma函数tanh函数ReLU函数 3、反向传播算法 三、算法优缺点&#xff08;一&#xff09;优点&#xff08;二&#xff09;缺点 四、MLP分类任务实现…

PCL addLine可视化K近邻

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 本文将介绍如何使用PCL库中…