xml格式:
".xml"格式是可扩展标记语言,因其可以跨越多平台的属性,成为网络数据传输的重要工具。如下图所示,xml格式数据特点就是简单易理解,清晰易操控。我们可以清晰的看到这个数据的文件夹,文件名,路径,来源,格式,目标对象的类别,位置等信息。
txt格式:
“.txt”格式就是最常见的文本类型,这种格式保存的内容简单,占用的内存小。如下图所示,为用notebook打开的一个目标检测数据的类别标签,它有两个目标物体,一行代表一个,类别标签后的四个浮点数代表目标的中心位置x,y和长宽w,h(以一副图片左上角为原点的相对位置)。因txt为保存的目标检测数据没有类别名称,所以通常会在一系列类别文件后,有一个classes.txt,用来保存类名称。
xml格式转换为txt:
通常的voc数据集会有三个文件,一个是JPEGImages,用来保存原始图像;第二个是Annotations,用来保存标签信息,它一般与JPEGImages中的图像一一对应,即JPEGImages中每幅图像在Annotations中有与它一一对应的xml文件;第三个是ImageSets,它包含一个Main的子文件夹,Main中含有至少三个txt文件,分别为train.txt,test.txt,val.txt,记录训练、测试、验证集的图像序号。在接下来的代码中我们分别生成三个文件夹,用来保存训练、测试、验证的txt标签。
xml格式记录的信息多,转向txt也较为简单,只用把相应的信息进行提取即可。
首先导包,都是很简单的包,一般下载编译器自带的。
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
然后获取全部的类别标签,因为xml的类别信息保存在每个xml文件中,没有一起的类别展示。而用于yolo的txt需要一个完整的类别文件描述。
# 获取全部类别标签
#image_id是每个xml的名称
classes = []
def gen_classes(image_id):
in_file = open('%s/Annotations/%s.xml'%(path,image_id))
tree = ET.parse(in_file)
root = tree.getroot()
for obj in root.iter('object'):
cls_name = obj.find('name').text
if cls_name in classes:
pass
else:
classes.append(cls_name)
return classes
将实际位置坐标转换为相对位置坐标的代码,其中size是图片大小,box是目标物品的位置。
#改变坐标格式
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)
读取xml文件中内容并将转换好的信息写入txt文件
#这里是读取xml文件中内容并将转换好的信息写入txt文件的过程,
#主要就是索引前面classes中的名字将名字转换成编号再写入txt
#image_set是train、test、val中的一个
def convert_annotation(image_set,image_id):
in_file = open('%s/Annotations/%s.xml'%(path,image_id))
out_file = open('%s/%s'%(path,image_set)+'labels/%s.txt'%image_id, 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
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))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
调用以上函数,进行实际操作。其中path是文件的路径,sets是需要进行格式转变的文件,一般包括训练集,测试集,验证集。
#路径是文件路径,该路径下应该至少包含三个文件夹:JPEGImages、Annotations、ImageSets
path = 'C:/Users/zy080/Downloads/水面漂浮物数据集-2400/VOCdevkit/VOC2007'
sets = ['train','test','val']
for image_set in sets:
if not os.path.exists('%s/%s'%(path,image_set)+'labels/'):
os.makedirs('%s/%s'%(path,image_set)+'labels/')
image_ids = open('%s/ImageSets/Main/%s.txt'%(path,image_set)).read().strip().split()
for image_id in image_ids:
gen_classes(image_id)
convert_annotation(image_set,image_id)
classes_file = open('%s/%s'%(path,image_set)+'labels/classes.txt','w')
classes_file.write("\n".join([a for a in classes]))
classes_file.close()
总的代码如下:
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
# 获取全部类别标签
classes = []
def gen_classes(image_id):
in_file = open('%s/Annotations/%s.xml'%(path,image_id))
tree = ET.parse(in_file)
root = tree.getroot()
for obj in root.iter('object'):
cls_name = obj.find('name').text
if cls_name in classes:
pass
else:
classes.append(cls_name)
return classes
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_set,image_id):
in_file = open('%s/Annotations/%s.xml'%(path,image_id))
out_file = open('%s/%s'%(path,image_set)+'labels/%s.txt'%image_id, 'w')
tree=ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
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))
bb = convert((w,h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')
path = 'C:/Users/zy080/Downloads/水面漂浮物数据集-2400/VOCdevkit/VOC2007'
sets = ['train','test','val']
for image_set in sets:
if not os.path.exists('%s/%s'%(path,image_set)+'labels/'):
os.makedirs('%s/%s'%(path,image_set)+'labels/')
image_ids = open('%s/ImageSets/Main/%s.txt'%(path,image_set)).read().strip().split()
for image_id in image_ids:
gen_classes(image_id)
convert_annotation(image_set,image_id)
classes_file = open('%s/%s'%(path,image_set)+'labels/classes.txt','w')
classes_file.write("\n".join([a for a in classes]))
classes_file.close()