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.