Yolov3-spp系列 | 训练Pascal voc格式的数据集

简介: Yolov3-spp系列 | 训练Pascal voc格式的数据集

1. 将数据集标注成yolo格式


image.png

这里我曾经已经写过一个对xml目录文件转txt标注文件的脚本,修改目录路径直接运行即可。脚本文件见博文:yolov5的数据集准备 | 处理Pascal voc格式的数据集


xml_to_txt.py

import os
import pickle
import xml.etree.ElementTree as ET
from tqdm import tqdm
# chance your classes here
names: ['with_mask', 'without_mask', 'mask_weared_incorrect']
# function:
#       (xmin, xmax, ymin, ymax) -> (center_x, cneter_y, box_weight, box_height)
# size: (w, h)
# box : (xmin, xmax, ymin, ymax)
def convert(size, box):
    dw = 1.0 / size[0]
    dh = 1.0 / size[1]
    x = (box[0] + box[1]) / 2.0     # center_x
    y = (box[2] + box[3]) / 2.0     # center_y
    w = box[1] - box[0]             # box_weight
    h = box[3] - box[2]             # box_height
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return (x, y, w, h)
# 功能:对单个文件进行转换,其中名字是相匹配的
# infile: xml文件路径
# outfile:txt文件输出路径
def convert_annotation(infile, outfile):
    in_file = open(infile, encoding='utf-8')  # xml path
    out_file = open(outfile, 'w')
    tree = ET.parse(in_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))
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
    in_file.close()
    out_file.close()
# 功能:对xml目录下的全部xml文件全部转换成yolo的txt格式,并输出在savepath目下
# xmlpath: the dir path save xml files
# savepath: the dir path to save txt files
def xml_to_txt(xmlpath, savepath):
    assert os.path.exists(xmlpath), "{} not exists.".format(xmlpath)
    if not os.path.exists(savepath):
        os.mkdir(savepath)
        assert os.path.exists(savepath), "{} create fail.".format(savepath)
    xmlfiles = os.listdir(xmlpath)
    assert len(xmlfiles) != 0, "{} have no files".format(xmlpath)
    xmlfilepaths = [os.path.join(xmlpath, xmlfile) for xmlfile in xmlfiles]
    for xmlfilepath in tqdm(xmlfilepaths, desc="change xml to txt: "):
        # E:\学习\机器学习\数据集\众盈比赛数据集\xml\train1.xml -> train1
        image_id = xmlfilepath.split('\\')[-1].split('.')[0]
        # train1 -> \..\train1.txt
        txtfilepath = os.path.join(savepath, image_id) + ".txt"
        convert_annotation(xmlfilepath, txtfilepath)
if __name__ == '__main__':
    xmlpath = r"E:\学习\机器学习\数据集\VOC2012\VOCdevkit\VOC2012\Annotations"
    savepath = r"E:\学习\机器学习\数据集\VOC2012\VOCdevkit\VOC2012\YoloLabels"
    xml_to_txt(xmlpath, savepath)


2. 生成train.txt与val.txt文件


最重要的就是这一步骤了,需要分别保存所有的训练与测试图片路径在txt文件中。


那么,首先就是需要构建验证集与测试集,然后再根据对应的数据集来存放相应的图片路径。


2.1 数据集结构处理

image.png

对于这里,yolov3的数据集结构与yolov5的不一致,所以需要重新摆放数据集结构,结构如下:


├── Dataset 自定义数据集根目录
│         ├── train   训练集目录
│         │     ├── images  训练集图像目录
│         │     └── labels  训练集标签目录 
│         └── val    验证集目录
│               ├── images  验证集图像目录
│               └── labels  验证集标签目录


对于之前的代码进行简单的修改即可(就是主要是修改路径)

image.png

构建数据集结构代码如下:

from sklearn.model_selection import train_test_split
import os
from tqdm import tqdm
from shutil import copyfile
# 功能: 递归穿件目录文件,有则跳过,没有则创建并验证
def _mkdir(dirpath):
    if not os.path.exists(dirpath):
        os.makedirs(dirpath)
        assert os.path.exists(dirpath), "{} create fail.".format(dirpath)
# 功能: 从图片路径中获取最后的文件名(含后缀或者不含后缀)
def get_filename(filepath, suffix=True):
    if suffix is True:
        image_id = filepath.split('\\')[-1]     # 获取文件名(含后缀)
    else:
        image_id = filepath.split('\\')[-1].split('.')[0]   # 获取文件名(不含后缀)
    return image_id
