在cv领域数据集的标注过程中,用labelImg工具我们可以对数据进行标注,标注生成的voc类型的xml框一般是如下格式:(bndbox)(x1,y1,x2,y2)
但是在标注的过程中,往往会遇到使用水平的矩形框无法完整地框出类别的特征的情况,这会导致无用特征的冗余。
在dota类型的标注中,就能解决这个问题:
我们可以定义三个类型的bbox:HBB,OBB,POLY
HBB由左上点和右下点表示。 HBB 的最后一个维度应该是 4。
OBB由中心点(x, y)、宽度(w)、高度(h) 和θ 表示。宽度是较长边的长度。高度是较短边的长度。Theta 是长边和 x 轴之间的角度。 OBB 的最后一个维度应该是 5。
POLY由四点坐标表示。这些点的顺序无关紧要,但相邻的点应该是POLY的一侧。 POLY的最后一个维度应该是8
很明显,普通的xml标注文件属于HBB类型。而在旋转目标检测中,我们如果用到labelImg2来标注旋转的矩形框:
以及他生成的xml标注文件:
很明显,格式产生了变化
(robndbox)(cx,cy,w,h)
为了统一使用,我们可以将两种格式的框统一转成dota格式的txt文件(也就是poly格式)
他的格式一般是这样:
————数字分别对应从左上角开始顺时针旋转的四个点坐标
接下来直接附上voc转dota的代码,适用于xml混合bndbox和robndbox的情况:
import math
import shutil
import os
import numpy as np
import xml.etree.ElementTree as ET
dataset_dir = r'/home/xiaopeng/dataset/test_oriented/1/img'
ana_dir = r'/home/xiaopeng/dataset/test_oriented/1/xml'
save_dir = r'/home/xiaopeng/dataset/test_oriented/1/dota_txt'
train_img_dir = r'/home/xiaopeng/dataset/test_oriented/1/imgSets/test.txt' #txt文件保存的是所有文件的名称
f1 = open(train_img_dir, 'r')
train_img = f1.readlines()
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 rota(x, y, w, h, a): # 旋转中心点,旋转中心点,框的w,h,旋转角
x0, y0 = rotatePoint(x, y, x - w / 2, y - h / 2, -a)
x1, y1 = rotatePoint(x, y, x + w / 2, y - h / 2, -a)
x2, y2 = rotatePoint(x, y, x + w / 2, y + h / 2, -a)
x3, y3 = rotatePoint(x, y, x - w / 2, y + h / 2, -a)
return x0, y0, x1, y1, x2, y2, x3, y3 # 旋转后的四个点,左上,右上,右下,左下
for img in train_img:
shutil.copy(os.path.join(dataset_dir, img[:-1] + '.jpg'),
os.path.join(save_dir, 'images', img[:-1] + '.jpg'))
xml_file = open(os.path.join(ana_dir, img[:-1] + '.xml'), encoding='utf-8')
tree = ET.parse(xml_file)
root = tree.getroot()
with open(os.path.join(save_dir, 'labelTxt', img[:-1] + '.txt'), 'w') as f:
for obj in root.iter('object'):
cls = obj.find('name').text
box = obj.find('robndbox')
# print(box)
if(box!=None): #xml for labelImg2
x_c = float(box.find('cx').text)
y_c = float(box.find('cy').text)
h = float(box.find('h').text)
w = float(box.find('w').text)
theta = float(box.find('angle').text)
# bdx = rota(x_c, y_c, w, h, theta)
# print(bdx)
x1=bdx[0]
y1=bdx[1]
x2=bdx[2]
y2=bdx[3]
x3=bdx[4]
y3=bdx[5]
x4=bdx[6]
y4=bdx[7]
f.write("{} {} {} {} {} {} {} {} {} 0\n".format(str(x1), str(y1), str(x2), str(y2), str(x3), str(y3), str(x4), str(y4), cls))
else:
#xml for labelImg
box1=obj.find('bndbox')
xmin = int(box1[0].text)
ymin = int(box1[1].text)
xmax = int(box1[2].text)
ymax = int(box1[3].text)
f.write("{} {} {} {} {} {} {} {} {} 0\n".format(xmin,ymax,xmax,ymax,xmax,ymin,xmin,ymin,cls))
if __name__ == '__main__':
rota()
生成目录下所有文件名的txt文件代码:
def ListFilesToTxt(dir, file, wildcard, recursion):
exts = wildcard.split(" ")
for root, subdirs, files in os.walk(dir):
for name in files:
# name1=name.split(".")[0]
for ext in exts:
if (name.endswith(ext)):
file.write(name.split('.')[0] + "\n")
break
if (not recursion):
break
def Test():
dir = "/home/xiaopeng/dataset/test_oriented/1/split_class/Rough yarn/img"
outfile = "/home/xiaopeng/dataset/test_oriented/1/split_class/Rough yarn/test.txt"
wildcard = ".txt .exe .dll .lib .jpg"
file = open(outfile, "w")
if not file:
print("cannot open the file %s for writing" % outfile)
ListFilesToTxt(dir, file, wildcard, 0)
file.close()
if __name__ == '__main__':
Test()