Utilisez YOLOv5 pour former votre propre ensemble de données de détection de cible (prenez la détection de masque comme exemple)

        Pour exécuter YOLOv5, vous devez d'abord installer l'environnement d'apprentissage en profondeur. Pour le didacticiel, veuillez vous reporter à Installation de l'environnement d'apprentissage en profondeur pytorch (version GPU) .

Le code de YOLOv5 est open source GitHub - ultralytics/yolov5         sur GitHub . L'utilisation de son code pour réaliser vos propres besoins de détection de cible nécessite trois étapes : 1. Préparer l'ensemble de données ; 2. Configurer les paramètres de code et former le modèle ; 3. Prévoir . L'auteur suivant vous guidera étape par étape pour réaliser votre propre formation de modèle de détection de cible.

1. Préparer le jeu de données

1.1 Collecter des images

        Nous collectons des images pertinentes en fonction de nos propres besoins, et ici nous prenons la reconnaissance de masque comme exemple. Nous avons collecté quelques images sur Internet avec et sans masques, comme indiqué ci-dessous :                

1.2 Utiliser le logiciel labelimg pour étiqueter les photos collectées

1.2.1 Installation du logiciel labelimg

        Le logiciel labelimg est un outil d'étiquetage de données open source qui peut étiqueter trois formats. ① Fichier XML au format balise VOC . ② Fichier txt au format d'étiquette yolo. ③format de balise createML fichier json.

        L'installation de labelimg est très simple, nous ouvrons cmd et entrons la commande suivante :

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

1.2.2 Étiquetage avec le logiciel labelimg 

        Tout d'abord, nous pourrions tout aussi bien créer un dossier nommé VOC2007, et y créer un dossier nommé JPEGImages pour stocker les images que nous avons collectées qui doivent être étiquetées ; puis créer un dossier nommé Annotations pour stocker les fichiers d'étiquettes étiquetés ; enfin créer un txt nommé preset_classes.txt pour stocker le nom de la classe à marquer. La structure est illustrée dans la figure ci-dessous :

        Ce que nous voulons réaliser ici est de détecter si un masque est porté, il n'y a donc que 2 catégories dans le fichier predefined_classes.txt, comme le montre la figure suivante :

                                   

        Ensuite, nous devons ouvrir cmd dans le répertoire de VOC2007 (il doit être dans ce répertoire), et saisir la commande suivante : 

labelimg JPEGImages predefined_classes.txt

         Cette commande consiste à utiliser le logiciel labelimg pour étiqueter les images du dossier JPEGImages selon les catégories du fichier predefined_classes.txt.

        L'interface ouverte est illustrée dans la figure ci-dessous, où

        Open Dir est le dossier à choisir pour stocker les images, et ici notre commande le place par défaut dans le dossier JPEGImages ;

        Change Save Dir est le dossier pour changer l'étiquette de stockage, ici nous utilisons par défaut le dossier Annotations;

        PascalVOC consiste à sélectionner le format d'étiquette.Comme mentionné ci-dessus, il existe trois types principaux.Nous choisissons généralement le format xml de PascalVOC, et le format YOLO convient également, et les deux peuvent être convertis l'un à l'autre;

        Créer RectBox consiste à générer une ligne de position croisée étiquetée pour marquer l'image.

        Après avoir sélectionné la position de détection cible, une boîte de sélection d'étiquette apparaîtra, et nous pouvons sélectionner l'étiquette correspondante, comme indiqué dans la figure ci-dessous. Ensuite, vous pouvez cliquer sur Image suivante pour marquer l'image suivante jusqu'à ce que toutes les images soient marquées.

        Les deux formats d'étiquettes sont illustrés dans la figure ci-dessous :

        Le format xml de PascalVOC :

                                           

        Le format txt de YOLO :

                                     

1.3 Transformation du format d'étiquette et division de l'ensemble d'apprentissage et de l'ensemble de vérification

1.3.1 Convertir les balises au format xml au format txt et diviser l'ensemble de formation (80 %) et l'ensemble de vérification (20 %)

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import random
from shutil import copyfile

classes = ["unmask", "mask"]

TRAIN_RATIO = 80        %训练集的比例


def clear_hidden_files(path):
    dir_list = os.listdir(path)
    for i in dir_list:
        abspath = os.path.join(os.path.abspath(path), i)
        if os.path.isfile(abspath):
            if i.startswith("._"):
                os.remove(abspath)
        else:
            clear_hidden_files(abspath)


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