# 功能: 根据图片数据与标签数据构建yolov5格式的数据集
# imagepath: 存放数据图片的路径
# labelpath: 存放数据标签的路径
# datasetpath: 构建voc格式数据集的目录路径
def dataset_split(imagepath, labelpath, datasetpath):
    assert os.path.exists(imagepath), "{} not exists.".format(imagepath)
    assert os.path.exists(labelpath), "{} not exists.".format(labelpath)
    # 构建图片与标签的路径列表,然后按8:2进行训练与测试的切分
    images = os.listdir(imagepath)
    imagelists = [os.path.join(imagepath, image) for image in images]
    assert len(imagelists) != 0, "{} have no files".format(imagepath)
    labels = os.listdir(labelpath)
    labellists = [os.path.join(labelpath, label) for label in labels]
    assert len(labellists) != 0, "{} have no files".format(labelpath)
    X_train, X_test, y_train, y_test = train_test_split(imagelists, labellists, test_size=0.2, random_state=42)
    # 构建voc格式的数据集存放格式
    _mkdir(datasetpath)
    # 在数据路径datasetpath下,分别递归建立目录:
    # | -----Dataset
    #       | -----train
    #           | -----images
    #           | -----labels
    #       | -----val
    #           | -----images
    #           | -----labels
    images_train_dirpath = os.path.join(datasetpath, "train", "images")
    _mkdir(images_train_dirpath)
    labels_train_dirpath = os.path.join(datasetpath, "train", "labels")
    _mkdir(labels_train_dirpath)
    images_val_dirpath = os.path.join(datasetpath, "val", "images")
    _mkdir(images_val_dirpath)
    labels_val_dirpath = os.path.join(datasetpath, "val", "labels")
    _mkdir(labels_val_dirpath)
    # 复制图片/标签列表中的数据到指定子目录下
    for image_path in tqdm(X_train, desc="process train images"):
        image_newpath = os.path.join(images_train_dirpath, get_filename(image_path))
        copyfile(image_path, image_newpath)
    for image_path in tqdm(X_test, desc="process val images"):
        image_newpath = os.path.join(images_val_dirpath, get_filename(image_path))
        copyfile(image_path, image_newpath)
    for image_path in tqdm(y_train, desc="process train labels"):
        image_newpath = os.path.join(labels_train_dirpath, get_filename(image_path))
        copyfile(image_path, image_newpath)
    for image_path in tqdm(y_test, desc="process val labels"):
        image_newpath = os.path.join(labels_val_dirpath, get_filename(image_path))
        copyfile(image_path, image_newpath)
    print("Process Success. The data has save to {}".format(datasetpath))
if __name__ == '__main__':
    imagepath = r"E:\学习\机器学习\数据集\mask\images"
    labelpath = r"E:\学习\机器学习\数据集\mask\YoloLabels"
    datasetpath = r"E:\学习\机器学习\数据集\mask\Yolov3_Dataset"
    dataset_split(imagepath, labelpath, datasetpath)


2.2 文件名保存处理

接着,先遍历刚刚整理好的数据集结构,然后把图像全部路径的放在对应的txt文件中。


文件名保存代码如下:

import os
import numpy as np
def make_filepath(dirpath, savepath):
    assert os.path.exists(dirpath), "{} not exists.".format(dirpath)
    assert os.path.exists(savepath), "{} not exists.".format(savepath)
    trainpath = os.path.join(dirpath, "train", "images")
    validpath = os.path.join(dirpath, "val", "images")
    assert os.path.exists(trainpath), "{} not exists.".format(trainpath)
    assert os.path.exists(validpath), "{} not exists.".format(validpath)
    trainfiles = os.listdir(trainpath)
    assert len(trainfiles) > 0, "{} has no files.".format(trainpath)
    validfiles = os.listdir(validpath)
    assert len(validfiles) > 0, "{} has no files.".format(validpath)
    with open(os.path.join(savepath, "train.txt"), "w") as f:
        for trainfile in trainfiles:
            trainfile = os.path.join(r"./dataset/mask/train/images", trainfile) + "\n"
            f.writelines(trainfile)
    with open(os.path.join(savepath, "val.txt"), "w") as f:
        for validfile in validfiles:
            validfile = os.path.join(r"./dataset/mask/val/images", validfile) + "\n"
            f.writelines(validfile)
    print("write success...")
if __name__ == '__main__':
    # dirpath = r"E:\PyCharm\workspace\Detection\yolov3_spp\dataset\mask"
    # savepath = r"E:\PyCharm\workspace\Detection\yolov3_spp\data"
    imgpath = r"./dataset/mask"
    savepath = r"./data"
    make_filepath(imgpath, savepath)


