SSDD数据集共1160张图片,该数据集使用voc格式标注,原始数据并没有划分好,需要自行划分。
在网上查找到voc格式数据集的相关划分代码,自己对其进行一定修改后可以成功运行,现在将代码记录如下:
'''
本脚本用来将所有的xml标注文件,随机划分成事先设定比例的训练集、验证集、训练验证集、测试集;结果是输出储存这些文件名(只是文件名而没有'.xml'这个后缀)的txt文件;从而得到到voc格式的数据集
本脚本在文件将数据集按7:2:1的比例分成训练集train、验证集val、测试集test
'''
import os
import random
random.seed(0)
xmlfilepath = r'/home/dwt/DataSets/SSDD_train_val_test/newAnnotations'
saveBasePath=r"/home/dwt/DataSets/SSDD_train_val_test/ImageSets/Main/"
#----------------------------------------------------------------------#
# 想要增加测试集修改trainval_percent
# train_percent不需要修改
#----------------------------------------------------------------------#
train_percent = 0.7 #训练集在数据集中的比例
val_percent = 0.2 #验证集在数据集中的比例
trainval_percent = 0.9 #训练集和验证集加在一起在数据集中的比例
test_percent = 0.1 #测试集在数据集中的比例
temp_xml = os.listdir(xmlfilepath) #os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表。
total_xml = []
for xml in temp_xml:
if xml.endswith(".xml"):
total_xml.append(xml)
num=len(total_xml) #num表示所有标注文件的总数量
total_list=range(num) #total_list是从0到1的总元素数量为num的列表。与total_xml列表一一对应
train_num = int(num*train_percent)#train_num表示所有标注文件中用于训练集的标注文件的数量
val_num = int(num*val_percent) #val_num表示所有标注文件中用于验证集的标注文件的数量
trainval_num = int(num*trainval_percent) #trainval_num表示所有标注文件中用于训练集和验证集的标注文件的数量
test_num = int(num*test_percent) #test_num表示所有标注文件中用于测试集的标注文件的数量
trainval_list = random.sample(total_list,trainval_num)#trainval_list时存储用于训练集和验证集的标注文件的序号的列表 #trainval = random.sample(list,tv)函数从列表list不分大小顺序的抽取tv个数,并抽取结果以列表的形式传给trainval
train_list = random.sample(trainval_list,train_num) #train_list是储存作为训练集中用于训练的标注文件在total_list列表中的序号
print("train and val size",trainval_num)
print("train size: {} , val size : {} , test size : {}".format(train_num,val_num,test_num))
ftrainval = open(os.path.join(saveBasePath,'trainval.txt'), 'w')
ftest = open(os.path.join(saveBasePath,'test.txt'), 'w')
ftrain = open(os.path.join(saveBasePath,'train.txt'), 'w')
fval = open(os.path.join(saveBasePath,'val.txt'), 'w')
for i in total_list:
name=total_xml[i][:-4]+'\n' #因为voc格式数据集的储存训练和测试集信息的txt文件是只有文件名的,所以诸如“aa.xml”的文件只将“aa”写入到储存训练和测试集信息的txt文件中
if i in trainval_list:
ftrainval.write(name)
if i in train_list:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
下面是将voc格式数据集注释(xml文件)转换到coco格式数据集(json)文件的代码(其中parseXMLFiles函数是将给定文件夹下的xml标注文件统一转换成json文件,parseXMLFiles_by_txt函数具体功能与parseXMLFiles函数一样,只是会根据voc格式数据集中区分训练集和测试集数据的train.txt和test.txt文件来将对应的xml文件转换成json文件):
注:本段内容参考了博客VOC数据集格式转化成COCO数据集格式中的代码,对其原代码添加了注释方便理解,并对细节出做出适当修改。
import xml.etree.ElementTree as ET
import os
import json
coco = dict()
coco['images'] = []
coco['type'] = 'instances'
coco['annotations'] = []
coco['categories'] = []
category_set = dict() #该字典负责统计该数据集中出现的目标种类名称及其对应的id值
image_set = set()
category_item_id = -1
image_id = 0
annotation_id = 0
def addCatItem(name): #该函数对json文件中的categories这一项添加内容
global category_item_id #category_item_id原本是该函数之外的变量,使用global关键字作用于category_item_id上后,该函数内对category_item_id的操作会影响到函数外category_item_id的值
category_item = dict()
category_item['supercategory'] = 'none'
category_item_id += 1
category_item['id'] = category_item_id
category_item['name'] = name
coco['categories'].append(category_item)
category_set[name] = category_item_id #对category_set字典添加内容
return category_item_id
def addImgItem(file_name, size):
global image_id
if file_name is None:
raise Exception('Could not find filename tag in xml file.')
if size['width'] is None:
raise Exception('Could not find width tag in xml file.')
if size['height'] is None:
raise Exception('Could not find height tag in xml file.')
image_id += 1
image_item = dict()
image_item['id'] = image_id
image_item['file_name'] = file_name
image_item['width'] = size['width']
image_item['height'] = size['height']
coco['images'].append(image_item)
image_set.add(file_name)
return image_id
def addAnnoItem(object_name, image_id, category_id, bbox):
global annotation_id
annotation_item = dict()
annotation_item['segmentation'] = []
seg = []
# bbox[] is x,y,w,h
# left_top
seg.append(bbox[0])
seg.append(bbox[1])
# left_bottom
seg.append(bbox[0])
seg.append(bbox[1] + bbox[3])
# right_bottom
seg.append(bbox[0] + bbox[2])
seg.append(bbox[1] + bbox[3])
# right_top
seg.append(bbox[0] + bbox[2])
seg.append(bbox[1])
annotation_item['segmentation'].append(seg)
annotation_item['area'] = bbox[2] * bbox[3]
annotation_item['iscrowd'] = 0
annotation_item['ignore'] = 0
annotation_item['image_id'] = image_id
annotation_item['bbox'] = bbox
annotation_item['category_id'] = category_id
annotation_id += 1
annotation_item['id'] = annotation_id
coco['annotations'].append(annotation_item)
def _read_image_ids(image_sets_file):
ids = []
with open(image_sets_file) as f:
for line in f:
ids.append(line.rstrip())
return ids
"""通过txt文件生成"""
# split ='train' 'va' 'trainval' 'test'
def parseXmlFiles_by_txt(data_dir, json_save_path, split='train'): #功能与parseXmlFiles相同,只是parseXmlFiles_by_txt根据txt文件中指示的文件进行对应xml的转化
print("hello")
labelfile = split + ".txt"
image_sets_file = data_dir + "/ImageSets/Main/" + labelfile
ids = _read_image_ids(image_sets_file)
for _id in ids:
xml_file = data_dir + f"/Annotations/{_id}.xml"
bndbox = dict()
size = dict()
current_image_id = None
current_category_id = None
file_name = None
size['width'] = None
size['height'] = None
size['depth'] = None
tree = ET.parse(xml_file)
root = tree.getroot()
if root.tag != 'annotation':
raise Exception('pascal voc xml root element should be annotation, rather than {}'.format(root.tag))
# elem is , , ,
下面的代码是在将SSDD从voc格式转换到coco格式(即:将voc格式标注信息xml文件转换到coco格式标注信息json文件)之后,再根据train、val、test这三个json文件中对应的图片归类成对应的三个文件夹:image_train、image_val、image_test,所写的脚本的代码记录:
(1)初始代码
#move_rename_image.py
#在这个脚本中实现根据annotation中图片id与图片名称来对image图片改名为其对应id并另存到单独的文件夹中
import os
import json
import argparse
import shutil
parser = argparse.ArgumentParser()
parser.add_argument('--source_path', default='D:/engineering/intrusion detection/dataset/preliminary_contest_crane_federal',type=str)
parser.add_argument('--save_path', default='D:/engineering/intrusion detection/dataset/diaoche_data', type=str, help="specify where to save the output dir of labels")
arg = parser.parse_args()
c = 0 #设置这个变量用来计数处理的标签数,在训练过程中用print打印c来指示进度
if __name__ == '__main__':
json_file = os.path.join(arg.source_path, 'annotations', 'train2.json')
#image_file_catalog = os.path.join(arg.save_path, 'images')
data = json.load(open(json_file, encoding="utf-8"))
images = data['images']
#在下面的循环中将图片改成其对应id的名称并另存到指定的文件夹
#在处理train2中的数据时,因为计划将train2中图片合在train1之后,故train2图片的重命名根据其对应annotation中id号加1159
for img_info in images:
img_name = img_info['file_name']
img_id = img_info['id']
src = os.path.join(arg.source_path,'images','train2',img_name)
dst = os.path.join(arg.save_path,'images',f'{str(img_id + 1159)}.jpg')
shutil.copy(src,dst)
c += 1
if c % 50 == 0:
print("当前已经重命名并另存%d个吊机图片" % (c))
(2)根据上面的初始代码又改写的代码
#image_tidy.py
'''在voc转化成coco格式后,需要将原本混在一起的图片分成train、val、test这三个文件夹'''
import os
import json
import argparse
import shutil
# train_txt = '/home/dwt/DataSets/SSDD_train_val_test/VOCdevkit/VOC2011/ImageSets/Main/train.txt'
# val_txt = '/home/dwt/DataSets/SSDD_train_val_test/VOCdevkit/VOC2011/ImageSets/Main/val.txt'
# test_txt = '/home/dwt/DataSets/SSDD_train_val_test/VOCdevkit/VOC2011/ImageSets/Main/test.txt'
train_json = '/home/dwt/DataSets/SSDD_train_val_test/coco_format/voc2011_train.json'
val_json = '/home/dwt/DataSets/SSDD_train_val_test/coco_format/voc2011_val.json'
test_json = '/home/dwt/DataSets/SSDD_train_val_test/coco_format/voc2011_test.json'
src_front = '/home/dwt/DataSets/SSDD_train_val_test/VOCdevkit'
dst_front = '/home/dwt/DataSets/SSDD_train_val_test/coco_format'
files = ["train","val","test"]
for file in files:
if file == "train":
dir_json = train_json
dst_file = os.path.join(dst_front,"image_train")
if file == "val":
dir_json = val_json
dst_file = os.path.join(dst_front, "image_val")
if file == "test":
dir_json = test_json
dst_file = os.path.join(dst_front, "image_test")
data = json.load(open(dir_json,encoding="utf-8"))
images = data['images']
for img_info in images:
img_dir = img_info["file_name"]# 例如:"VOC2011/JPEGImages/000078.jpg"
src = os.path.join(src_front,img_dir)
dst = os.path.join(dst_file,img_dir[-10:])
shutil.copy(src,dst)
print("done")
print("Done!")