def convert_annotation(image_id):
    in_file = open('VOCdevkit/VOC2007/Annotations/%s.xml' % image_id)
    out_file = open('VOCdevkit/VOC2007/YOLOLabels/%s.txt' % image_id, '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()


wd = os.getcwd()
wd = os.getcwd()
data_base_dir = os.path.join(wd, "VOCdevkit/")
if not os.path.isdir(data_base_dir):
    os.mkdir(data_base_dir)
work_sapce_dir = os.path.join(data_base_dir, "VOC2007/")
if not os.path.isdir(work_sapce_dir):
    os.mkdir(work_sapce_dir)
annotation_dir = os.path.join(work_sapce_dir, "Annotations/")
if not os.path.isdir(annotation_dir):
    os.mkdir(annotation_dir)
clear_hidden_files(annotation_dir)
image_dir = os.path.join(work_sapce_dir, "JPEGImages/")
if not os.path.isdir(image_dir):
    os.mkdir(image_dir)
clear_hidden_files(image_dir)
yolo_labels_dir = os.path.join(work_sapce_dir, "YOLOLabels/")
if not os.path.isdir(yolo_labels_dir):
    os.mkdir(yolo_labels_dir)
clear_hidden_files(yolo_labels_dir)
yolov5_images_dir = os.path.join(data_base_dir, "images/")
if not os.path.isdir(yolov5_images_dir):
    os.mkdir(yolov5_images_dir)
clear_hidden_files(yolov5_images_dir)
yolov5_labels_dir = os.path.join(data_base_dir, "labels/")
if not os.path.isdir(yolov5_labels_dir):
    os.mkdir(yolov5_labels_dir)
clear_hidden_files(yolov5_labels_dir)
yolov5_images_train_dir = os.path.join(yolov5_images_dir, "train/")
if not os.path.isdir(yolov5_images_train_dir):
    os.mkdir(yolov5_images_train_dir)
clear_hidden_files(yolov5_images_train_dir)
yolov5_images_test_dir = os.path.join(yolov5_images_dir, "val/")
if not os.path.isdir(yolov5_images_test_dir):
    os.mkdir(yolov5_images_test_dir)
clear_hidden_files(yolov5_images_test_dir)
yolov5_labels_train_dir = os.path.join(yolov5_labels_dir, "train/")
if not os.path.isdir(yolov5_labels_train_dir):
    os.mkdir(yolov5_labels_train_dir)
clear_hidden_files(yolov5_labels_train_dir)
yolov5_labels_test_dir = os.path.join(yolov5_labels_dir, "val/")
if not os.path.isdir(yolov5_labels_test_dir):
    os.mkdir(yolov5_labels_test_dir)
clear_hidden_files(yolov5_labels_test_dir)

train_file = open(os.path.join(wd, "yolov5_train.txt"), 'w')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'w')
train_file.close()
test_file.close()
train_file = open(os.path.join(wd, "yolov5_train.txt"), 'a')
test_file = open(os.path.join(wd, "yolov5_val.txt"), 'a')
list_imgs = os.listdir(image_dir)  # list image files
prob = random.randint(1, 100)
print("Probability: %d" % prob)
for i in range(0, len(list_imgs)):
    path = os.path.join(image_dir, list_imgs[i])
    if os.path.isfile(path):
        image_path = image_dir + list_imgs[i]
        voc_path = list_imgs[i]
        (nameWithoutExtention, extention) = os.path.splitext(os.path.basename(image_path))
        (voc_nameWithoutExtention, voc_extention) = os.path.splitext(os.path.basename(voc_path))
        annotation_name = nameWithoutExtention + '.xml'
        annotation_path = os.path.join(annotation_dir, annotation_name)
        label_name = nameWithoutExtention + '.txt'
        label_path = os.path.join(yolo_labels_dir, label_name)
    prob = random.randint(1, 100)
    print("Probability: %d" % prob)
    if (prob < TRAIN_RATIO):  # train dataset
        if os.path.exists(annotation_path):
            train_file.write(image_path + '\n')
            convert_annotation(nameWithoutExtention)  # convert label
            copyfile(image_path, yolov5_images_train_dir + voc_path)
            copyfile(label_path, yolov5_labels_train_dir + label_name)
    else:  # test dataset
        if os.path.exists(annotation_path):
            test_file.write(image_path + '\n')
            convert_annotation(nameWithoutExtention)  # convert label
            copyfile(image_path, yolov5_images_test_dir + voc_path)
            copyfile(label_path, yolov5_labels_test_dir + label_name)
train_file.close()
test_file.close()

 1.3.2 Convertissez le format de balise txt au format xml, puis utilisez la méthode de 1.3.1 pour diviser l'ensemble de données

from xml.dom.minidom import Document
import os
import cv2


