先用这段代码进行初步的转化
参考:谷歌Open Image v4数据集按指定类目下载并批量转化为VOC格式 - Anleenoの小屋 (anleenoxu.top)
import math
import shutil
import cv2
from lxml.etree import Element, SubElement, tostring
from xml.dom.minidom import parseString
import os
# 对于每一个label.txt,制作VOC格式的xml文件
# param1:../OID/UnRestraint_Of_MarineCreature_In_OpenImagev4/Dataset/train/Seahorse/
# param2:imglabel.txt
# param3:annotation
def make_xml(txt_root_path, txt_name_path, save_xml_path):
txt_path = txt_root_path + "Label/" + txt_name_path
pic_path = txt_root_path
pic = cv2.imread(pic_path + txt_name_path.replace(".txt", ".jpg"))
shutil.copy(pic_path + txt_name_path.replace(".txt", ".jpg"), "VOC2007/JPEGImages")
shape = pic.shape
f = open(txt_path, 'r')
data = []
for line in f:
data.append(line.strip())
print(data)
f.close()
# lines = str(line).split(' ')
# box_num = len(data)
pic_name = txt_name_path.replace(".txt", ".jpg")
node_root = Element('annotation')
node_folder = SubElement(node_root, 'folder')
node_folder.text = 'JPEGImages'
node_filename = SubElement(node_root, 'filename')
# 图片名字
node_filename.text = pic_name
# 图像尺寸
node_size = SubElement(node_root, 'size')
node_width = SubElement(node_size, 'width')
node_width.text = str(shape[1])
node_height = SubElement(node_size, 'height')
node_height.text = str(shape[0])
node_depth = SubElement(node_size, 'depth')
node_depth.text = str(shape[2])
# 第二层循环遍历有多少个框
for dat in data:
dat = dat.split(" ")
print(len(dat))
print(dat)
# 分支把标签合并
if len(dat) == 5:
Label = dat[0]
Xmin = math.floor(float(dat[1]))
Ymin = math.floor(float(dat[2]))
Xmax = math.ceil(float(dat[3]))
Ymax = math.ceil(float(dat[4]))
# Xmin = int(float(dat[1]))
# Ymin = int(float(dat[2]))
# Xmax = int(float(dat[3]))
# Ymax = int(float(dat[4]))
print(Label, Xmin, Ymin, Xmax, Ymax)
# img = cv2.rectangle(img, (Xmin, Ymin), (Xmax, Ymax), (0, 255, 0), 1)
cls_name = Label
node_object = SubElement(node_root, 'object')
node_name = SubElement(node_object, 'name')
# 类别名字
node_name.text = cls_name
node_bndbox = SubElement(node_object, 'bndbox')
node_xmin = SubElement(node_bndbox, 'xmin')
node_xmin.text = str(Xmin)
node_ymin = SubElement(node_bndbox, 'ymin')
node_ymin.text = str(Ymin)
node_xmax = SubElement(node_bndbox, 'xmax')
node_xmax.text = str(Xmax)
node_ymax = SubElement(node_bndbox, 'ymax')
node_ymax.text = str(Ymax)
else:
Label = dat[0] + dat[1]
Xmin = math.floor(float(dat[2]))
Ymin = math.floor(float(dat[3]))
Xmax = math.ceil(float(dat[4]))
Ymax = math.ceil(float(dat[5]))
# Xmin = int(float(dat[2]))
# Ymin = int(float(dat[3]))
# Xmax = int(float(dat[4]))
# Ymax = int(float(dat[5]))
print(Label, Xmin, Ymin, Xmax, Ymax)
# img = cv2.rectangle(img, (Xmin, Ymin), (Xmax, Ymax), (0, 255, 0), 1)
cls_name = Label
node_object = SubElement(node_root, 'object')
node_name = SubElement(node_object, 'name')
# 类别名字
node_name.text = cls_name
node_difficult = SubElement(node_object, 'difficult')
node_difficult.text = '0'
node_bndbox = SubElement(node_object, 'bndbox')
node_xmin = SubElement(node_bndbox, 'xmin')
node_xmin.text = str(Xmin)
node_ymin = SubElement(node_bndbox, 'ymin')
node_ymin.text = str(Ymin)
node_xmax = SubElement(node_bndbox, 'xmax')
node_xmax.text = str(Xmax)
node_ymax = SubElement(node_bndbox, 'ymax')
node_ymax.text = str(Ymax)
xml = tostring(node_root, pretty_print=True)
dom = parseString(xml)
# print xml 打印查看结果
xml_name = pic_name.replace(".jpg", "")
xml_name = os.path.join(save_xml_path, xml_name + '.xml')
with open(xml_name, 'wb') as f:
# f.write(dom.toprettyxml(indent='\t', encoding='utf-8'))
f.write(xml)
def make_voc_dir():
os.makedirs('VOC2007/Annotations')
os.makedirs('VOC2007/ImageSets')
os.makedirs('VOC2007/ImageSets/Main')
os.makedirs('VOC2007/ImageSets/Layout')
os.makedirs('VOC2007/ImageSets/Segmentation')
os.makedirs('VOC2007/JPEGImages')
os.makedirs('VOC2007/SegmentationClass')
os.makedirs('VOC2007/SegmentationObject')
if __name__ == '__main__':
dataset_folder = ["train", "validation", "test"]
class_folder = ["15instruments"]
root_path = "E:/study/OIDv4_ToolKit/OID/Dataset/"
make_voc_dir()
# 遍历train,val,test数据集
for folder1 in dataset_folder:
# ../OID/UnRestraint_Of_MarineCreature_In_OpenImagev4/Dataset/train/
dataset_root_path = root_path + folder1 + "/"
# 遍历具体类别
for folder2 in class_folder:
# ../OID/UnRestraint_Of_MarineCreature_In_OpenImagev4/Dataset/train/Seahorse/
label_root_path = dataset_root_path + folder2 + "/"
save_xml_path = 'VOC2007/Annotations'
# ../OID/UnRestraint_Of_MarineCreature_In_OpenImagev4/Dataset/train/Seahorse/Label/
files = os.listdir(label_root_path + "/Label/")
print(files)
# 遍历label和box的标签文件
for file in files:
# 对于每一个label.txt:
# param1:../OID/UnRestraint_Of_MarineCreature_In_OpenImagev4/Dataset/train/Seahorse/
# param2:imglabel.txt
# param3:annotation
make_xml(label_root_path, file, save_xml_path)
改完之后生成了相关文件夹和Annotation文件,图片也都复制到了JPEGImages
annotation和image的命名仍为当时的图片命名,但是看见很多文章都写命名有要求,最好是五位数 6位数,于是用下面的代码改了文件名称(注意是6位不是5位,不知道是哪里看到的5位数了!)
import os
import glob
# folder_path = r'E:\study\OIDv4_ToolKit\OID\Dataset\VOC2007\JPEGImages'
folder_path = 'E:\study\OIDv4_ToolKit\OID\Dataset\VOC2007\Annotation'
num = 1
if __name__ == '__main__':
for file in glob.glob(os.path.join(folder_path, "*.xml")):
s = '%05d' % num # 前面补零占位
os.rename(os.path.join(folder_path, file), os.path.join(folder_path, str(s) + '.xml'))
num += 1
但是annotation中的xml文件还有图片名信息,于是又用下面的代码修改了xml文件里filename内容
参考:Python修改xml文件内容_python 修改xml内容_pika虫的博客-CSDN博客
import xml.etree.ElementTree as ET
import os
# 批量修改整个文件夹所有的xml文件
def change_all_xml(xml_path):
filelist = os.listdir(xml_path)
print(filelist)
# 打开xml文档
for xmlfile in filelist:
xmlpath = os.path.join(xml_path, xmlfile)
doc = ET.parse(xmlpath)
root = doc.getroot()
sub1 = root.find('filename') # 找到filename标签,
sub1.text = xmlfile # 修改标签内容
doc.write(xmlpath) # 保存修改
# 修改某个特定的xml文件
def change_one_xml(xml_path): # 输入的是这个xml文件的全路径
# 打开xml文档
doc = ET.parse(xml_path)
root = doc.getroot()
sub1 = root.find('filename') # 找到filename标签,
sub1.text = '07_205.jpg' # 修改标签内容
doc.write(xml_path) # 保存修改
print('----------done--------')
change_all_xml(r'E:\study\OIDv4_ToolKit\OID\Dataset\VOC2007\Annotations') # xml文件总路径
# xml_path = r'Z:\pycharm_projects\ssd\VOC2007\Annotations\07_205.xml'
# change_one_xml(xml_path)
接下来创建和添加main文件夹下的文件
import os
import random
trainval_percent = 0.9
train_percent = 0.8
xmlfilepath = 'E:/study/OIDv4_ToolKit/OID/Dataset/VOC2007/Annotations'
txtsavepath = 'E:/study/OIDv4_ToolKit/OID/Dataset/VOC2007/ImageSets/Main'
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
ftrainval = open(txtsavepath+'/trainval.txt', 'w')
ftest = open(txtsavepath+'/test.txt', 'w')
ftrain = open(txtsavepath+'/train.txt', 'w')
fval = open(txtsavepath+'/val.txt', 'w')
for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
发现object需要为小写,但是Open Image的类别为首字母大写,需要修改,否则可能出现KeyError,使用以下代码修改(记得同步修改pacal_voc.py中的类别名为小写)
参考:Python修改xml文件中的类别名_Soochow_NJU_Smile的博客-CSDN博客
#修改xml文件中的目标的名字
import os, sys
import glob
from xml.etree import ElementTree as ET
# 批量读取Annotations下的xml文件
# per=ET.parse(r'C:\Users\xxxxx\Desktop\Annotations\000003.xml')
xml_dir = r'E:\study\OIDv4_ToolKit\OID\Dataset\VOC2007\Annotations'
xml_list = glob.glob(xml_dir + '/*.xml')
for xml in xml_list:
print(xml)
per = ET.parse(xml)
p = per.findall('/object')
for oneper in p: # 找出person节点
child = oneper.getchildren()[0] # 找出person节点的子节点
child.text = child.text.lower()
# if child.text == 'yanwu': #需要修改的名字
# child.text = 'smoke' #修改成什么名字
per.write(xml)
print(child.tag, ':', child.text)