voc旋转标注数据转dota类型

voc旋转标注数据转dota类型

在cv领域数据集的标注过程中,用labelImg工具我们可以对数据进行标注,标注生成的voc类型的xml框一般是如下格式:(bndbox)(x1,y1,x2,y2)

普通的xml标注

但是在标注的过程中,往往会遇到使用水平的矩形框无法完整地框出类别的特征的情况,这会导致无用特征的冗余。
在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的情况

voc2dota.py

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))

生成目录下所有文件名的txt文件代码:

ListFilesToTxt.py

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()

***———————————***2022.6.30更新
之前的voc转dota代码我自己用了之后觉得不好用,对于旋转角度比较小的情况,旋转框往往转换不出来。
在借鉴别人脚本的基础上,进行了改进,分享给大家。只需要下面一个脚本便可实现xml格式转dota格式:

roxml_to_dota.py

# 文件名称   :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):
    """
    修改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(xml_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 = '/home/xiaopeng/dataset/test_oriented/3/split/for-try (copy)'
    out_path = '/home/xiaopeng/dataset/test_oriented/3/split/try_txt/'
    # 想要生成的txt文件保存的路径,这里可以自己修改

    files = os.listdir(xml_path)
    for file in files:

        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('bndbox')
            x0 = int(float(box.find('x0').text))
            y0 = int(float(box.find('y0').text))
            x1 = int(float(box.find('x1').text))
            y1 = int(float(box.find('y1').text))
            x2 = int(float(box.find('x2').text))
            y2 = int(float(box.find('y2').text))
            x3 = int(float(box.find('x3').text))
            y3 = int(float(box.find('y3').text))
            file.write("{} {} {} {} {} {} {} {} {} 0\n".format(x0, y0, x1, y1, x2, y2, x3, y3, cls))
        file.close()
        print(output)


if __name__ == '__main__':
    # -----**** 第一步:把xml文件统一转换成旋转框的xml文件 ****-----
    dir = "/home/xiaopeng/dataset/test_oriented/3/split/for-try (copy)"  # 目录下保存的是需要转换的xml文件
    filelist = os.listdir(dir)
    for file in filelist:
        edit_xml(os.path.join(dir, file))

    # -----**** 第二步:把旋转框xml文件转换成txt格式 ****-----
    totxt()

  • 12
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
VOC数据增强中的旋转是指对图像进行旋转操作,通过旋转图像来生成新的训练样本,从而扩充训练集的规模和多样性。旋转操作可以通过调整图像的角度来改变物体的方向和姿态,使得模型能够更好地学习到物体在不同角度下的特征。 在进行VOC数据增强中的旋转操作时,需要注意以下几点: 1. 选择旋转角度:可以根据需要选择不同的旋转角度,通常可以选择从0到360度之间的任意角度,或者按照一定的角度间隔进行旋转。 2. 旋转中心:旋转操作通常会设置一个旋转中心点,可以选择物体的中心点作为旋转中心,也可以选择图像的中心点作为旋转中心。 3. 旋转方式:旋转操作可以通过旋转矩阵进行实现,即对图像进行仿射变换。旋转矩阵是根据旋转角度和旋转中心点计算得出的,通过对图像的每个像素点进行旋转操作。 4. 边界处理:在旋转图像时,有可能会导致旋转后的图像超出原图像的边界。需要进行边界处理,可以通过裁剪或者填充的方式将旋转后的图像调整为与原图像相同的大小。 通过旋转操作进行VOC数据增强可以提升训练模型的鲁棒性和泛化能力,使其能够更好地适应不同的图像角度和姿态。同时,旋转操作也可以增加训练集的多样性,提高模型的识别准确率。但是,在进行旋转操作时,需要注意避免过度旋转和边界处理问题,以防止训练模型的性能下降。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值