首先需要将yolov8/yolov11的pt文件转为onnx文件

from ultralytics import YOLO
model = YOLO("best.pt")
model.export(format="onnx",opset=11,dynamic=False)

本次C++工具使用vs2017,需要下载OpenCV包:https://opencv.org/releases/,下在windows包即可,本次代码opencv4.7.0和opencv4.8.0均正常运行,下载好后跟着下面的步骤进行配置。

#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <chrono>
#include <fstream>using namespace cv;
using namespace cv::dnn;
using namespace std;
using namespace chrono;class YOLO {
private:float confidenceThreshold;float iouThreshold;Net net;int inputHeight;int inputWidth;vector<string> classes;vector<Scalar> colors;// 初始化类别void initClasses() {classes = { "black", "cue", "solid", "stripe" };//填入你需要的类别}// 生成随机颜色void initColors() {random_device rd;mt19937 gen(rd());uniform_int_distribution<int> dist(0, 255);for (size_t i = 0; i < classes.size(); ++i) {colors.push_back(Scalar(dist(gen), dist(gen), dist(gen)));}}public:// 构造函数YOLO(const string& onnxModelPath, float confThreshold = 0.5f, float iouThreshold = 0.5f): confidenceThreshold(confThreshold), iouThreshold(iouThreshold),inputHeight(640), inputWidth(640) { //默认640,640try {// 加载模型net = readNetFromONNX(onnxModelPath);if (net.empty()) {throw runtime_error("无法加载ONNX模型: " + onnxModelPath);}// 设置计算后端和目标设备net.setPreferableBackend(DNN_BACKEND_OPENCV);net.setPreferableTarget(DNN_TARGET_CPU);// 初始化类别和颜色initClasses();initColors();// 打印网络信息vector<String> layerNames = net.getLayerNames();vector<String> outputNames = net.getUnconnectedOutLayersNames();cout << "模型加载成功!" << endl;cout << "输入尺寸: " << inputWidth << "x" << inputHeight << endl;cout << "网络层数: " << layerNames.size() << endl;cout << "输出层数: " << outputNames.size() << endl;for (size_t i = 0; i < outputNames.size(); i++) {cout << "输出层[" << i << "]: " << outputNames[i] << endl;}}catch (const Exception& e) {cerr << "初始化YOLOv8失败: " << e.what() << endl;throw;}}// 预处理图像Mat preprocess(const Mat& image) {Mat blob;// 创建blob,BGR->RGB,归一化到[0,1]blobFromImage(image, blob, 1.0 / 255.0, Size(inputWidth, inputHeight), Scalar(), true, false, CV_32F);return blob;}// 输出张量信息用于调试void printTensorInfo(const Mat& tensor, const string& name) {cout << name << " 信息:" << endl;cout << "  维度: " << tensor.dims << endl;cout << "  形状: [";for (int i = 0; i < tensor.dims; i++) {cout << tensor.size[i];if (i < tensor.dims - 1) cout << ", ";}cout << "]" << endl;cout << "  类型: " << tensor.type() << endl;cout << "  总元素数: " << tensor.total() << endl;}// 后处理void postprocess(const Mat& image, const vector<Mat>& outputs,vector<Rect>& boxes, vector<float>& confidences, vector<int>& classIds) {boxes.clear();confidences.clear();classIds.clear();if (outputs.empty()) {cerr << "错误: 模型输出为空" << endl;return;}int imageHeight = image.rows;int imageWidth = image.cols;// 打印所有输出的信息for (size_t i = 0; i < outputs.size(); i++) {printTensorInfo(outputs[i], "输出[" + to_string(i) + "]");}// 获取第一个输出Mat output = outputs[0];// 确保输出是浮点型if (output.type() != CV_32F) {output.convertTo(output, CV_32F);}int numClasses = classes.size();int numDetections = 0;int featuresPerDetection = 0;// 处理不同维度的输出Mat processedOutput;if (output.dims == 3) {// 3维输出: [batch, features, detections] 或 [batch, detections, features]int dim1 = output.size[1];int dim2 = output.size[2];cout << "处理3维输出: [" << output.size[0] << ", " << dim1 << ", " << dim2 << "]" << endl;// 判断格式if (dim1 == numClasses + 4) {// 格式: [1, 8, 8400] -> 转换为 [8400, 8]numDetections = dim2;featuresPerDetection = dim1;processedOutput = Mat::zeros(numDetections, featuresPerDetection, CV_32F);// 手动转置数据for (int i = 0; i < numDetections; i++) {for (int j = 0; j < featuresPerDetection; j++) {// 安全地访问3D张量数据const float* data = output.ptr<float>(0);int index = j * numDetections + i;processedOutput.at<float>(i, j) = data[index];}}}else if (dim2 == numClasses + 4) {// 格式: [1, 8400, 8] -> 直接重塑为 [8400, 8]numDetections = dim1;featuresPerDetection = dim2;// 创建2D视图processedOutput = Mat(numDetections, featuresPerDetection, CV_32F,(void*)output.ptr<float>(0));}else {cerr << "无法识别的3D输出格式" << endl;return;}}else if (output.dims == 2) {// 2维输出: [detections, features]cout << "处理2维输出: [" << output.size[0] << ", " << output.size[1] << "]" << endl;numDetections = output.size[0];featuresPerDetection = output.size[1];processedOutput = output;}else {cerr << "不支持的输出维度: " << output.dims << endl;return;}cout << "处理格式: " << numDetections << " 个检测, 每个 " << featuresPerDetection << " 个特征" << endl;// 检查特征数量是否正确if (featuresPerDetection != numClasses + 4) {cerr << "警告: 特征数量(" << featuresPerDetection << ")与期望值(" << numClasses + 4 << ")不匹配" << endl;}float x_factor = float(imageWidth) / float(inputWidth);float y_factor = float(imageHeight) / float(inputHeight);// 处理每个检测for (int i = 0; i < numDetections; ++i) {const float* detection = processedOutput.ptr<float>(i);// 前4个值是边界框坐标 [cx, cy, w, h]float cx = detection[0];float cy = detection[1];float w = detection[2];float h = detection[3];// 找到最高分的类别float maxScore = 0;int classId = -1;int availableClasses = min(numClasses, featuresPerDetection - 4);for (int j = 0; j < availableClasses; ++j) {float score = detection[4 + j];if (score > maxScore) {maxScore = score;classId = j;}}// 过滤低置信度if (maxScore > confidenceThreshold && classId >= 0 && classId < numClasses) {// 转换坐标:中心点坐标转换为左上角坐标float x1 = (cx - w / 2) * x_factor;float y1 = (cy - h / 2) * y_factor;float width = w * x_factor;float height = h * y_factor;// 确保边界框在图像范围内x1 = max(0.0f, x1);y1 = max(0.0f, y1);width = min(width, float(imageWidth) - x1);height = min(height, float(imageHeight) - y1);if (width > 0 && height > 0) {boxes.push_back(Rect(int(x1), int(y1), int(width), int(height)));confidences.push_back(maxScore);classIds.push_back(classId);}}}cout << "NMS前检测到 " << boxes.size() << " 个候选框" << endl;// 非极大值抑制vector<int> indices;if (!boxes.empty()) {NMSBoxes(boxes, confidences, confidenceThreshold, iouThreshold, indices);}// 应用NMS结果vector<Rect> tempBoxes;vector<float> tempConfidences;vector<int> tempClassIds;for (int i : indices) {tempBoxes.push_back(boxes[i]);tempConfidences.push_back(confidences[i]);tempClassIds.push_back(classIds[i]);}boxes = tempBoxes;confidences = tempConfidences;classIds = tempClassIds;cout << "NMS后保留 " << boxes.size() << " 个检测框" << endl;}// 绘制检测结果void drawDetections(Mat& image, const vector<Rect>& boxes,const vector<float>& confidences, const vector<int>& classIds) {for (size_t i = 0; i < boxes.size(); ++i) {Rect box = boxes[i];int classId = classIds[i];if (classId >= 0 && classId < colors.size()) {Scalar color = colors[classId];// 绘制边界框rectangle(image, box, color, 2);// 绘制类别和置信度string label = classes[classId] + ": " +to_string(int(confidences[i] * 100)) + "%";// 计算文本尺寸int baseline;Size textSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseline);// 绘制文本背景rectangle(image,Point(box.x, box.y - textSize.height - 10),Point(box.x + textSize.width, box.y),color, FILLED);// 绘制文本putText(image, label, Point(box.x, box.y - 5),FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 255), 1);}}}// 执行检测 void detect(Mat& image, Mat& resultImage,vector<Rect>& boxes, vector<float>& confidences, vector<int>& classIds) {try {// 预处理cout << "开始预处理..." << endl;Mat blob = preprocess(image);cout << "预处理完成: [" << blob.size[0] << ", " << blob.size[1]<< ", " << blob.size[2] << ", " << blob.size[3] << "]" << endl;// 设置输入net.setInput(blob);// 方法1: 使用简单的forward()方法cout << "开始推理(方法1)..." << endl;auto start = high_resolution_clock::now();try {Mat output = net.forward();auto end = high_resolution_clock::now();vector<Mat> outputs;outputs.push_back(output);// 计算推理时间duration<double> inferenceTime = end - start;cout << "推理完成,耗时: " << inferenceTime.count() * 1000 << " 毫秒" << endl;// 后处理cout << "开始后处理..." << endl;postprocess(image, outputs, boxes, confidences, classIds);}catch (const Exception& e1) {cout << "方法1失败: " << e1.what() << endl;// 方法2: 使用指定输出层名称的forward()方法cout << "尝试方法2..." << endl;try {vector<String> outputNames = net.getUnconnectedOutLayersNames();if (!outputNames.empty()) {cout << "使用输出层: " << outputNames[0] << endl;start = high_resolution_clock::now();vector<Mat> outputs;net.forward(outputs, outputNames);auto end = high_resolution_clock::now();duration<double> inferenceTime = end - start;cout << "推理完成,耗时: " << inferenceTime.count() * 1000 << " 毫秒" << endl;postprocess(image, outputs, boxes, confidences, classIds);}else {throw runtime_error("无法获取输出层名称");}}catch (const Exception& e2) {cout << "方法2也失败: " << e2.what() << endl;// 方法3: 使用所有输出层cout << "尝试方法3..." << endl;vector<int> outLayerIds = net.getUnconnectedOutLayers();vector<String> layerNames = net.getLayerNames();vector<String> outLayerNames;for (int id : outLayerIds) {outLayerNames.push_back(layerNames[id - 1]);}start = high_resolution_clock::now();vector<Mat> outputs;net.forward(outputs, outLayerNames);auto end = high_resolution_clock::now();duration<double> inferenceTime = end - start;cout << "推理完成,耗时: " << inferenceTime.count() * 1000 << " 毫秒" << endl;postprocess(image, outputs, boxes, confidences, classIds);}}// 绘制结果resultImage = image.clone();drawDetections(resultImage, boxes, confidences, classIds);cout << "最终检测到 " << boxes.size() << " 个目标" << endl;}catch (const Exception& e) {cerr << "检测过程中出错: " << e.what() << endl;resultImage = image.clone();}}
};int main() {try {// 模型和图像路径string onnxModelPath = "yolov8.onnx";//填入你需要的onnx权重文件string imagePath = "test.jpg";//测试图片// 检查文件是否存在ifstream modelFile(onnxModelPath);if (!modelFile.good()) {cerr << "错误: 找不到模型文件 " << onnxModelPath << endl;return -1;}// 初始化YOLOv8模型cout << "初始化YOLOv8模型..." << endl;YOLO yolo(onnxModelPath, 0.5f, 0.4f);// 读取图像Mat image = imread(imagePath);if (image.empty()) {cerr << "无法读取图像: " << imagePath << endl;return -1;}cout << "图像尺寸: " << image.cols << "x" << image.rows << endl;// 执行检测Mat resultImage;vector<Rect> boxes;vector<float> confidences;vector<int> classIds;yolo.detect(image, resultImage, boxes, confidences, classIds);// 显示结果if (!resultImage.empty()) {imshow("YOLOv8 Detection", resultImage);cout << "按任意键继续..." << endl;waitKey(0);// 保存结果imwrite("result.jpg", resultImage);cout << "检测结果已保存为 result.jpg" << endl;}destroyAllWindows();return 0;}catch (const exception& e) {cerr << "程序异常: " << e.what() << endl;return -1;}
}

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Mysql】日志--错误日志、二进制日志、查询日志、慢查询日志

错误日志:数据库出现错误时&#xff0c;进行故障排除默认位置&#xff1a;/var/log/mysqld.log查看日志位置show variables like %log_error%查看日志tail -50 /var/log/mysqld.log二进制日志&#xff1a;记录了所有的DDL语句和DML语句&#xff0c;不包含查询&#xff08;selec…

后端常用框架环境与软件详解

一、基础运行环境 1. JDK&#xff08;Java Development Kit&#xff09; 定义&#xff1a;Java 开发工具包&#xff0c;包含编译器、运行时环境&#xff08;JRE&#xff09;及核心类库 作用&#xff1a;提供 Java 程序开发和运行的基础环境&#xff0c;是所有 Java 应用的必备依…

本地服务器端部署基于大模型的通用OCR项目——dots.ocr

本地服务器端部署基于大模型的通用OCR项目——dots.ocrdots.ocr相关介绍本地服务器端部署第一步&#xff1a;安装cuda12.8与CUDNN8.9.7第二步&#xff1a;创建项目所需的依赖环境第三步&#xff1a;启动项目第四步&#xff1a;测试第五步&#xff1a;文本解析相关性测试第六步&…

Text2SQL 智能问答系统开发-spider验证集(三)

概述 已完成 基础 Text2SQL 功能实现 实现用户输入自然语言问题后&#xff0c;系统能够自动生成 SQL 并执行返回结果。用户交互优化 支持用户通过补充信息对查询进行调整&#xff0c;提升易用性。模糊时间处理机制 对“最近”“近期”等模糊时间关键词进行补全或引导&#xf…

ElementUI常用的组件展示

文章目录1、要使用ElementUI先导入组件库2、自定义表头&#xff0c;可以改为添加和批量删除的按钮3、Dialog模态框&#xff0c;主要用于添加和修改时展示信息4、抽屉5、消息提示&#xff1a;用于提示是否操作成功6、询问&#xff1a;常用于询问是否确定删除7、批量选择复选框8、…

在电脑上可以存储文件并合理备份文件的工具用哪个?

每天被群消息、报表、PPT 轮番轰炸的上班族&#xff0c;最怕的不是加班&#xff0c;而是——文件突然失踪&#xff01;别再把“CtrlS”当护身符&#xff0c;今天一口气测完 4 款热门“文件保险箱”&#xff0c;看看谁才真正配得上你的 Deadline。 敬业签 首先登场的是敬业签&am…

JavaWeb(04)

MyBatis 时一款优秀的持久层框架&#xff0c;用于简化JDBC的开发 The MyBatis Blog 目录 MyBatis入门Mybatis基础CRUDMybatis动态SQL Mybatis入门 快速入门 JDBC介绍 数据库连接池 lombok 准备工作(创建springboot工程&#xff0c;数据库表user&#xff0c;实体类User) …

统计学1:伯努利模型的参数估计与等价性分析

伯努利模型的参数估计方法 1. 统计学习方法三要素对比方法模型策略算法极大似然估计概率模型经验风险最小化数值解贝叶斯估计概率模型结构风险最小化解析解2. 极大似然估计 2.1 模型设定 设P(x1)θP(x1)\thetaP(x1)θ&#xff0c;则P(x0)1−θP(x0)1-\thetaP(x0)1−θ 2.2 似然…

游戏行业DDoS攻防实战指南

一、游戏DDoS攻击特征分析游戏行业DDoS攻击呈现高度复合化特征&#xff0c;攻击手段日益专业化。2023年Akamai监测数据显示&#xff0c;63%的游戏服务器攻击采用UDP反射放大&#xff08;如NTP、Memcached协议&#xff09;与HTTP慢速攻击&#xff08;如Slowloris&#xff09;相结…

[自动化Adapt] 录制引擎 | iframe 穿透 | NTP | AIOSQLite | 数据分片

链接&#xff1a;https://github.com/OpenAdaptAI/OpenAdapt/wiki/OpenAdapt-Architecture-(draft) docs&#xff1a;OpenAdapt OpenAdapt 是一个开源项目&#xff0c;旨在 记录 和 回放 用户在计算机上的交互行为。 它如同智能助手般 观察 我们的操作&#xff08;鼠标点击、…

ipv6学习

ipv6的历史背景和及展望ipv6普及不够&#xff0c;ipv4快要用完。ipv6技术部分ivp6包头结构ipv6不允许分片&#xff0c;减轻中间设备压力。IPv6 包头结构可按字段分层解析&#xff0c;核心特点是 固定头部长度&#xff08;40 字节&#xff09; &#xff0c;将可选功能移至扩展头…

软件定义汽车 --- 电子电气架构的驱动

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…

HTML5 语义元素

HTML5 语义元素 引言 HTML5 作为现代网页开发的基础&#xff0c;引入了许多新的语义元素&#xff0c;这些元素使得网页内容更加结构化&#xff0c;便于搜索引擎更好地理解和索引页面内容。本文将详细介绍 HTML5 中的语义元素&#xff0c;并探讨其在网页设计中的应用。 HTML5…

vue3 el-select el-option 使用

在 Vue 3 中&#xff0c;el-select 是 Element Plus 组件库中的一个选择器组件&#xff0c;它允许用户从下拉菜单中选择一个或多个选项。如果你想在使用 Vue 3 和 Element Plus 时让 el-select 支持多种选择&#xff08;即多选&#xff09;&#xff0c;你可以通过设置 multiple…

windows搬运文件脚本

使用方法&#xff1a;copy_files_by_prefix.bat [目标目录] [结果目录] [文件名前缀] [可选参数&#xff1a;文件包含内容]echo off chcp 65001 >nul setlocal enabledelayedexpansion:: Check parameters if "%~3""" (echo Usage: %~nx0 [SourceDir] […

C++ 中 initializer_list 类型推导

在 C 中&#xff0c;initializer_list 是一种用于表示列表初始化的标准库模板类&#xff0c;提供了一种方便的方式来初始化容器或者进行函数调用时传递一组参数。initializer_list&& 类型推导涉及到右值引用和移动语义&#xff0c;这在现代 C 中变得越来越重要。initia…

自动驾驶中的传感器技术22——Camera(13)

1、可靠性验证的目标车载摄像头作为自动驾驶和高级驾驶辅助系统&#xff08;ADAS&#xff09;的核心传感器&#xff0c;其可靠性直接影响到行车安全。可靠性验证的目标如下&#xff1a;暴露产品缺陷&#xff1a;在研制阶段&#xff0c;通过测试发现并修正产品设计中的问题&…

一周学会Matplotlib3 Python 数据可视化-图形的组成部分

锋哥原创的Matplotlib3 Python数据可视化视频教程&#xff1a; 2026版 Matplotlib3 Python 数据可视化 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程讲解利用python进行数据可视化 科研绘图-Matplotlib&#xff0c;学习Matplotlib图形参数基本设置&…

三万字带你了解那些年面过的Java八股文

Java基础 1. String 和StringBuffer 和 StringBuilder的区别&#xff1f; String 字符串常量 StringBuffer 字符串变量&#xff08;线程安全&#xff09; StringBuilder 字符串变量&#xff08;非线程安全&#xff09; 2. sleep() 区间wait()区间有什么区别&#xff1f; sleep…

HTML 媒体元素概述

HTML 提供了多种元素用于嵌入和控制多媒体内容&#xff0c;包括音频、视频、图像、画布等。以下是常用的 HTML 媒体元素及其用法&#xff1a;音频 (<audio>)<audio> 元素用于嵌入音频内容&#xff0c;支持 MP3、WAV、OGG 等格式。 示例代码&#xff1a;<audio c…