这篇文章主要参考博客Yolo标准数据集格式转Voc数据集中的代码,对原博客代码进行一定修改、添加注释,此外还在后面添加了我自己写的一段关于对转换后的标注文件进行整理的脚本代码。
Yolo标注的格式与VOC格式不同之处在于:
(1)Yolo格式下的每张图片的所有包含的目标的标注信息,都统一以txt文件的形式储存。一张图片对应一个与其相同名称的txt文件。在txt文件中,每一行对应图片中一个目标的信息,用一个数字指代种类编号,剩下四个数字代表坐标信息。
VOC格式则是每张图片对应一个与其相同名称的xml格式文件。
(2)Yolo格式的数据集,是将训练数据和验证(测试)数据分成两个文件夹,例如训练数据文件夹下包括:训练图片文件夹、训练图片对应的标注文件(txt格式)的文件夹。
VOC格式则是将所有图片(无论是训练图片还是测试图片)都放在一个文件夹下、将所有标注文件(无论是训练的还是测试的)也都放在一个文件夹下,最后用两个txt格式的文件来指定那些是训练数据,哪些是测试数据:例如,train.txt中将所有训练数据(训练图片的名称,去掉.jpg后缀)按每行对应一个的方式储存,test.txt则将所有测试数据的名称(去掉后缀)储存起来。
由此,我们在将Yolo格式的数据集转换成voc格式的数据集时,主要有两部分工作:
(1)将所有的txt标注文件转化成xml标注文件。(2)将yolo数据集的整理形式转化成voc的整理形式:包括将分成train和val(test)两个部分的图片、标注文件合到一起,同时将所有训练集数据名称(去掉后缀)写入到train.txt、将所有测试集数据名称(去掉后缀)写入到test.txt。
以上介绍的转换工作对应的代码均在下面列出。
1.将txt标注文件批量转换成xml标注文件
import xml.dom.minidom
import glob
from PIL import Image
from math import ceil
import shutil
yolo_file = r'/home/dwt/DataSets/MSAR/MSAR-1.0/val/yolo_labels'#yolo格式下的存放txt标注文件的文件夹
turn_xml_file = r'/home/dwt/DataSets/MSAR/MSAR-1.0/val/voc_labels'#转换后储存xml的文件夹地址
img_file = r'/home/dwt/DataSets/MSAR/MSAR-1.0/val/images'#存放图片的文件夹
labels = ['船只', '油罐','飞机', '桥梁']
src_img_dir = img_file
src_txt_dir = yolo_file
src_xml_dir = turn_xml_file #转换后储存xml的文件夹地址
img_Lists = glob.glob(src_img_dir + '/*.jpg')
img_basenames = []
for item in img_Lists:
img_basenames.append(os.path.basename(item))#os.path.basename返回path最后的文件名
img_names = []
for item in img_basenames:
temp1, temp2 = os.path.splitext(item) #os.path.splitext(“文件路径”) 分离文件名与扩展名
img_names.append(temp1)
total_num = len(img_names) #统计当前总共要转换的图片标注数量
count = 0 #技术变量
for img in img_names: #这里的img是不加后缀的图片名称,如:'GF3_SAY_FSI_002732_E122.3_N29.9_20170215_L1A_HH_L10002188179__1__4320___10368'
count +=1
if count % 1000 == 0:
print("当前转换进度{}/{}".format(count,total_num))
im = Image.open((src_img_dir + '/' + img + '.jpg'))
width, height = im.size
#打开yolo格式下的txt文件
gt = open(src_txt_dir + '/' + img + '.txt').read().splitlines()
if gt:
# 将主干部分写入xml文件中
xml_file = open((src_xml_dir + '/' + img + '.xml'), 'w')
xml_file.write('\n')
xml_file.write(' VOC2007 \n')
xml_file.write(' ' + str(img) + '.jpg' + ' \n')
xml_file.write(' \n')
xml_file.write(' ' + str(width) + ' \n')
xml_file.write(' ' + str(height) + ' \n')
xml_file.write(' 3 \n')
xml_file.write(' \n')
# write the region of image on xml file
for img_each_label in gt:
spt = img_each_label.split(' ') # 这里如果txt里面是以逗号‘,’隔开的,那么就改为spt = img_each_label.split(',')。
xml_file.write(' \n')
xml_file.write(' ')
else:
# 将主干部分写入xml文件中
xml_file = open((src_xml_dir + '/' + img + '.xml'), 'w')
xml_file.write('\n')
xml_file.write(' VOC2007 \n')
xml_file.write(' ' + str(img) + '.jpg' + ' \n')
xml_file.write(' \n')
xml_file.write(' ' + str(width) + ' \n')
xml_file.write(' ' + str(height) + ' \n')
xml_file.write(' 3 \n')
xml_file.write(' \n')
xml_file.write(' ')
2. 将所有训练集、测试集数据名称(去掉后缀)分别写入到train.txt、test.txt
#将转换后的xml文件按train 和test 归类到train.txt和test.txt中
path = r'/home/dwt/DataSets/MSAR/MSAR-1.0'
xml_Lists = glob.glob(src_xml_dir + '/*.xml')
xml_basenames = []
for item in xml_Lists:
xml_basenames.append(os.path.basename(item))
xml_names = [] #这里是将xml文件去掉.xml后缀储存到的列表中
for item in xml_basenames:
temp1, temp2 = os.path.splitext(item) # os.path.splitext(“文件路径”) 分离文件名与扩展名
xml_names.append(temp1)
txt_file = open((path + '/val.txt'),'w')
for item in xml_names:
txt_file.write(str(item)+'\n')
3.将分成train和val(test)两个部分的图片、标注文件和到一起(分别将yolo下的训练部分和测试部分的数据复制到同一文件夹下)
#复制train和val下的文件到一个文件夹中
base_src = r'/home/dwt/DataSets/MSAR/MSAR-1.0/val/images'#要复制的文件所在的文件夹
base_dst = r'/home/dwt/MyCode/pycharm_projects/YOLOX_offical/datasets/VOCdevkit/VOC2011/JPEGImages'#将文件复制到的目标文件夹
list_src = glob.glob(base_src + '/*.jpg')#复制的所有文件
list_src_basenames = []
for item in list_src:
list_src_basenames.append(os.path.basename(item))
total_num = len(list_src_basenames)
c = 0
for item in list_src_basenames:
c += 1
if c % 1000 == 0:
print('当前已复制{}/{}'.format(c,total_num))
shutil.copyfile(os.path.join(base_src,item),os.path.join(base_dst,item))
4.当训练集数据过多,我们还可以再用下面的代码,来从已有的训练集中随机抽取一部分数据进行训练,来加快训练速度。
由于VOC格式的标注中是将训练集、测试集数据混在一起存放,通过train.txt文件来指定那些数据是训练集数据,所以要实现随机抽取部分数据进行训练可以通过在原train.txt文件中随机抽取一部分写入新的train.txt,实现代码如下:
#由于数据量过大,计划将数据集的训练集部分抽出一部分来训练,减少训练花费时间
import random
train_txt = r'/home/dwt/MyCode/pycharm_projects/YOLOX_offical/datasets/VOCdevkit/VOC2011/ImageSets/Main/train.txt'#原先分好的储存训练集信息的train.txt
train_sample_txt = r'/home/dwt/MyCode/pycharm_projects/YOLOX_offical/datasets/VOCdevkit/VOC2011/ImageSets/Main/train_sample.txt'
sample_ratio = 0.1 #指定抽取的数据占原来数据的比例
train_data_list = []
train_file = open(train_txt)
for line in train_file.readlines():
train_data_list.append(line)
total_num = len(train_data_list)
total_id_list = range(total_num)
train_sample_num = int(total_num * sample_ratio)
sample_id_list = random.sample(total_id_list,train_sample_num)
train_sample_file = open(train_sample_txt,'w')
for i in total_id_list:
if i in sample_id_list:
name = train_data_list[i]
train_sample_file.write(name)
train_sample_file.close()