YOLO介绍

YOLO(You Only Look Once)是一种基于深度学习的实时目标检测算法,最早由 Joseph Redmon 等人提出,其主要特点是将目标检测任务转化为一个单一的回归问题,在一次前向传播中同时预测多个边界框和类别概率,因此得名 “只看一次”。这种设计使得YOLO能够实现快速且高效的目标检测,特别适合于需要实时处理的场景,如视频监控、自动驾驶等。

YOLOv11性能评估

项目文件夹最终分布

YOLO11_TEST2
│
├── archive                 # 数据集
│   ├── Annotations         # xml数据文件夹
│   ├── JPEGImages          # 图像文件夹
│   ├── labels              # txt数据文件夹
│   └── classes.txt         # 类别文件
│
├── runs                    # 存放训练输出的文件夹
│   └── detect
│       └── train             # 训练输出文件夹
│           ├── weights       # 权重文件夹,存放训练过程中保存的模型权重
│           │   ├── best.pt   # 最佳权重文件
│           │   └── last.pt   # 最后一次训练的权重文件
│           └── ...
│
├── test                   # 测试集文件夹
│   ├── test
│   │   ├── images         # 测试图像
│   │   └── labels         # 测试集标签
│   ├── train
│   │   ├── images
│   │   ├── labels
│   │   └── labels.cache   # 标签缓存文件
│   ├── val                # 验证集文件夹
│   │   ├── images
│   │   ├── labels
│   │   └── labels.cache   # 标签缓存文件
│   └── data.yaml          # 数据集配置文件
│
├── 0Shi_Bie_Lei_Bie.py
├── 1Geng_Gai_Ge_Shi.py
├── 2Hua_Fen_Xun_Lian_Ji.py
├── 3Mo_Xin_Xun_Lian.py
├── 4Mo_Xin_Yan_Zheng.py
├── 5Ping_Mu_Nei_Rong_Jian_Ce.py
├── pytest.py
├── test.jpg
└── yolo11n.pt              # 预训练 YOLO 模型权重文件

一、环境准备

Anaconda

介绍

Anaconda 是一个数据科学和机器学习的软件套装,它包含了许多工具和库,让您能够更轻松地进行编程、分析数据和构建机器学习模型。

Anaconda 包及其依赖项和环境的管理工具为conda命令,文章后面部分会详细介绍。

与传统的Python pip工具相比 Anaconda 的conda可以更方便地在不同环境之间进行切换,环境管理较为简单。

为什么选择 Anaconda?

方便安装、包管理器、环境管理、集成工具和库;

Jupyter 笔记本、Spyder 集成开发环境、跨平台性、社区支持。

安装Anaconda:

https://www.anaconda.com/download/success

双击程序 → Next → I Agree → Next → Next → 全选 → Install → Next → Next → Finish


创建yolov11虚拟环境

image-mdid.png

#创建新的虚拟环境
conda create -n yolov11 python=3.13

#激活虚拟环境
conda activate yolov11

二、yolov11模型下载

https://gitcode.com/gh_mirrors/ul/ultralytics/

YOLO11 是UltralyticsYOLO 是实时物体检测器系列中的最新产品,以最先进的精度、速度和效率重新定义了可能实现的目标。在之前YOLO 版本令人印象深刻的进步基础上,YOLO11 在架构和训练方法上进行了重大改进,使其成为广泛的计算机视觉任务的多功能选择。

下载项目所需的python库

pip install ultralytics opencv-python mss pyyaml numpy -i https://pypi.tuna.tsinghua.edu.cn/simple/
#安装GPU训练所需的python库,如果不用GPU则不用下载
pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126

这里的GPU所需的cu版本需要自行检查,看自己的显卡是否支持此版本

#cmd中输入
nvcc --version
nvidia-smi
https://pytorch.org/

请注意cu126不支持python3.8之前的版本

三、准备数据集

下载现成的标注好的数据集(这个数据集比较差,因为是卫星拍摄,许多图片很模糊,建议自行筛选)

https://aistudio.baidu.com/datasetdetail/223758

整理数据集中的类别

Python程序代码:

# 0Shi_Bie_Lei_Bie.py

import os
import xml.etree.ElementTree as ET


def extract_classes(xml_dir):
    classes = set()
    for file in os.listdir(xml_dir):
        if not file.endswith(".xml"):
            continue
        path = os.path.join(xml_dir, file)
        tree = ET.parse(path)
        root = tree.getroot()
        for obj in root.iter("object"):
            cls = obj.find("name").text.strip()
            classes.add(cls)
    return sorted(classes)


