實現效果:
Yolov11環境搭建(搭建好的可以直接跳過)
最好使用Anconda進行包管理,安裝可參考【文章】。下面簡單過一下如何快速部署環境。如果搭建過或可以參考其他文章可以跳過Yolo11環境搭建這一章節。總體來說Yolov11環境搭建越來越容易了。
Anconda創建環境
- 打開Anaconda prompt
- 使用下面命令創建新環境,這里指定python版本為3.11.9。不一定使用這個版本的python,3.10、3.9、3.12都可以。具體請看【詳細說明】,這里展示了部分需求Install the?
ultralytics
?package, including all?requirements, in a?Python>=3.8?environment with?PyTorch>=1.8## 注意我在Anconda配置了環境了安裝位置,如果沒有配置的可能創建的環境是在C盤,如果沒有設置這里創建環境時候需要指定環境的路徑## 不指定Python版本(默認是比較新的Pyhton版本) conda create --name 環境名 #示例,創建一個名為yolov11的環境: conda create --name yolov11## 指定python版本 conda create --name 環境名 python=環境版本 # 示例,創建一個名為yolov11的環境,并指定python版本為3.11.9 conda create --name yolov11 python=3.11.9
- 切換到自己剛才創建的環境中
## 指令 conda activate 環境名 # 不清楚的可以使用下面指令列出所有環境 conda env list## 示例,激活剛才創建的環境yolov11 conda activate yolov11
- 安裝ultralytics,詳細請參考【ultralytics官網快速入門教程】
## 直接安裝ultralytics,默認最新版本的,也可以指定版本,這里使用最新的 pip install ultralytics## 上面安裝的有一點慢,可以指定國內源,這里使用清華源 pip install ultralytics -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
- 安裝過程中的警告(這個警告是多打了some-package導致的,正常沒有,可以跳過)
DEPRECATION: Building 'some-package' using the legacy setup.py bdist_wheel mechanism, which will be removed in a future version. pip 25.3 will enforce this behaviour change. A possible replacement is to use the standardized build interface by setting the `--use-pep517` option, (possibly combined with `--no-build-isolation`), or adding a `pyproject.toml` file to the source tree of 'some-package'. Discussion can be found at https://github.com/pypa/pip/issues/6334##含義:你正在安裝的包(如some-package)使用了舊的setup.py bdist_wheel方式構建 wheel 包,這種方式將在未來版本中被移除。從 pip 25.3 版本開始,將強制要求使用新的構建標準(PEP 517/518),舊方式可能導致安裝失敗。## 臨時解決: pip install some-package --use-pep517## 這里沒有使用,不影響正常使用
- 上面會默認安裝Pytorch,但是只能使用CPU。如果只想使用CPU進行訓練,至此,所有環境配置完成。
- 卸載安裝ultralytics是默認安裝的torchvision,因為下面安裝CUDA版本的Pytorch不會卸載torchvision,后面訓練和使用模型的時候會報錯。
pip uninstall torchvision##如果不卸載安裝的話會報錯,后面使用代碼會報錯: NotImplementedError: Could not run 'torchvision::nms' with arguments from the 'CUDA' backend. This could be because the operator doesn't exist for this backenden...
- 安裝Pytorch GPU版本,查看支持的最高的CUDA版本
## 查看支持最高的CUDA版本,使用下面指令(這里需要在開一個終端,使用Win+R快捷鍵,輸入cmd就可以打開) nvidia-smi## 不出意外得到下面結果 +-----------------------------------------------------------------------------------------+ | NVIDIA-SMI 576.80 Driver Version: 576.80 CUDA Version: 12.9 | |-----------------------------------------+------------------------+----------------------+ | GPU Name Driver-Model | Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |=========================================+========================+======================| # 可以看到這里支持的最高的CUDA版本為12.9。如果想要顯卡支持比較高的CUDA版本,需要更新顯卡驅動就可以。
- 打開【Pytorch官網】,如下
- 可以看到我最高支持的為12.9版本,所以12.9以下的都可以,這里安裝12.6。安裝完成后環境就配置完成了。
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
?下載Yolov11源碼
?github上鏈接在這里【yolov11源碼】,好像最近不用魔法很難登進去,也有可能是我網絡的原因。
測試
這里使用pycharm打開下載后的源碼,配置Python環境為上面conda創建的環境。如下:
編寫代碼測試一下環境,在ultralytics-main目錄下創建python文件名為env_verification.py。其他目錄下注意修改圖片的目錄,這里使用的是自帶的目錄。注意yolo11n.pt是在工程文件內容沒有的,代碼會自行下載,如果下載不成功需要自己下載,移植到根目錄下。
from ultralytics import YOLO
import cv2# Load a pretrained YOLO11n model
model = YOLO("yolo11n.pt")# Perform object detection on an image
results = model("ultralytics/assets/") # Predict on an image# 逐幀顯示
for result in results:annotated_frame = result.plot()# 使用OpenCV顯示cv2.imshow("YOLOv11 Result", annotated_frame)cv2.waitKey(0) # 等待按鍵,0表示無限等待cv2.destroyAllWindows()
訓練自己的模型(檢測)
準備好訓練數據
和之前yolo系列沒有什么太大區別。第一找開源的數據集,第二種自己標注。
這里使用自己標注,可以使用軟件(labelimg)或者【標注網站】,這里使用網站makesense,可以自行搜索使用方法,標注完成后導出。格式如下,上面是文件,下面是文件內容。
?建立訓練文件夾
在ultralytics-main新建datasets文件夾,文件夾內容如下:
images文件夾下存放圖片,labels文件夾下存放標簽,train文件夾下存放訓練數據,val是驗證集,注意文件夾圖片和標簽是一一對應的。
新建訓練Python文件
在根目錄ultralytics-main下新建train.py文件,內容如下:
from ultralytics import YOLOif __name__ == '__main__':# Load a modelmodel = YOLO("yolo11.yaml") # build a new model from YAMLmodel = YOLO("yolo11n.pt") # load a pretrained model (recommended for training)model = YOLO("yolo11.yaml").load("yolo11n.pt") # build from YAML and transfer weights# Train the modelresults = model.train(data="coco128.yaml", epochs=100, imgsz=640, batch=8)
注意有兩個文件復制到根目錄下,并需要進行修改。一個是yolo11.yaml(在目錄下:ultralytics/cfg/models/11/yolo11.yaml),一個是coco128.yaml(在目錄下:ultralytics/cfg/datasets/coco128.yaml),之后需要修改文件內容:
## yolo11.yaml文件nc: 2 # 原文件第8行,修改檢測的類別個數,看自己標簽打了多少個這里就改為多少
## cocol28.yaml文件# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license# COCO128 dataset https://www.kaggle.com/datasets/ultralytics/coco128 (first 128 images from COCO train2017) by Ultralytics
# Documentation: https://docs.ultralytics.com/datasets/detect/coco/
# Example usage: yolo train data=coco128.yaml
# parent
# ├── ultralytics
# └── datasets
# └── coco128 ← downloads here (7 MB)# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: D:/Python_Project/yolov11/ultralytics-main/datasets # 數據集的根目錄,最好用絕對路徑
train: images/train # 訓練圖片在的目錄,和上面的path需要組成一個完整的目錄,當然也可以直接寫成絕對路徑
val: images/val # 驗證集圖片
test: # test images (optional)# Classes 類別,這個是2,冒號右邊是檢測框上面會出現的標識,注意和自己打好的標簽相互對應
names:0: circle1: rect# Download script/URL (optional)
#download: https://github.com/ultralytics/assets/releases/download/v0.0.0/coco128.zip
?運行就可以進行訓練了,最后訓練完成后會顯示存儲位置:Results saved to runs\detect\train27
追訓
一般情況下訓練的時候,加載預訓練模型,上面使用的是官方的,所以效果沒有那么好。訓練完成后可以加載自己訓練好的模型,繼續訓練。
from ultralytics import YOLOif __name__ == '__main__':# Load a modelmodel = YOLO("yolo11.yaml") # build a new model from YAMLmodel = YOLO("last.pt") # load a pretrained model (recommended for training)model = YOLO("yolo11.yaml").load("last.pt") # build from YAML and transfer weights# Train the modelresults = model.train(data="coco128.yaml", epochs=100, imgsz=640, batch=8)
QT中使用訓練好的模型
這里是在Qt Creator 15.0.1 (Community)進行UI設計和加載模型,使用的c++。當然還有一種是直接在Python環境中進行設計的,在上面常見的yolov11環境中使用pip安裝Pyside2/6就可以,比使用Qt Creator調用訓練好的模型要方便一點。
看一下官方說明,下面有對應的yolo模型在不同平臺和編程語言中的各種使用案例和部署策略。這里使用第一個。
轉換模型
選練好的模型為pt,不能夠直接使用,需要轉換為onnx格式的。
首先安裝依賴庫
pip install onnx==1.17.0 # 這里我的要求為['onnx>=1.12.0,<1.18.0'],運行轉換代碼報錯后注意版本
pip install onnxslim
pip install onnxruntime
在根目錄下,新建pt_to_onnx.py文件,轉換代碼如下:
?
from ultralytics import YOLO# 加載一個訓練好的模型
model = YOLO("runs/detect/train28/weights/best.pt")path = model.export(format="onnx") # 返回導出模型的路徑
print(f"導出的路徑為:{path}")
代碼會自動打印轉換后模型所在的目錄,自己到相應目錄中去找就可以。
QT界面設計(可以不用看,設計好自己的界面就可以)
設計自己的界面就可以了,這里完全不用看
打開Qt Creator,創建工程,這里是使用cmake,不用qmake。設計的界面如下:
配置文件
如果在qt中加載訓練好的模型,需要額外的庫文件,之前說使用YOLO ONNX Detection Inference with C++,對應的就是在yolov11目錄下examples文件中,需要把下面兩個文件拷貝到qt文件夾下
拷貝的記結果如下:
然后把轉換后的模型也放入到該目錄中,其實也可以不放,程序里面寫的是絕對路徑就可以
?安裝opencv
因為上面兩個文件依賴opencv,需要安裝opencv。這里使用了一種比較好的方法就是直接把opencv庫移植到項目中,不需要安裝到其他位置和配置環境變量。
- 下載exe版本的opecnv,這里有【鏈接】,這里下載exe文件
- 下載會后直接點擊exe文件,然后選擇解壓的目錄,下面是我的解壓的目錄(之前下載的是4.11.0版本,這里沒有換圖片),4.11.0會有點問題,所以推薦是4.8.1版本:
- 進入到解壓的opencv文件中,復制build文件夾,粘貼到qt項目中。這里我放入到了opencv目錄中,因為該文件夾下已經存在一個build,新建opencv目錄。下面是我的qt項目文件:
配置CMakeLists.txt文件
## 第一處是增添inference.cpp和inference.h文件set(PROJECT_SOURCESmain.cppmainwindow.cppinference.cppmainwindow.hinference.hmainwindow.ui
)# ======================= OpenCV 配置開始 ========================
# 設置 OpenCV 路徑(請修改成你實際的路徑)
set(OpenCV_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/opencv/build/include")
set(OpenCV_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/opencv/build/x64/vc16/lib")
set(OpenCV_DLL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/opencv/build/x64/vc16/bin")# 添加頭文件路徑
include_directories(${OpenCV_INCLUDE_DIR})
include_directories(${OpenCV_INCLUDE_DIR}/opencv4)# 添加庫路徑
link_directories(${OpenCV_LIB_DIR})# 添加要鏈接的庫(可按需精簡)
# Debug下使用opencv_world481會有錯誤
# opencv_world481和opencv_world481d不能同時使用,否則會出錯
set(OpenCV_LIBS# opencv_world481opencv_world481d
)
# ======================= OpenCV 配置結束 ========================## 第三處修改鏈接部分,鏈接 Qt + OpenCV
target_link_libraries(KeyboardTemplateMatching PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OpenCV_LIBS})
?編寫QT代碼程序
因為使用qt設計師設計了界面,如上面QT界面設計小結所示,這里給出關鍵代碼:
mainwindow.cpp
注意: Inf_ = new Inference("D:/programming_learning/QT/KeyboardTemplateMatching/best.onnx", cv::Size(640, 640), "D:/classes.txt", false);中的D:/classes.txt沒有用,直接用一個空字符代替就好,細看代碼就知道,程序沒有使用這個文件。然后就是最后一個參數設置為false,就是使用CPU,如果為true會有點警告:[ WARN:0@6.279] global net_impl.cpp:178 cv::dnn::dnn4_v20230620::Net::Impl::setUpNet DNN module was not built with CUDA backend; switching to CPU,不過是警告,所以功能正常。
#include "mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>
#include "./ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);try {Inf_ = new Inference("D:/programming_learning/QT/KeyboardTemplateMatching/best.onnx", cv::Size(640, 640), "D:/classes.txt", false);}catch (const std::exception& e) {std::cerr << "創建 Inference 實例時發生異常: " << e.what() << std::endl;QMessageBox::critical(this, "異常", QString("模型加載失敗: %1").arg(e.what()));}catch (...) {std::cerr << "創建 Inference 實例時發生未知異常。" << std::endl;QMessageBox::critical(this, "異常", "發生未知錯誤,無法初始化模型。");}// 這里實現了讀取圖片和進行檢測QObject::connect(ui->action_select_img, &QAction::triggered, this, [=](){QString imagePath = QFileDialog::getOpenFileName(this,"選擇圖片","","圖像文件 (*.png *.jpg *.jpeg *.bmp *.gif)");if (!imagePath.isEmpty()) {QPixmap pixmap(imagePath);if (pixmap.isNull()) {QMessageBox::warning(this, "錯誤", "無法加載圖片!");return;}ui->original_image_label->setPixmap(pixmap.scaled(ui->original_image_label->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));std::string stdStr = imagePath.toUtf8().constData();cv::Mat img_mat = cv::imread(stdStr, cv::IMREAD_COLOR);if (img_mat.empty()) {qDebug() << "圖像讀取失敗,路徑:" << imagePath;}// 執行檢測任務std::vector<Detection> output = Inf_->runInference(img_mat);int detections = output.size();std::cout << "Number of detections: " << detections << std::endl;// 類別計數字典(class_id -> count)std::map<int, int> class_counts;for (const Detection& detection : output){const cv::Rect& box = detection.box;const cv::Scalar& color = detection.color;int class_id = detection.class_id;std::string className = detection.className;float confidence = detection.confidence;// 累計計數class_counts[class_id]++;// 繪制框cv::rectangle(img_mat, box, color, 2);// 構建標簽文字std::string label = className + ' ' + std::to_string(confidence).substr(0, 4);cv::Size textSize = cv::getTextSize(label, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0);cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20);// 繪制背景框和文字cv::rectangle(img_mat, textBox, color, cv::FILLED);cv::putText(img_mat, label, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0);}// Inference ends here...QString resultText = "類別統計結果:\n";for (const auto& [class_id, count] : class_counts) {QString line = QString("- %1: %2\n").arg(QString::fromStdString(Inf_->getClassName(class_id))).arg(count);resultText += line;}// 將結果顯示到 QLabel 上ui->result_label->setText(resultText);// 將 cv::Mat 轉換為 QImage,再顯示在 QLabel 上QImage qimg(img_mat.data,img_mat.cols,img_mat.rows,static_cast<int>(img_mat.step),QImage::Format_BGR888); // 注意格式要匹配 OpenCV 的 BGR// 設置 QLabel 顯示圖像(自動縮放至 label 尺寸)ui->detect_imag_label->setPixmap(QPixmap::fromImage(qimg).scaled(ui->detect_imag_label->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));}});
}MainWindow::~MainWindow()
{delete ui;delete Inf_;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include "inference.h"QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;Inference *Inf_;
};
#endif // MAINWINDOW_H
修改inference.h里面內容(非常重要)
根據實際情況修改自己的類別,注意要和yolo訓練的時候類別保持一致,否則會出現全屏都是框框的情況。
std::vector<std::string> classes{"circle", "rect"};
一些其他的工作
為了實現匯總每個類別個體的數量,我在inference.h里面增加了一個getClassName函數,在mainwindow.cpp里面進行了調用。
具體實現:
std::string Inference::getClassName(int class_id) const {if (class_id >= 0 && class_id < classes.size()) {return classes[class_id];} else {return "Unknown";}
}
Over
至此工作全部完成,其中也踩了不少坑。