YOLOv8/YOLOv11 C++ OpenCV DNN推理

首先需要將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…