DOTA和DIOR等其他数据集类似,转载+修改
# 修改者:TZF
# 文件名称 :roxml_to_dota.py
# 功能描述 :把rolabelimg标注的xml文件转换成dota能识别的xml文件,
# 再转换成dota格式的txt文件
# 把旋转框 cx,cy,w,h,angle,转换成四点坐标x1,y1,x2,y2,x3,y3,x4,y4
import os
import xml.etree.ElementTree as ET
import math
def edit_xml(xml_file, dotaxml_file):
"""
修改xml文件
:param xml_file:xml文件的路径
:return:
"""
tree = ET.parse(xml_file)
objs = tree.findall('object')
for ix, obj in enumerate(objs):
x0 = ET.Element("x0") # 创建节点
y0 = ET.Element("y0")
x1 = ET.Element("x1")
y1 = ET.Element("y1")
x2 = ET.Element("x2")
y2 = ET.Element("y2")
x3 = ET.Element("x3")
y3 = ET.Element("y3")
# obj_type = obj.find('bndbox')
# type = obj_type.text
# print(xml_file)
if (obj.find('robndbox') == None):
obj_bnd = obj.find('bndbox')
obj_xmin = obj_bnd.find('xmin')
obj_ymin = obj_bnd.find('ymin')
obj_xmax = obj_bnd.find('xmax')
obj_ymax = obj_bnd.find('ymax')
xmin = float(obj_xmin.text)
ymin = float(obj_ymin.text)
xmax = float(obj_xmax.text)
ymax = float(obj_ymax.text)
obj_bnd.remove(obj_xmin) # 删除节点
obj_bnd.remove(obj_ymin)
obj_bnd.remove(obj_xmax)
obj_bnd.remove(obj_ymax)
x0.text = str(xmin)
y0.text = str(ymax)
x1.text = str(xmax)
y1.text = str(ymax)
x2.text = str(xmax)
y2.text = str(ymin)
x3.text = str(xmin)
y3.text = str(ymin)
else:
obj_bnd = obj.find('robndbox')
obj_bnd.tag = 'bndbox' # 修改节点名
obj_cx = obj_bnd.find('cx')
obj_cy = obj_bnd.find('cy')
obj_w = obj_bnd.find('w')
obj_h = obj_bnd.find('h')
obj_angle = obj_bnd.find('angle')
cx = float(obj_cx.text)
cy = float(obj_cy.text)
w = float(obj_w.text)
h = float(obj_h.text)
angle = float(obj_angle.text)
obj_bnd.remove(obj_cx) # 删除节点
obj_bnd.remove(obj_cy)
obj_bnd.remove(obj_w)
obj_bnd.remove(obj_h)
obj_bnd.remove(obj_angle)
x0.text, y0.text = rotatePoint(cx, cy, cx - w / 2, cy - h / 2, -angle)
x1.text, y1.text = rotatePoint(cx, cy, cx + w / 2, cy - h / 2, -angle)
x2.text, y2.text = rotatePoint(cx, cy, cx + w / 2, cy + h / 2, -angle)
x3.text, y3.text = rotatePoint(cx, cy, cx - w / 2, cy + h / 2, -angle)
# obj.remove(obj_type) # 删除节点
obj_bnd.append(x0) # 新增节点
obj_bnd.append(y0)
obj_bnd.append(x1)
obj_bnd.append(y1)
obj_bnd.append(x2)
obj_bnd.append(y2)
obj_bnd.append(x3)
obj_bnd.append(y3)
tree.write(dotaxml_file, method='xml', encoding='utf-8') # 更新xml文件
# 转换成四点坐标
def rotatePoint(xc, yc, xp, yp, theta):
xoff = xp - xc
yoff = yp - yc
cosTheta = math.cos(theta)
sinTheta = math.sin(theta)
pResx = cosTheta * xoff + sinTheta * yoff
pResy = - sinTheta * xoff + cosTheta * yoff
return str(int(xc + pResx)), str(int(yc + pResy))
def totxt(xml_path, out_path):
# 想要生成的txt文件保存的路径,这里可以自己修改
files = os.listdir(xml_path)
num = len(files)
for file in files:
# trainval = random.sample(list, tv)
# train = random.sample(trainval, tr)
if '.ipynb_checkpoints' in file:
num = num - 1
print('error')
continue
tree = ET.parse(xml_path + os.sep + file)
# root = tree.getroot()
name = file.strip('.xml')
output = out_path + name + '.txt'
file = open(output, 'w')
objs = tree.findall('object')
for obj in objs:
cls = obj.find('name').text
box = obj.find('rotated_bndbox')
x0 = int(float(box.find('x1').text))
y0 = int(float(box.find('y1').text))
x1 = int(float(box.find('x2').text))
y1 = int(float(box.find('y2').text))
x2 = int(float(box.find('x3').text))
y2 = int(float(box.find('y3').text))
x3 = int(float(box.find('x4').text))
y3 = int(float(box.find('y4').text))
file.write("{} {} {} {} {} {} {} {} {} 0\n".format(x0, y0, x1, y1, x2, y2, x3, y3, cls))
file.close()
print(num)
if __name__ == '__main__':
# -----**** 第一步:把xml文件统一转换成旋转框的xml文件 ****-----
# roxml_path = "./crack/roxml" # 目录下保存的是需要转换的xml文件
dotaxml_path = '../VOC/Annotations/'
out_path = '../VOC/labels/'
if not os.path.exists(out_path):
os.mkdir(out_path)
# filelist = os.listdir(roxml_path)
# for file in filelist:
# edit_xml(os.path.join(roxml_path, file), os.path.join(dotaxml_path, file))
# -----**** 第二步:把旋转框xml文件转换成txt格式 ****-----
totxt(dotaxml_path, out_path)
附:DOTA数据的voc格式转JSON格式
import json
import os
import os.path as osp
import random
from PIL import Image
from dota_poly2rbox import poly2rbox_single_v2, poly2rbox_single
def parse_ann_info(img_base_path, label_base_path, img_name):
lab_path = osp.join(label_base_path, img_name+'.txt')
bboxes, labels, bboxes_ignore, labels_ignore = [], [], [], []
with open(lab_path, 'r') as f:
for ann_line in f.readlines():
ann_line = ann_line.strip().split(' ')
bbox = [float(ann_line[i]) for i in range(8)]
# 8 point to 5 point xywha
bbox = poly2rbox_single_v2(bbox)
class_name = ann_line[8]
difficult = int(ann_line[9])
# ignore difficult =2
if difficult == 0:
bboxes.append(bbox)
labels.append(class_name)
elif difficult == 1:
bboxes_ignore.append(bbox)
labels_ignore.append(class_name)
return bboxes, labels, bboxes_ignore, labels_ignore
def generate_txt_labels(src_path, out_path, trainval=True):
"""Generate .txt labels recording img_names
Args:
src_path: dataset path containing images and labelTxt folders.
out_path: output txt file path
trainval: trainval or test?
"""
img_path = os.path.join(src_path, 'images')
label_path = os.path.join(src_path, 'labelTxt')
img_lists = os.listdir(img_path)
with open(out_path, 'w') as f:
for img in img_lists:
img_name = osp.splitext(img)[0]
label = os.path.join(label_path, img_name+'.txt')
if(trainval == True):
if(os.path.exists(label) == False):
print('Label:'+img_name+'.txt'+' Not Exist')
else:
f.write(img_name+'\n')
else:
f.write(img_name+'\n')
def generate_json_labels(src_path, out_path, trainval=True):
"""Generate .json labels which is similar to coco format
Args:
src_path: dataset path containing images and labelTxt folders.
out_path: output json file path
trainval: trainval or test?
"""
img_path = os.path.join(src_path, 'images')
label_path = os.path.join(src_path, 'labelTxt')
img_lists = os.listdir(img_path)
data_dict = []
with open(out_path, 'w') as f:
for id, img in enumerate(img_lists):
img_info = {}
img_name = osp.splitext(img)[0]
label = os.path.join(label_path, img_name+'.txt')
img = Image.open(osp.join(img_path, img))
img_info['filename'] = img_name+'.jpg'
img_info['height'] = img.height
img_info['width'] = img.width
img_info['id'] = id
if(trainval == True):
if(os.path.exists(label) == False):
print('Label:'+img_name+'.txt'+' Not Exist')
else:
bboxes, labels, bboxes_ignore, labels_ignore = parse_ann_info(
img_path, label_path, img_name)
ann = {}
ann['bboxes'] = bboxes
ann['labels'] = labels
ann['bboxes_ignore'] = bboxes_ignore
ann['labels_ignore'] = labels_ignore
img_info['annotations'] = ann
data_dict.append(img_info)
json.dump(data_dict, f)
if __name__ == '__main__':
generate_json_labels('../DOTA_voc/train/',
'../DOTA_voc/train/train.json')
generate_json_labels('../DOTA_voc/test/',
'../DOTA_voc/test/test.json', trainval=False)
print('done!')