实现效果:

Yolov11环境搭建(搭建好的可以直接跳过)

最好使用Anconda进行包管理,安装可参考【文章】。下面简单过一下如何快速部署环境。如果搭建过或可以参考其他文章可以跳过Yolo11环境搭建这一章节。总体来说Yolov11环境搭建越来越容易了。

Anconda创建环境

  1. 打开Anaconda prompt
  2. 使用下面命令创建新环境,这里指定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
  3. 切换到自己刚才创建的环境中
    ## 指令
    conda activate 环境名
    # 不清楚的可以使用下面指令列出所有环境
    conda env list
    
    ## 示例,激活刚才创建的环境yolov11
    conda activate yolov11
  4. 安装ultralytics,详细请参考【ultralytics官网快速入门教程
    ## 直接安装ultralytics,默认最新版本的,也可以指定版本,这里使用最新的
    pip install ultralytics
    
    ## 上面安装的有一点慢,可以指定国内源,这里使用清华源
    pip install ultralytics -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
  5. 安装过程中的警告(这个警告是多打了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
    
    ## 这里没有使用,不影响正常使用
  6. 上面会默认安装Pytorch,但是只能使用CPU。如果只想使用CPU进行训练,至此,所有环境配置完成。
  7. 卸载安装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...
  8. 安装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版本,需要更新显卡驱动就可以。
  9. 打开【Pytorch官网】,如下
  10. 可以看到我最高支持的为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 YOLO

if __name__ == '__main__':
    # Load a model
    model = YOLO("yolo11.yaml")  # The first way: build a new model from YAML
    model = YOLO("yolo11n.pt")  # The second way: load a pretrained model (recommended for training)
    model = YOLO("yolo11.yaml").load("yolo11n.pt")  # The third way: build from YAML and transfer weights

    # Train the model
    results = 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: circle
  1: 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 YOLO

if __name__ == '__main__':
    # Load a model
    model = YOLO("yolo11.yaml").load("last.pt")  # build from YAML and transfer weights

    # Train the model
    results = 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库移植到项目中,不需要安装到其他位置和配置环境变量。

  1. 下载exe版本的opecnv,这里有【链接】,这里下载exe文件
  2. 下载会后直接点击exe文件,然后选择解压的目录,下面是我的解压的目录(之前下载的是4.11.0版本,这里没有换图片),4.11.0会有点问题,所以推荐是4.8.1版本:
  3. 进入到解压的opencv文件中,复制build文件夹,粘贴到qt项目中。这里我放入到了opencv目录中,因为该文件夹下已经存在一个build,新建opencv目录。下面是我的qt项目文件:

配置CMakeLists.txt文件

## 第一处是增添inference.cpp和inference.h文件

set(PROJECT_SOURCES
        main.cpp
        mainwindow.cpp
        inference.cpp
        mainwindow.h
        inference.h
        mainwindow.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_world481
    opencv_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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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

至此工作全部完成,其中也踩了不少坑。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