yolov5 は独自のデータセットをトレーニングします - ちょっとしたメモ

私自身の研究の過程でいくつかの記録があり、不正確な点がある場合は、批判と修正をお願いします!

データセット形式の変換

データセット (MAR20) を VOC 形式でダウンロードしたので、まずデータセットを yolo が使用できる形式に変換する必要があります。

  1. xml ファイルをマージする

MAR20 データセットの画像は 2 つの xml ファイルに対応します。まず xml ファイルをマージします (私のレベルが限られているため、参照した情報は 1 つの xml ファイルに対応する 1 つの画像であるため、最初に 2 つの xml ファイルをマージしますが、2 つの xml ファイルをマージします)。 xmlファイル ファイルは 2 つのマーキング方法に対応しています。結合できるかどうかは疑問です。誰かが私のジレンマを解決してくれることを願っています)。コードは次のとおりです。

from xml.etree.ElementTree import ElementTree, Element, parse
import xml.etree.ElementTree as ET
import os
import shutil
hole_path = 'D:/ProgramData/code/datasets/MAR20/Annotations/Horizontal Bounding Boxes'
arm_path = 'D:/ProgramData/code/datasets/MAR20/Annotations/Oriented Bounding Boxes'
out_path = 'D:/ProgramData/code/datasets/MAR20/Annotations/xmlcombine'
# 格式化
def __indent(elem, level=0):
    i = "\n" + level*"\t"
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "\t"
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            __indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

for hole_xml in os.listdir(hole_path):
    # 将同名xml合并
    if os.path.exists(os.path.join(arm_path,hole_xml)):
        print('Horizontal Bounding Boxes',hole_xml)
        tree_hole = parse(os.path.join(hole_path,hole_xml))
        root_hole = tree_hole.getroot()  # annotation
        new_hole = tree_hole
        tree_arm = parse(os.path.join(arm_path,hole_xml))
        root_arm = tree_arm.getroot()  # annotation
        object = (tree_arm.findall('object'))
        for i in range(len(object)):
            root_hole.append(object[i])
        __indent(root_hole)
        new_hole.write(os.path.join(out_path,hole_xml))
#不同名xml复制
    else:
     print('copying',hole_xml)
     shutil.copy(os.path.join(hole_path,hole_xml), out_path)
# 将不同名xml复制
    for arm_xml in os.listdir(arm_path):
        if not os.path.exists(os.path.join(out_path,arm_xml)):
            print('copying')
            shutil.copy(os.path.join(arm_path, arm_xml), out_path

2.xml変換txt

データセットのルート ディレクトリの下に新しいラベル フォルダーを作成し、xml ファイルから txt 形式に変換されたファイルを格納します。具体的な操作は次のとおりです。

pycharm プロジェクトのルート ディレクトリに新しい txt_write.py ファイルを作成して、データ セットを分割します。コードは次のとおりです。

# -*- coding: utf-8 -*-
"""
分训练集、验证集和测试集,按照 8:1:1 的比例来分,训练集8,验证集1,测试集1

"""
import os
import random
import argparse

parser = argparse.ArgumentParser()
# xml文件的地址,根据自己的数据进行修改 xml一般存放在Annotations下
parser.add_argument('--xml_path', default='D:/ProgramData/code/datasets/MAR20/Annotations/Horizontal Oriented Bounding Boxes', type=str, help='input xml label path')
# 数据集的划分,地址选择自己数据下的ImageSets/Main
parser.add_argument('--txt_path', default='D:/ProgramData/code/datasets/MAR20/ImageSets/Main', type=str, help='output txt label path')
opt = parser.parse_args()

train_percent = 0.8  # 训练集所占比例
val_percent = 0.1  # 验证集所占比例
test_persent = 0.1  # 测试集所占比例

xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)

if not os.path.exists(txtsavepath):
    os.makedirs(txtsavepath)

num = len(total_xml)
list = list(range(num))

t_train = int(num * train_percent)
t_val = int(num * val_percent)

train = random.sample(list, t_train)
num1 = len(train)
for i in range(num1):
    list.remove(train[i])

val_test = [i for i in list if not i in train]
val = random.sample(val_test, t_val)
num2 = len(val)
for i in range(num2):
    list.remove(val[i])

file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')

for i in train:
    name = total_xml[i][:-4] + '\n'
    file_train.write(name)

for i in val:
    name = total_xml[i][:-4] + '\n'
    file_val.write(name)

for i in list:
    name = total_xml[i][:-4] + '\n'
    file_test.write(name)

file_train.close()
file_val.close()
file_test.close()

xml ファイルを txt 形式に変換して、トレーニング セット、検証セット、およびテスト セットの txt を生成します。txt には、画像のパスが記録されます。

# *coding:utf-8 *
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import cv2

sets = ['train', 'test','val']   
#数据集类别
classes = ['A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11','A12','A13','A14','A15','A16','A17','A18','A19','A20' ]

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('D:/yolov7-main/voc2007/Annotations/%s.xml' % (image_id))  # 修改路径(最好使用全路径)
    # img_file = cv2.imread('JILI_NB\images\%s.jpg' % (image_id))
    # out_file = open('JILI_NB\labels/%s.txt' % (image_id), 'w+')  # 修改路径(最好使用全路径)
    in_file = open('D:/ProgramData/code/datasets/MAR20/Annotations/Horizontal Oriented Bounding Boxes/%s.xml' % (image_id))  # 修改路径(最好使用全路径)
    img_file = cv2.imread('D:/ProgramData/code/datasets/MAR20/JPEGImages/%s.jpg' % (image_id))
    out_file = open('D:/ProgramData/code/datasets/MAR20/Labels/%s.txt' % (image_id), 'w+')  # 修改路径(最好使用全路径)
    tree = ET.parse(in_file)
    root = tree.getroot()
    # size = root.find('size')
    assert img_file is not None
    size = img_file.shape[0:-1]
    h = int(size[0])
    w = int(size[1])
    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))
        ZIP_ONE = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in ZIP_ONE]) + '\n')

