文章目录[隐藏]
1. 数据准备
如下图所示,训练图片images的格式尺寸可以不等:
标签label(.txt)一个文件对应一个txt文件:
labellmg 标注可以直接导出 .txt文件 一一对应的格式,如图:
上图换了个任务标签,细节不要在意,意思到就行~
如果是labelme生成的Json文件,like this:
json转化一下txt:
import json
import os
name2id = {'advertisement': 0} # 此处为类别id
# name2id = {'person':0,'helmet':1,'Fire extinguisher':2,'Hook':3,'Gas cylinder':4}
#
def convert(img_size, box):
dw = 1. / (img_size[0])
dh = 1. / (img_size[1])
x = (box[0] + box[2]) / 2.0 - 1
y = (box[1] + box[3]) / 2.0 - 1
w = box[2] - box[0]
if w < 0:
w = box[0] - box[2]
h = box[3] - box[1]
if h < 0:
h = box[1] - box[3]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def decode_json(json_floder_path, json_name):
txt_name = txt_floder_path + json_name[0:-5] + '.txt' #
txt_file = open(txt_name, 'w')
json_path = os.path.join(json_floder_path, json_name)
data = json.load(open(json_path, 'r', encoding='gb2312'))
img_w = data['imageWidth']
img_h = data['imageHeight']
for i in data['shapes']:
label_name = i['label']
if (i['shape_type'] == 'rectangle'):
x1 = int(i['points'][0][0])
y1 = int(i['points'][0][1])
x2 = int(i['points'][1][0])
y2 = int(i['points'][1][1])
bb = (x1, y1, x2, y2)
bbox = convert((img_w, img_h), bb)
txt_file.write(str(name2id[label_name]) + " " + " ".join([str(a) for a in bbox]) + '\n')
if __name__ == "__main__":
json_floder_path = 'D:\\advertisement1622+json\\json1615\\' # json文件夹路径
txt_floder_path = 'D:\\advertisement1622+json\\txt1615\\' # txt文件夹路径
json_names = os.listdir(json_floder_path)
for json_name in json_names:
decode_json(json_floder_path, json_name)
生成训练测试验证的总json文件:
"""
YOLO 格式的数据集转化为 COCO 格式的数据集
--root_dir 输入根路径
--save_path 保存文件的名字(没有random_split时使用)
--random_split 有则会随机划分数据集,然后再分别保存为3个文件。
"""
import os
import cv2
import json
from tqdm import tqdm
from sklearn.model_selection import train_test_split
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--root_dir', default='F:\\advertisement1622+json\\advertisementv2\\', type=str,
help="root path of images and labels, include ./images and ./labels and classes.txt")
parser.add_argument('--save_path', type=str, default='./train.json',
help="if not split the dataset, give a path to a json file")
parser.add_argument('--random_split', action='store_false', help="random split the dataset, default ratio is 8:1:1") # store_false = 8:1:1
arg = parser.parse_args() # store_true =train 全部
# test_image = "F:\\advertisement1622+json\\test2017\\" # 新建以下三个文件夹
# val_image = "F:\\advertisement1622+json\\val2017\\"
# train_image = "F:\\advertisement1622+json\\train2017\\"
test_image = os.path.join(arg.root_dir, 'test2017')
val_image = os.path.join(arg.root_dir, 'val2017')
train_image = os.path.join(arg.root_dir, 'train2017')
if not os.path.exists(test_image):
os.makedirs(test_image)
if not os.path.exists(val_image):
os.makedirs(val_image)
if not os.path.exists(train_image):
os.makedirs(train_image)
def train_test_val_split(img_paths, ratio_train=0.889, ratio_test=0.1, ratio_val=0.001,):
# 这里可以修改数据集划分的比例。
assert int(ratio_train + ratio_test + ratio_val) == 1
train_img, middle_img = train_test_split(img_paths, test_size=1 - ratio_train, random_state=233)
ratio = ratio_val / (1 - ratio_train)
val_img, test_img = train_test_split(middle_img, test_size=ratio, random_state=233)
print("NUMS of train:val:test = {}:{}:{}".format(len(train_img), len(val_img), len(test_img)))
return train_img, val_img, test_img
def yolo2coco(root_path, random_split):
originLabelsDir = os.path.join(root_path, 'labels')
originImagesDir = os.path.join(root_path, 'images')
with open(os.path.join(root_path, 'classes.txt')) as f:
classes = f.read().strip().split()
# images dir name
indexes = os.listdir(originImagesDir)
if random_split:
# 用于保存所有数据的图片信息和标注信息
train_dataset = {'categories': [], 'annotations': [], 'images': []}
val_dataset = {'categories': [], 'annotations': [], 'images': []}
test_dataset = {'categories': [], 'annotations': [], 'images': []}
# 建立类别标签和数字id的对应关系, 类别id从0开始。
for i, cls in enumerate(classes, 0):
train_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})
val_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})
test_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})
train_img, val_img, test_img = train_test_val_split(indexes, 0.899, 0.1, 0.001) # 这里可以修改数据集划分的比例。
else:
dataset = {'categories': [], 'annotations': [], 'images': []}
for i, cls in enumerate(classes, 0):
dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'mark'})
# 标注的id
ann_id_cnt = 0
for k, index in enumerate(tqdm(indexes)):
# 支持 png jpg 格式的图片。
txtFile = index.replace('images', 'txt').replace('.jpg', '.txt').replace('.png', '.txt')
# 读取图像的宽和高
im = cv2.imread(os.path.join(root_path, 'images/') + index)
height, width, _ = im.shape
# index = index.replace('.png', '.jpg')
if random_split:
# 切换dataset的引用对象,从而划分数据集
if index in train_img:
dataset = train_dataset
cv2.imwrite(train_image + os.sep + index, im)
# print(train_image + os.sep + index)
elif index in val_img:
dataset = val_dataset
cv2.imwrite(val_image + os.sep + index, im)
# print(val_image + os.sep + index)
elif index in test_img:
dataset = test_dataset
cv2.imwrite(test_image + os.sep + index, im)
# print(test_image + os.sep + index)
# 添加图像的信息
dataset['images'].append({'file_name': index,
'id': k,
'width': width,
'height': height})
if not os.path.exists(os.path.join(originLabelsDir, txtFile)):
# 如没标签,跳过,只保留图片信息。
continue
with open(os.path.join(originLabelsDir, txtFile), 'r') as fr:
labelList = fr.readlines()
for label in labelList:
label = label.strip().split()
x = float(label[1])
y = float(label[2])
w = float(label[3])
h = float(label[4])
# convert x,y,w,h to x1,y1,x2,y2
H, W, _ = im.shape
x1 = (x - w / 2) * W
y1 = (y - h / 2) * H
x2 = (x + w / 2) * W
y2 = (y + h / 2) * H
# 标签序号从0开始计算, coco2017数据集标号混乱,不管它了。
cls_id = int(label[0])
width = max(0, x2 - x1)
height = max(0, y2 - y1)
dataset['annotations'].append({
'area': width * height,
'bbox': [x1, y1, width, height],
'category_id': cls_id,
'id': ann_id_cnt,
'image_id': k,
'iscrowd': 0,
# mask, 矩形是从左上角点按顺时针的四个顶点
'segmentation': [[x1, y1, x2, y1, x2, y2, x1, y2]]
})
ann_id_cnt += 1
# 保存结果
folder = os.path.join(root_path, 'annotations')
if not os.path.exists(folder):
os.makedirs(folder)
if random_split:
for phase in ['instances_train2017', 'instances_val2017', 'instances_test2017']:
json_name = os.path.join(root_path, 'annotations/{}.json'.format(phase))
with open(json_name, 'w') as f:
if phase == 'instances_train2017':
json.dump(train_dataset, f)
elif phase == 'instances_val2017':
json.dump(val_dataset, f)
elif phase == 'instances_test2017':
json.dump(test_dataset, f)
print('Save annotation to {}'.format(json_name))
else:
json_name = os.path.join(root_path, 'annotations/{}'.format(arg.save_path))
with open(json_name, 'w') as f:
json.dump(dataset, f)
print('Save annotation to {}'.format(json_name))
if __name__ == "__main__":
root_path = arg.root_dir
assert os.path.exists(root_path)
random_split = arg.random_split
print("Loading data from ", root_path, "\nWhether to split the data:", random_split)
yolo2coco(root_path, random_split)
自动生成annotations文件夹和里面的train/val/test.json、训练图片、验证、测试图片的文件夹:
最后上传的是以下数据文件:
2.训练开始
2.1 修改文件1.yolox_s.py 修改网络宽度和深度、数据集的位置、种类等参数
./exps/example/custom/yolox_s.py
其中yolox所用的depth和width要和模型大小(S\M\L) ./exps/default/yolox_s.py(yolox_m.py/yolox_l.py) 保持一致,like this:
2.2 修改文件 2.yolox_base.py 同样修改类别、网络宽度和深度等参数
./yolox/exp/yolox_base.py
2.3 修改文件 3.coco_classes.py 改成自己的类别,记得类别之后打“,”逗号,不然会报错
./yolox/data/datasets/coco_classes.py
3.训练起来:
终端执行:
python train.py -f exps/example/custom/yolox_s.py -d 2 -b 16 --fp16 -o -c yolox_s.pth --cache
like this:
※注意!!!!# 遇到的一些坑和问题:
1.直接pip install loguru会报错,用清华镜像安装就没有问题了
pip install loguru -i http://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn
2.AssertionError错误
终端开始训练输入:python train.py -f exps/example/custom/yolox_s.py -d 2 -b 16 --fp16 -o -c yolox_m.pth [--cache]
会出现报错 但是之前不会。
训练的代码在train.py 中,首先对训练的参数进行解析,然后调用get_exp(args.exp_file, args.name) 获取exp,调用exp.merge(args.opts)并merge训练参数,然后启动训练,报错如下:
Traceback (most recent call last):
File "train.py", line 122, in <module>
exp.merge(args.opts)
File "/home/jovyan/YOLOX-main/yolox/exp/base_exp.py", line 64, in merge
assert len(cfg_list) % 2 == 0
AssertionError
代码终端训练改成:
python train.py -f exps/example/custom/yolox_s.py -d 2 -b 16 --fp16 -o -c yolox_s.pth --cache
去掉[–cache]外面的括号,然后正常开始训练~
版权声明:本文为CSDN博主「blue_lala」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44245653/article/details/121144231
暂无评论