if __name__ == "__main__":
    current_path = os.path.dirname(__file__)
    xml_dir = rf"{current_path}\archive\Annotations"
    class_list = extract_classes(xml_dir)

    # 输出到终端
    print("发现的类别:", class_list)

    # 保存到 classes.txt
    save_path = rf"{current_path}\archive"
    os.makedirs(save_path, exist_ok=True)
    with open(os.path.join(save_path, "classes.txt"), "w") as f:
        for cls in class_list:
            f.write(cls + "\n")

    labels_path = rf"{current_path}\archive\labels"
    os.makedirs(labels_path, exist_ok=True)
    print(f"已创建 labels 文件夹:{labels_path}")
    
    test_path = rf"{current_path}\test"
    os.makedirs(test_path, exist_ok=True)
    print(f"已创建 test 文件夹:{test_path}")

将xml转成txt文件

将标注文件转换为YOLO格式。YOLO格式的标注文件内容如下:

<class_id>    <x_center>        <y_center>     <width>     <height>
<类别编号>  <边框中心点x坐标>  <边框中心点y坐标>  <边框宽度>  <边框高度>

Python程序代码:

# 1Geng_Gai_Ge_Shi.py

import xml.etree.ElementTree as ET
import os


def convert(size, box):
    x_center = (box[0] + box[1]) / 2.0
    y_center = (box[2] + box[3]) / 2.0
    x = x_center / size[0]
    y = y_center / size[1]
    w = (box[1] - box[0]) / size[0]
    h = (box[3] - box[2]) / size[1]
    return (x, y, w, h)


def convert_annotation(xml_files_path, save_txt_files_path, classes):
    os.makedirs(save_txt_files_path, exist_ok=True)
    xml_files = os.listdir(xml_files_path)
    print(xml_files)
    for xml_name in xml_files:
        if not xml_name.endswith('.xml'):
            continue
        print(xml_name)
        xml_file = os.path.join(xml_files_path, xml_name)
        out_txt_path = os.path.join(save_txt_files_path, xml_name.split('.')[0] + '.txt')
        out_txt_f = open(out_txt_path, 'w')
        tree = ET.parse(xml_file)
        root = tree.getroot()
        size = root.find('size')
        w = int(size.find('width').text)
        h = int(size.find('height').text)
        if w == 0 or h == 0:
            print(f"[跳过] {xml_file} 图像宽或高为0")
            continue

        for obj in root.iter('object'):
            cls = obj.find('name').text.strip()
            difficult_node = obj.find('difficult')
            difficult = int(difficult_node.text) if difficult_node is not None else 0
            if cls not in classes or difficult == 1:
                continue

            cls_id = classes.index(cls)
            xmlbox = obj.find('bndbox')
            b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
                 float(xmlbox.find('ymax').text))
            print(w, h, b)
            bb = convert((w, h), b)
            out_txt_f.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
        out_txt_f.close()


if __name__ == "__main__":
    current_path = os.path.dirname(__file__)
    print("当前文件的路径:", current_path)
    classes1 = ['A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A18', 'A2', 'A3', 'A4', 'A5', 'A7', 'A8', 'A9']  # 请替换为你的真实类别列表
    xml_files1 = rf'{current_path}\archive\Annotations'
    save_txt_files1 = rf'{current_path}\archive\labels'

    convert_annotation(xml_files1, save_txt_files1, classes1)
    with open(save_txt_files1 + '/classes.txt', 'w') as file:
        for class_name in classes1:
            file.write(class_name + '\n')

划分数据集

在机器学习和计算机视觉领域,特别是在深度学习模型的训练过程中,通常会将数据集划分为几个部分,以确保模型的有效性和泛化能力。这四个部分:testtraintrainvalval,各自扮演不同的角色:

Python程序代码:

# 2Hua_Fen_Xun_Lian_Ji.py