wd = getcwd()
for image_set in sets:
    # if not os.path.exists('JILI_NB/labels'):
    #     os.makedirs('JILI_NB/labels/')
    if not os.path.exists('D:/ProgramData/code/datasets/MAR20/Labels'):
        os.makedirs('D:/ProgramData/code/datasets/MAR20/Labels')
    image_ids = open('D:/ProgramData/code/datasets/MAR20/ImageSets/Main/%s.txt' % (image_set)).read().split()  # 修改路径(最好使用全路径)
    list_file = open('D:/ProgramData/code/datasets/MAR20/Labels%s.txt' % (image_set), 'w+')  # 修改路径(最好使用全路径)
    # print(image_ids)
    for image_id in image_ids:
        try:
            print(image_id)
            list_file.write('D:/ProgramData/code/datasets/MAR20/JPEGImages/%s.jpg\n' % (image_id))  # 修改路径(最好使用全路径)
            convert_annotation(image_id)
        except:
            print('error img:', image_id)
    list_file.close()

3. フォルダ構造の変更

上記の操作が完了すると、データセットのファイル形式に問題はなくなりますので、次にフォルダ構造を変更する必要があります。

上記の操作を実行すると、フォルダー構造は次のようになります。さらに変更が必要です

それを下の画像に変更すると、コードは次のようになります: (ここで、shutil.copy() は txt ファイルのパスを呼び出すことに注意してください。生成された txt はパスではなく画像の名前であるため、次のコードは実行時に見つからないことを常に報告ファイルの障害、問題を見つけるのに1日かかりました.この問題のコードを追加し、パスをtxtファイルに書き込んだ後、正常に実行できます)

import shutil
import os
 
file_List = ["test", "val","train" ]
for file in file_List:
    if not os.path.exists('D:/ProgramData/code/yolov5-master/MAR20/images/%s' % file):
        os.makedirs('D:/ProgramData/code/yolov5-master/MAR20/images/%s' % file)
    if not os.path.exists('D:/ProgramData/code/yolov5-master/MAR20/labels/%s' % file):
        os.makedirs('D:/ProgramData/code/yolov5-master/MAR20/labels/%s' % file)
    print(os.path.exists('../tmp/ImageSets/Main/%s.txt' % file))
    f = open('../tmp/ImageSets/Main/%s.txt' % file, 'r')
    lines = f.readlines()
 '''
#将路径写进txt文件,如果生成的txt文件为图片路径则不需要
    f.seek(0)
    f.truncate()  # 先将原来文件进行清空
    for line_list in lines:  # 对于原来文件的内容每一行进行添加的操作
        line_list = line_list.replace("\n", "")
        line_new ="D:\ProgramData\code\yolov5-master\tmp\JPEGImages\" + line_list + ".jpg "+"\n"
        f.write(line_new)
    f.close()
'''
for line in lines:
        print(line)
        line = "/".join(line.split('/')[-5:]).strip()
        shutil.copy(line, "../MAR20/images/%s" % file)
        #shutil.copyfile(line, "../MAR20/images/%s" % file)
        #shutil.copymode(line, "../MAR20/images/%s" % file)
        line = line.replace('JPEGImages', 'Labels')
        line = line.replace('jpg', 'txt')
        shutil.copy(line, "../MAR20/labels/%s/" % file)

この時点で、データ セットの変換はすべて完了です。

構成ファイル

4. pycharm プロジェクトの同じレベルのフォルダーに新しい datastes フォルダーを作成します。

データセット フォルダーは、データセットを格納するために使用され、生成されたデータセット フォルダーをデータセット フォルダーに配置してから、ファイルの構成を開始します。

まず、プロジェクト ディレクトリのデータ フォルダーに新しい plane.yaml ファイルを作成します。

train: D:\ProgramData\code\datasets\MAR20\images\train
val: D:\ProgramData\code\datasets\MAR20\images\val
test: D:\ProgramData\code\datasets\MAR20\images\test

nc: 20 #有多少个类
#类别名称
names: ['A1','A2','A3','A4','A5','A6','A7','A8','A9','A10','A11','A12','A13','A14','A15','A16','A17','A18','A19','A20']

次に、プロジェクトのルート ディレクトリにある train.py ファイルを変更します。

#yolov5有smlx四个模型,选择哪个模型设置哪个模型路径(所有的路径都最好是全路径,不用ROOT代替)
parser.add_argument('--cfg', type=str, default='D:/ProgramData/code/yolov5-master/models/yolov5s.yaml', help='model.yaml path')
#调用自己设置的yaml文件
parser.add_argument('--data', type=str, default= 'D:/ProgramData/code/yolov5-master/data/airplane.yaml', help='dataset.yaml path')

これらが完了したら、トレーニングを開始できます。

Extension: epochs: 必要に応じて変更できるトレーニングのラウンド数 (デフォルトは 100)。

バッチサイズ: 各トレーニングで選択されたサンプルの数。たとえば、3073 個のサンプルがある場合、バッチサイズ = 16 の場合、各トレーニングのサンプル数は 193 です。

おすすめ

転載: blog.csdn.net/weixin_39424706/article/details/129215456