实现效果:
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
至此工作全部完成,其中也踩了不少坑。