import os
import random
import shutil
import time
import yaml
 
 
class YOLOTrainDataSetGenerator:
    def __init__(self, origin_dataset_dir, train_dataset_dir, train_ratio=0.7, val_ratio=0.15, test_ratio=0.15,
                 clear_train_dir=False):
        # 设置随机数种子
        random.seed(1233)
 
        self.origin_dataset_dir = origin_dataset_dir
        self.train_dataset_dir = train_dataset_dir
        self.train_ratio = train_ratio
        self.val_ratio = val_ratio
        self.test_ratio = test_ratio
        self.clear_train_dir = clear_train_dir
 
        assert self.train_ratio > 0.5, 'train_ratio must larger than 0.5'
        assert self.val_ratio > 0.01, 'train_ratio must larger than 0.01'
        assert self.test_ratio > 0.01, 'test_ratio must larger than 0.01'
        total_ratio = round(self.train_ratio + self.val_ratio + self.test_ratio)
        assert total_ratio == 1.0, 'train_ratio + val_ratio + test_ratio must equal 1.0'
 
    def generate(self):
        time_start = time.time()
        # 原始数据集的图像目录,标签目录,和类别文件路径
        origin_image_dir = os.path.join(self.origin_dataset_dir, 'JPEGImages')
        origin_label_dir = os.path.join(self.origin_dataset_dir, 'labels')
        origin_classes_file = os.path.join(self.origin_dataset_dir, 'classes.txt')
        if not os.path.exists(origin_classes_file):
            return
        else:
            origin_classes = {}
            with open(origin_classes_file, mode='r') as f:
                for cls_id, cls_name in enumerate(f.readlines()):
                    cls_name = cls_name.strip()
                    if cls_name != '':
                        origin_classes[cls_id] = cls_name
 
        # 获取所有原始图像文件名(包括后缀名)
        origin_image_filenames = os.listdir(origin_image_dir)
 
        # 随机打乱文件名列表
        random.shuffle(origin_image_filenames)
 
        # 计算训练集、验证集和测试集的数量
        total_count = len(origin_image_filenames)
        train_count = int(total_count * self.train_ratio)
        val_count = int(total_count * self.val_ratio)
        test_count = total_count - train_count - val_count
 
        # 定义训练集文件夹路径
        if self.clear_train_dir and os.path.exists(self.train_dataset_dir):
            shutil.rmtree(self.train_dataset_dir, ignore_errors=True)
        train_dir = os.path.join(self.train_dataset_dir, 'train')
        val_dir = os.path.join(self.train_dataset_dir, 'val')
        test_dir = os.path.join(self.train_dataset_dir, 'test')
        train_image_dir = os.path.join(train_dir, 'images')
        train_label_dir = os.path.join(train_dir, 'labels')
        val_image_dir = os.path.join(val_dir, 'images')
        val_label_dir = os.path.join(val_dir, 'labels')
        test_image_dir = os.path.join(test_dir, 'images')
        test_label_dir = os.path.join(test_dir, 'labels')
 
        # 创建训练集输出文件夹
        os.makedirs(train_image_dir, exist_ok=True)
        os.makedirs(train_label_dir, exist_ok=True)
        os.makedirs(val_image_dir, exist_ok=True)
        os.makedirs(val_label_dir, exist_ok=True)
        os.makedirs(test_image_dir, exist_ok=True)
        os.makedirs(test_label_dir, exist_ok=True)
 
        # 将图像和标签文件按设定的ratio划分到训练集,验证集,测试集中
        for i, filename in enumerate(origin_image_filenames):
            if i < train_count:
                output_image_dir = train_image_dir
                output_label_dir = train_label_dir
            elif i < train_count + val_count:
                output_image_dir = val_image_dir
                output_label_dir = val_label_dir
            else:
                output_image_dir = test_image_dir
                output_label_dir = test_label_dir
            src_img_name_no_ext = os.path.splitext(filename)[0]
            src_image_path = os.path.join(origin_image_dir, filename)
            src_label_path = os.path.join(origin_label_dir, src_img_name_no_ext + '.txt')
            if os.path.exists(src_label_path):
                # 复制图像文件
                dst_image_path = os.path.join(output_image_dir, filename)
                shutil.copy(src_image_path, dst_image_path)
                # 复制标签文件
                src_label_path = os.path.join(origin_label_dir, src_img_name_no_ext + '.txt')
                dst_label_path = os.path.join(output_label_dir, src_img_name_no_ext + '.txt')
                shutil.copy(src_label_path, dst_label_path)
            else:
                pass
        train_dir = os.path.normpath(train_dir)
        val_dir = os.path.normpath(val_dir)
        test_dir = os.path.normpath(test_dir)
        data_dict = {
            'train': train_dir,
            'val': val_dir,
            'test': test_dir,
            'nc': len(origin_classes),
            'names': origin_classes
        }
 
        yaml_file_path = os.path.normpath(os.path.join(self.train_dataset_dir, 'data.yaml'))
        with open(yaml_file_path, mode='w') as f:
            yaml.safe_dump(data_dict, f, default_flow_style=False, allow_unicode=True)
  
 