# def makexml(txtPath, xmlPath, picPath):  # txt所在文件夹路径,xml文件保存路径,图片所在文件夹路径
def makexml(picPath, txtPath, xmlPath):  # txt所在文件夹路径,xml文件保存路径,图片所在文件夹路径
    """此函数用于将yolo格式txt标注文件转换为voc格式xml标注文件
    在自己的标注图片文件夹下建三个子文件夹,分别命名为picture、txt、xml
    """
    dic = {'0': "unmask",  # 创建字典用来对类型进行转换
           '1': "mask",  # 此处的字典要与自己的classes.txt文件中的类对应,且顺序要一致
           }
    files = os.listdir(txtPath)
    for i, name in enumerate(files):
        xmlBuilder = Document()
        annotation = xmlBuilder.createElement("annotation")  # 创建annotation标签
        xmlBuilder.appendChild(annotation)
        txtFile = open(txtPath + name)
        txtList = txtFile.readlines()
        img = cv2.imread(picPath + name[0:-4] + ".jpg")
        Pheight, Pwidth, Pdepth = img.shape

        folder = xmlBuilder.createElement("folder")  # folder标签
        foldercontent = xmlBuilder.createTextNode("driving_annotation_dataset")
        folder.appendChild(foldercontent)
        annotation.appendChild(folder)  # folder标签结束

        filename = xmlBuilder.createElement("filename")  # filename标签
        filenamecontent = xmlBuilder.createTextNode(name[0:-4] + ".jpg")
        filename.appendChild(filenamecontent)
        annotation.appendChild(filename)  # filename标签结束

        size = xmlBuilder.createElement("size")  # size标签
        width = xmlBuilder.createElement("width")  # size子标签width
        widthcontent = xmlBuilder.createTextNode(str(Pwidth))
        width.appendChild(widthcontent)
        size.appendChild(width)  # size子标签width结束

        height = xmlBuilder.createElement("height")  # size子标签height
        heightcontent = xmlBuilder.createTextNode(str(Pheight))
        height.appendChild(heightcontent)
        size.appendChild(height)  # size子标签height结束

        depth = xmlBuilder.createElement("depth")  # size子标签depth
        depthcontent = xmlBuilder.createTextNode(str(Pdepth))
        depth.appendChild(depthcontent)
        size.appendChild(depth)  # size子标签depth结束

        annotation.appendChild(size)  # size标签结束

        for j in txtList:
            oneline = j.strip().split(" ")
            object = xmlBuilder.createElement("object")  # object 标签
            picname = xmlBuilder.createElement("name")  # name标签
            namecontent = xmlBuilder.createTextNode(dic[oneline[0]])
            picname.appendChild(namecontent)
            object.appendChild(picname)  # name标签结束

            pose = xmlBuilder.createElement("pose")  # pose标签
            posecontent = xmlBuilder.createTextNode("Unspecified")
            pose.appendChild(posecontent)
            object.appendChild(pose)  # pose标签结束

            truncated = xmlBuilder.createElement("truncated")  # truncated标签
            truncatedContent = xmlBuilder.createTextNode("0")
            truncated.appendChild(truncatedContent)
            object.appendChild(truncated)  # truncated标签结束

            difficult = xmlBuilder.createElement("difficult")  # difficult标签
            difficultcontent = xmlBuilder.createTextNode("0")
            difficult.appendChild(difficultcontent)
            object.appendChild(difficult)  # difficult标签结束

            bndbox = xmlBuilder.createElement("bndbox")  # bndbox标签
            xmin = xmlBuilder.createElement("xmin")  # xmin标签
            mathData = int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)
            xminContent = xmlBuilder.createTextNode(str(mathData))
            xmin.appendChild(xminContent)
            bndbox.appendChild(xmin)  # xmin标签结束

            ymin = xmlBuilder.createElement("ymin")  # ymin标签
            mathData = int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)
            yminContent = xmlBuilder.createTextNode(str(mathData))
            ymin.appendChild(yminContent)
            bndbox.appendChild(ymin)  # ymin标签结束

            xmax = xmlBuilder.createElement("xmax")  # xmax标签
            mathData = int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)
            xmaxContent = xmlBuilder.createTextNode(str(mathData))
            xmax.appendChild(xmaxContent)
            bndbox.appendChild(xmax)  # xmax标签结束

            ymax = xmlBuilder.createElement("ymax")  # ymax标签
            mathData = int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)
            ymaxContent = xmlBuilder.createTextNode(str(mathData))
            ymax.appendChild(ymaxContent)
            bndbox.appendChild(ymax)  # ymax标签结束

            object.appendChild(bndbox)  # bndbox标签结束

            annotation.appendChild(object)  # object标签结束

        f = open(xmlPath + name[0:-4] + ".xml", 'w')
        xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
        f.close()


