2. 9月11日 YOLO环境安装与数据准备

YOLO介绍

YOLO(You Only Look Once)是一种流行的目标检测算法,它的核心思想是将目标检测任务转换为一个单次推断问题。与传统的目标检测方法不同,YOLO算法通过一个统一的神经网络同时预测图像中的目标位置和类别。这种设计使得YOLO能够实现快速且高效的目标检测,特别适合于需要实时处理的场景,如视频监控、自动驾驶等。

YOLOv8作为该系列的最新版本,进一步优化了检测精度和速度,使其在多个应用场景中表现出色。本专栏将带领大家从0开始学习,有兴趣的小伙伴们可以点个关注~

YOLOv8性能评估

在COCO数据集上的评估结果显示,YOLOv8在检测精度和速度上均优于前几代模型。下表展示了YOLOv8与其他版本在COCO数据集上的性能对比:

Model mAP (mean Average Precision) FPS (Frames Per Second)
YOLOv5 0.48 140
YOLOv6 0.52 120
YOLOv7 0.56 110
YOLOv8 0.60 100

mAP(mean Average Precision)是一个在目标检测任务中常用的性能评估指标。它衡量的是模型在识别图像中的对象时的准确性和一致性。mAP计算了在不同召回率水平上的平均精度,通常用于比较不同模型的性能。具体来说,对于目标检测任务,mAP是所有类别的AP的平均值,其中AP(Average Precision)是指在某个特定召回率下,模型预测正确的正样本数与所有预测为正的样本数之比的平均值。在实际应用中,mAP越高,表示模型的检测性能越好。

FPS(Frames Per Second)则是用来衡量视频播放或游戏运行流畅度的一个指标,表示每秒钟能够渲染和显示的帧数。在视频和游戏领域,FPS越高,画面的流畅度越好。对于实时视频处理或游戏开发来说,高FPS是必要的,因为它能够提供更平滑和响应更快的用户体验。在目标检测模型中,FPS也是一个重要的性能指标,特别是在需要实时处理视频流的应用中,如视频监控、自动驾驶等。

一、环境准备

1. Anaconda

Anaconda 是一个开源的 Python 和 R 语言的发行版,致力于简化数据科学、机器学习、人工智能和大数据的包管理和部署。它包含了大量流行的数据科学包,并且通过 Conda 包管理器提供了便捷的包安装、更新和管理方式。

https://www.anaconda.com/download

2. 创建yolov8虚拟环境

在电脑左下角搜索Anaconda,点击打开Anaconda Prompt

image-wwue.png

创建新的虚拟环境yolov8

conda create -n yolov8 python=3.8

激活yolov8环境

conda activate yolov8

二、yolov8模型下载

Ultralytics 的 YOLOv8 是一款前沿的尖端模型,它在以前的 YOLO 版本基础上取得了成功,并引入了新特性与改进,以进一步提升性能和灵活性。YOLOv8 设计得既快速又精确,易于使用,适用于广泛的对象检测与跟踪、实例分割、图像分类及姿态估计任务。

GitCode - 全球开发者的开源社区,开源代码托管平台

pip install ultralytics -i  https://pypi.tuna.tsinghua.edu.cn/simple/ 

下载训练模型

image-bahk.png

三、准备数据集

之前标注做的不好,

本次用现成的

安全帽检测数据集 (Helmet Detection)_数据集-飞桨AI Studio星河社区 (baidu.com)

image-sawp.png

1. 将xml转成txt文件

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

<class_id> <x_center> <y_center> <width> <height>

image-bypd.png

image-tzzg.png

image-xcrw.png

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]
 
    # print(x, y, w, h)
    return (x, y, w, h)
 
 
def convert_annotation(xml_files_path, save_txt_files_path, classes):
    xml_files = os.listdir(xml_files_path)
    print(xml_files)
    for xml_name in xml_files:
        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)
 
        for obj in root.iter('object'):
            difficult = obj.find('difficult').text
            cls = obj.find('name').text
            if cls not in classes or int(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))
            # b=(xmin, xmax, ymin, ymax)
            print(w, h, b)
            bb = convert((w, h), b)
            out_txt_f.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
 
 
if __name__ == "__main__":
    # 1、指定yolo类别
    classes1 = ["helmet","head"]
    # 2、voc格式的xml标签文件路径
    xml_files1 = r'D:\yolo_test\HelmetDetection\annotations'
    # 3、转化为yolo格式的txt标签文件存储路径
    save_txt_files1 = r'D:\yolo_test\HelmetDetection\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')

2. 划分数据集

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

  1. train(训练集)
    • 训练集是用于训练模型的数据集部分。
    • 模型通过学习训练集中的样本来调整参数。
    • 在深度学习中,训练集通常包含大量标注好的数据,用于网络的迭代训练。
  2. val(验证集)
    • 验证集用于在训练过程中评估模型的性能,但不参与训练。
    • 它帮助我们监控模型在看不见的数据上的表现,从而避免过拟合。
    • 验证集独立于训练集,可以用来进行超参数调整和模型选择。
  3. test(测试集)
    • 测试集用于在模型训练完成后最终评估模型的性能。
    • 它应该完全独立于训练过程,只在最后评估时使用一次。
    • 测试集可以看作是模型部署前的最后一道检验。

在实际应用中,数据的划分比例可以根据具体任务和数据量来决定。常见的划分比例有:

  • 70% 训练集,15% 验证集,15% 测试集
  • 80% 训练集,10% 验证集,10% 测试集
  • 或者其他根据需要调整的比例

正确地划分数据集对于构建一个健壮、可靠的机器学习模型至关重要。

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, 'images')
        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__':
    g_origin_dataset_dir = r'D:\yolo_test\HelmetDetection'
    g_train_dataset_dir =  r'D:\yolo_test\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()

四、最终文件夹分布

image-ywjd.png