yolov5 训练自己的数据集,怎样组织数据集结构
yolov5官方写了一个教程来说明怎么用yolov5模型来训练自己的数据集,https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data,这里对官方文档的一些关键点进行说明。
1.创建自己的data.yaml
在训练时,需要在yolov5/data/
文件夹中为自己的数据集创建一个data.yaml
文件,指明数据集图片和标签的存放地址,以及类别的数目和名称,首先来看官方的coco128.yaml
文件
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: ../coco128/images/train2017/ #指定训练集图片存放的地址
val: ../coco128/images/train2017/
# number of classes
nc: 80
# class names
names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard',
'cell phone', 'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors',
'teddy bear', 'hair drier', 'toothbrush']
图片的存放地址可以用三种方式来表达
-
给定一个 目录:里面存放着图片的路径加图片名 见
coco128.yaml
-
给定一个 txt文件:里面存放着图片的路径和图片名,见
coco.yaml
-
给定一个 列表
比如对coco128数据集进行训练
python train.py --img 640 --batch 16 --epochs 5 --data coco128.yaml --weights yolov5s.pt
train.py
在yolov5
文件夹下,此时的工作路径就是yolov5,在coco128.yaml
中指定了train(训练集的路径),那么系统就会在yolov5+train这个路径下去寻找训练集的图片,也就是
../coco128/images/train2017/
所以如果按照这个路径命名方式,就要将coco128文件夹放在yolov5相邻的地方,而不是作为yolov5的子文件夹。(当然也可以通过修改train的值去指定自己其他任何路径,但是要记住是以yolov5作为当前工作目录)
在data.yaml文件中既然没有定义labels的路径,那么 YOLOV5是怎么定义label路径的呢?
YOLOv5 locates labels automatically for each image by replacing the last instance of /images/
in each image path with /labels/
.
也就是如果图片的路径为:
../coco128/images/train2017/img001.jpg
那么labels必须为
../coco128/labels/train2017/img001.txt
一张图片对应一个txt文件,路径除了将最后一个images换成labels,没有任何不同不然就会找不到labels。
具体格式要求
- One row per object
- Each row is
class x_center y_center width height
format. - Box coordinates must be in normalized xywh format (from 0 - 1). If your boxes are in pixels, divide
x_center
andwidth
by image width, andy_center
andheight
by image height. - Class numbers are zero-indexed (start from 0).
最后根据自己最终预测出的类别的数量和名称,修改 nc 和 names两项,就可以开始训练了。
我的数据集
mydata.yaml
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: /home/yyz/yolov5/mydataset/ppe_yolov5_dataset/train.txt # 118287 images
val: /home/yyz/yolov5/mydataset/ppe_yolov5_dataset/valid.txt # 5000 images
test: /home/yyz/yolov5/mydataset/ppe_yolov5_dataset/test.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794
# number of classes
nc: 4
# class names
names: [ 'W', 'WH', 'WV', 'WHV' ]
train.txt
/home/yyz/yolov5/mydataset/ppe_yolov5_dataset/images/image_from_china(1).jpg
/home/yyz/yolov5/mydataset/ppe_yolov5_dataset/images/image_from_china(1).jpg
/home/yyz/yolov5/mydataset/ppe_yolov5_dataset/images/image_from_china(10).jpg
....
....
/home/yyz/yolov5/mydataset/ppe_yolov5_dataset/images/labels
image_from_china\(279\).txt
0 0.400493 0.397366 0.026316 0.122942
1 0.849095 0.496707 0.064967 0.12843
0 0.150082 0.350714 0.028783 0.113063
转换脚本
# coding:utf-8
#将第一列拆分出来
f = open('Labels\\pictor_ppe_crowdsourced_approach-02_test.txt') # 打开txt文件
line = f.readline() # 以行的形式进行读取文件
list1 = []
while line:
a = line.split()
b = a[0:1] # 这是选取需要读取的列
list1.append(b) # 将其添加在列表之中
line = f.readline()
f.close()
path_out = 'test.txt' # 新的txt文件
t = ''
with open(path_out, 'w+') as f_out:
for i in list1:
for j in range(len(list1[0])):
t = t + str(i[j])
f_out.write(t)
f_out.write('\n')
t = ''
f=open('test.txt')
lines=f.readlines() #整行读取
f.close()
for line in lines:
rs = line.rstrip('\n') # 去除原来每行后面的换行符,但有可能是\r或\r\n
#newname = rs.replace(rs)
newfile = open('test.txt', 'a')
newfile.write(rs + '\n')
newfile.close()
"""
将 filename <object-class> <x> <y> <width> <height>
image_from_china(1015).jpg 137,556,186,703,1 246,544,323,685,1 0,614,23,725,0 477,131,512,179,1 906,76,938,146,1 926,41,953,109,1
image_from_china(1023).jpg 764,0,1215,688,0
.....
格式的labels文件转换为
filename.txt中有class id, x, y, w, h
并进行标准化
"""
import os,sys
from PIL import Image
if not os.path.exists("labels"):
os.makedirs("labels")
f = open('Labels\\pictor_ppe_crowdsourced_approach-02_valid.txt') # 打开txt文件
line = f.readline() # 以行的形式进行读取文件
files_name = [] #定义一个空字符串来装第一列
files_value = []
while line:
a = line.split()
list1 = a[0] # 假设第N次运行,第N行的第一列
im = Image.open('ppe_yolov5_dataset\\Images\\' + list1)
im_w = im.size[0]
im_h = im.size[1]
v5label = ''
for i in range( len(a)-1 ):
#obj = a[i] #将文件名去掉了,一个单独的框
#'left(x1),top(y1),right(x2),bottom(y2),class'
leftx1 = a[i+1].split(',')[0]
topy1 = a[i + 1].split(',')[1]
rightx2 = a[i + 1].split(',')[2]
bottomy2 = a[i + 1].split(',')[3]
objclass = a[i + 1].split(',')[4]
x_truth = (int(rightx2) + int(leftx1)) / 2
y_truth = (int(bottomy2) + int(topy1)) / 2
w_truth = (int(rightx2) - int(leftx1))
h_truth = (int(bottomy2) - int(topy1))
x = round((x_truth / im_w),6)
y = round((y_truth / im_h),6)
w = round((w_truth / im_w),6)
h = round((h_truth / im_h),6)
# <object-class> <x> <y> <width> <height>,且归一化,空格隔开,直接加逗号是元祖
v5label = v5label + objclass+' '+str(x)+' '+str(y)+' '+str(w)+' '+str(h)+'\n'
files_name = list1[:-4] # 去掉字符串最后四个 .jpg后缀
fs = open("ppe_yolov5_dataset\\labels\\valid\\" + files_name + ".txt", "w")
fs.write(v5label)
fs.close()
# files_name = list1[:-4] #去掉字符串最后四个 .jpg后缀
# with open("test\\"+files_name+".txt", "w") as fs:
# fs.write(files_value)
# fs.close() #不要跟f重复,记得关闭文件
line = f.readline() #这样就会读取下一行
f.close()
#将图片转换为train.txt文件中包括了所有的训练集图片的路径
f = open('Labels\\pictor_ppe_crowdsourced_approach-02_valid.txt') # 打开txt文件
line = f.readline() # 以行的形式进行读取文件
list1 = []
img_dir = ''
while line:
qianzhui = ['/home/yyz/yolov5/mydataset/ppe_yolov5_dataset/images/']
a = line.split()
b = a[0:1] # 这是选取需要读取的列
qianzhui.extend(b)
img_dir = img_dir + ''.join(qianzhui) + '\n'
fs = open('valid.txt','a')
fs.write(img_dir)
fs.close()
line = f.readline()
f.close()
path_out = 'test.txt' # 新的txt文件
t = ''
with open(path_out, 'w+') as f_out:
for i in list1:
for j in range(len(list1[0])):
t = t + str(i[j])
f_out.write(t)
f_out.write('\n')
t = ''