if __name__ == "__main__":
    picPath = "VOCdevkit/VOC2007/JPEGImages/"  # 图片所在文件夹路径,后面的/一定要带上
    txtPath = "VOCdevkit/VOC2007/YOLO/"  # txt所在文件夹路径,后面的/一定要带上
    xmlPath = "VOCdevkit/VOC2007/Annotations/"  # xml文件保存路径,后面的/一定要带上
    makexml(picPath, txtPath, xmlPath)

        Si l'étiquette est au format txt, plusieurs problèmes doivent être pris en compte lors du processus de conversion :

        1. Selon les dernières lignes du code, la note txt doit être stockée dans le dossier YOLO ;

        2. Les balises converties au format xml seront stockées dans le dossier Annotations ;

        3. S'il y a une erreur de .shape, vérifiez s'il y a un fichier class.txt dans le dossier YOLO et supprimez-le.

        Avec cela, la partie de préparation de l'ensemble de données est terminée.

2. Configurez les paramètres de code et entraînez les modèles

2.1 Télécharger le code source

Le code de YOLOv5 est open source GitHub - ultralytics/yolov5          sur GitHub , et nous pouvons télécharger le code source sur le site Web. Comme le montre la figure ci-dessous, nous avons sélectionné ici la version v6.0.

        Nous décompressons le code yolov5 téléchargé, puis l'ouvrons avec pycharm. Après ouverture, l'ensemble du répertoire de code est le suivant :

2.2 Joindre le jeu de données

        Placez l'ensemble de données que vous avez préparé dans le dossier VOCdevkit sous le répertoire du projet et utilisez la méthode de conversion d'étiquettes et de division d'ensemble de données introduite dans 1.3 pour diviser l'ensemble de données, comme illustré dans la figure suivante :

                  

2.3 Configurer les paramètres de code       

1. Sélectionnez l'environnement pytorch configuré dans le coin inférieur droit de pycharm. Si l'environnement n'a pas été installé, veuillez vous reporter à l'article précédent ;

2. Installez les bibliothèques dépendantes requises par yolov5. Ouvrez le terminal de commande de pycharm et entrez la commande suivante, comme indiqué sur la figure :

pip install -r requirements.txt

 3. Téléchargez le fichier de prépesée. Téléchargement de pré-poids du site Web , ici nous utilisons yolov5s.pt , téléchargez-le et placez-le dans le répertoire du projet.

 4. Modifiez le fichier de configuration des données.

① Copiez le fichier VOC.yaml dans le dossier de données de ce répertoire, nommez-le mask.yaml et modifiez-le en vous référant à la figure ci-dessous.

 ②Copiez une copie du fichier yolov5s.yaml sous le dossier modèle de ce répertoire, nommez-le yolov5s_mask.yaml et modifiez-le en vous référant à la figure ci-dessous.

 5. Réglage des paramètres du fichier train

        Comme le montre la figure ci-dessous, la ligne 436 sert à définir le fichier de prépondération, les lignes 437 et 438 définissent le fichier de configuration des données et la ligne 440 définit le nombre d'itérations (vous pouvez le définir vous-même en fonction de votre besoins).

         Une fois les paramètres ci-dessus configurés, vous pouvez exécuter le fichier train.py pour la formation. Mais vous pouvez rencontrer les problèmes suivants :

Question une:

Cela signifie que la mémoire virtuelle n'est pas suffisante. Nous pouvons changer le paramètre num_workers nw à la ligne 117 à 0 en modifiant le fichier datasets.py sous le chemin utils.

 Question deux :

Cela indique un débordement de mémoire GPU. Nous pouvons le résoudre en réduisant la taille du lot et la taille des paramètres des travailleurs.

         Après avoir terminé la configuration des paramètres ci-dessus, nous pouvons former nos propres données. Exécutez le fichier train.py, la colonne Exécuter comme indiqué dans la figure ci-dessous indique que la formation a commencé.

 3. Prévision

        Une fois la formation terminée, le projet aura un dossier runs/train/exp, qui contient les données de poids formées et d'autres fichiers de paramètres, comme illustré dans la figure suivante : 

         Ensuite, nous ouvrons le fichier detect.py et modifions les paramètres suivants. La ligne 269 sert à définir le fichier de poids formé, la ligne 270 sert à définir le dossier d'images ou l'image spécifique que nous voulons détecter ou appeler l'appareil photo, 0 signifie appeler l'appareil photo.

         Une fois le paramétrage terminé, exécutez simplement le fichier detect.py et les résultats de la détection sont enregistrés dans le dossier runs\detect\exp.

         Les résultats des tests sont les suivants :

         De cette façon, nous avons réalisé un projet expérimental basé sur YOLOv5 entraînant notre propre ensemble de données de détection de cible. Êtes-vous surpris par les résultats du test ? Vous pouvez également avoir l'impression que le processus est lourd ou peu clair. La pratique rend parfait, si vous faites plus de pratique, vous pourrez bientôt saisir les points principaux. D'accord, merci à tous, si vous rencontrez d'autres problèmes, vous pouvez communiquer.

Je suppose que tu aimes

Origine blog.csdn.net/cxzgood/article/details/124618506
conseillé
Classement