处理好之后,会生成两个txt文件,分别对应的是训练集的全部图像路径和验证集图像的全部路径。可以注意到,其实这里是不需要处理label路径的,因为会根据图像的路径自动的找到label的路径。处理之后的结果如下所示:


train.txt

image.png

val.txt

image.png


3. 创建一个.names标签


.names标签文件用来存储类别信息

image.png

这里我的口罩检测数据集是有3类:[‘with_mask’, ‘without_mask’, ‘mask_weared_incorrect’],然后直接记录在文件中即可。

image.png


4. 创建一个.data文件


data文件包含txt文件的路径与标签文件的路径,还需要指定类别数量

image.png

这里根据项目的路径位置自行设置:

image.png


5. 更改预测器卷积核的个数


image.png

由于这里我的类别个数为3,所以[convolutional]中的filters值为:(5+3)*3=24,然后修改一些[yolo]中的classes即可,这里改为你的类别数,如下所示:

image.png


6. 更改超参及训练


这里的超参没有一点炼丹的经验一般不建议改动

image.png

然后,就可以开始训练自己的数据集了。


补充一下我对yolov3spp数据集制作的思路:


可以先将数据集按yolov3的结构啦进行摆放,然后再生成相对应的文件,毕竟需要使用不同的系统(服务器与本地),这样做可以不需要改动摆放结构,只需要改变相关文件的路径即可。所以大体思路如下,也就是我上面所提供的两个脚本


1)制作对应结构的数据集;

2)根据生成的数据集来生成相关文件,比如训练图片路径与测试图片路径;


目录
相关文章
|
机器学习/深度学习 监控 数据可视化
【31】yolov5的使用 | 训练Pascal voc格式的数据集
【31】yolov5的使用 | 训练Pascal voc格式的数据集
1596 0
【31】yolov5的使用 | 训练Pascal voc格式的数据集
|
算法 数据库 计算机视觉
Dataset之COCO数据集:COCO数据集的简介、下载、使用方法之详细攻略
Dataset之COCO数据集:COCO数据集的简介、下载、使用方法之详细攻略
|
计算机视觉
数据集学习笔记(三):COCO创建dataloader用于训练
如何使用COCO数据集创建dataloader进行训练,包括安装环境、加载数据集代码、定义数据转换、创建数据集对象以及创建dataloader。
389 5
|
存储 人工智能 自然语言处理
云上玩转DeepSeek系列之二:PAI+DeepSeek,打造智能问答助手
本文将为您带来“PAI+DeepSeek,30分钟打造支持连网搜索+私有知识库的智能应用”最佳实践,大模型能力、联网能力再加持 RAG 方案,实现 DeepSeek 系列模型与现有业务的高效融合。
|
机器学习/深度学习 运维 监控
深度学习之异常检测
基于深度学习的异常检测是一项重要的研究领域,主要用于识别数据中的异常样本或行为。异常检测广泛应用于多个领域,如网络安全、金融欺诈检测、工业设备预测性维护、医疗诊断等。
1336 2
|
XML JSON 数据可视化
数据集学习笔记(二): 转换不同类型的数据集用于模型训练(XML、VOC、YOLO、COCO、JSON、PNG)
本文详细介绍了不同数据集格式之间的转换方法,包括YOLO、VOC、COCO、JSON、TXT和PNG等格式,以及如何可视化验证数据集。
4502 1
数据集学习笔记(二): 转换不同类型的数据集用于模型训练(XML、VOC、YOLO、COCO、JSON、PNG)
|
自然语言处理 UED 开发者
LLaMA-Omni 低延迟高质量语音交互,开源!
随着GPT-4o的发布,在语音界面的Voice-Chat越来越受到大家的关注,对于低延迟,高准确性模型的speech-to-speech的需求日益增长
|
机器学习/深度学习 数据采集 人工智能
机器学习在金融领域的应用及其挑战
【8月更文挑战第18天】本文将探讨机器学习技术在金融行业中的运用,以及在实际应用过程中遇到的挑战和问题。我们将从算法选择、数据处理、模型解释性及伦理法规四个方面进行详细讨论,并给出相应的解决建议。
351 1
|
机器学习/深度学习 算法 数据挖掘
【机器学习】Python详细实现基于欧式Euclidean、切比雪夫Chebyshew、曼哈顿Manhattan距离的Kmeans聚类
文章详细实现了基于不同距离度量(欧氏、切比雪夫、曼哈顿)的Kmeans聚类算法,并提供了Python代码,展示了使用曼哈顿距离计算距离矩阵并输出k=3时的聚类结果和轮廓系数评价指标。
452 1
|
XML 数据可视化 算法
目标检测YOLO数据集的三种格式及转换
目标检测YOLO数据集的三种格式及转换