if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    print("当前文件的路径:", current_path)
    g_origin_dataset_dir = rf'{current_path}\archive'
    g_train_dataset_dir =  rf'{current_path}\test'
    g_train_ratio = 0.7
    g_val_ratio = 0.15
    g_test_ratio = 0.15
    yolo_generator = YOLOTrainDataSetGenerator(g_origin_dataset_dir, g_train_dataset_dir, g_train_ratio, g_val_ratio,
                                               g_test_ratio, True)
    yolo_generator.generate()
    print("任务完成")

四、模型训练

Python程序代码GPU版本:

# 3Mo_Xin_Xun_Lian.py

# coding:utf-8
from ultralytics import YOLO
import torch
import os

torch.cuda.empty_cache()
# 确保 CUDA 可用
assert torch.cuda.is_available(), "CUDA 不可用,请检查你的 GPU 驱动或 PyTorch 安装是否支持 CUDA"

# 加载 YOLOv8 模型
model = YOLO("yolo11n.pt")  # 你可以改成 yolov11s.pt, yolov11m.pt, yolov11l.pt 以使用更大模型

if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    print("当前文件的路径:", current_path)
    # 训练模型
    results = model.train(
        data=rf'{current_path}\test\data.yaml',  # 数据集配置
        epochs=300,        # 训练轮数
        batch=4,           # 批量大小,根据你显卡显存适当调整
        device='cuda',     # 使用 GPU
    )

Python程序代码CPU版本:

# 3Mo_Xin_Xun_Lian.py

# coding:utf-8
from ultralytics import YOLO
import os

# 加载 YOLOv8 模型
model = YOLO("yolo11n.pt")  # 你可以改成 yolov11s.pt, yolov11m.pt, yolov11l.pt 以使用更大模型

if __name__ == '__main__':
    current_path = os.path.dirname(__file__)
    print("当前文件的路径:", current_path)
    # 训练模型
    results = model.train(
        data=rf'{current_path}\test\data.yaml',  # 数据集配置
        epochs=180,        # 训练轮数
        batch=2,           # 批量大小,根据你的CPU适当调整
    )

CPU与GPU训练过程截图:

CPU:

GPU:

五、训练结果

image-rvfs.png

Yolov8在训练完成之后,会在runs/detect/train目录下把训练的过程一些参数与结果示意图保存下来,这里面包含是目标检测性能指标。

runs/detect/train/weights包含best.pt以及last.pt,前者是模型在验证集上表现最好的权重文件,后者是模型在训练结束时的最终权重文件,代表模型训练过程中的最后一个状态。

六、模型效果验证

对图片进行验证

Python程序代码CPU版本:

# 4Mo_Xin_Yan_Zheng.py

from ultralytics import YOLO
import os

current_path = os.path.dirname(__file__)
print("当前文件的路径:", current_path)
model = YOLO(rf'{current_path}\runs\detect\train\weights\best.pt')
if __name__ == '__main__':
    results = model.predict(source='test.jpg')
    results[0].show()

结果:

对屏幕内容进行验证

Python程序代码CPU版本:

# 5Ping_Mu_Nei_Rong_Jian_Ce.py

import cv2
import numpy as np
from mss import mss
from ultralytics import YOLO
import os

current_path = os.path.dirname(__file__)
print("当前文件的路径:", current_path)
# 加载训练好的 YOLO 模型
model = YOLO(rf'{current_path}\runs\detect\train\weights\best.pt')

sct = mss()

# 假设屏幕分辨率为 1920x1080
screen_width = 1920
screen_height = 1080

# 捕获屏幕中央 800x600 的区域
monitor = {
    "top": (screen_height - 1060) // 2,  # 从屏幕中央向下偏移 230 像素
    "left": (screen_width - 1400) // 2,  # 从屏幕中央向左偏移 320 像素
    "width": 1600,  # 区域宽度
    "height": 1400  # 区域高度
}

# 定义 OpenCV 窗口名称
window_name = "YOLO Detection"

if __name__ == '__main__':
    try:
        while True:
            screenshot = sct.grab(monitor)      # 捕获屏幕
            img = np.array(screenshot)          # 将屏幕截图转换为 numpy 数组格式 (cv2 兼容)
            img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGB)      # 将图像从 BGRA 转换为 RGB 格式(因为 mss 捕获的图像带有 alpha 通道)
            results = model.predict(source=img, show=False)  # 使用 YOLO 模型对屏幕截图进行预测;禁用自动显示
            result_img = results[0].plot()      # 获取预测结果图像
            cv2.imshow(window_name, result_img) # 显示结果图像
            
            # 按下 'q' 键退出循环
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            
    except Exception as e:
        print(f"发生错误: {e}")
    finally:
        # 释放资源
        cv2.destroyAllWindows()

结果: