如何将rolabelImg工具制作的.xml文件转成MSRA-TD500格式

最近在使用RRPN模型来训练自己的数据集,旋转bounding box的标注工具使用的是roLabelImg。下面记录如何将roLabelImg标注得到的.xml文件转成MSRA-TD500格式。

MSRA-TD500的数据集标注格式特点

MSRA-TD500标注格式

每一张图片的label信息保存在一个文本文件中,每一行记录一个旋转bndbox信息,x,y值记录的是水平包围框的左上角顶点坐标;旋转角度值特点:未旋转时角度为0,顺时针旋转角度为正,逆时针旋转时角度为负,角度单位为弧度。

roLabelImg标注的.xml文件特点

比如针对下面这张图片(特点是文字方向近似水平):

近似水平的字符

使用roLabelImg标注得到的图中左上角的“CAIU"的label信息如下:

<robndbox>
      <cx>100.0</cx>
      <cy>79.5</cy>
      <w>134.0</w>
      <h>50.0</h>
      <angle>3.081593</angle>
    </robndbox>

其中:

  • cx, cy表示bndbox的中心点坐标(坐标系方向和一般的图像坐标系相同,左上角为原点,向右为x正方向,向下为y正方向);
  • h和w是字符块的高和宽;
  • angle是旋转角度信息,这里得注意,roLabelImg标注得到的旋转角度值规则:首先是画一个水平bndbox,此时的angle=0,如果你往顺时针方向旋转,得到的角度值是一个弧度单位的正值。按照这种思路,如果往逆时针方向旋转,得到的角度值应该是一个弧度单位的负值,但实际并不是这样,比如上面的例子,实际角度应该是往逆时针方向旋转了一小点,但得到的角度值=3.081593,也是正值?假设规定往逆时针方向旋转时角度为负,那么它的角度theta应该是:theta=angle-pi,只要注意这一点就没问题了。

下面是我用来将上面这种近似水平的文本转换成MSRA-TD500格式的python代码:

import os
import numpy as np
import xml.etree.ElementTree as ET

if __name__ == '__main__':
    #source_dir是存放.xml文件的文件夹
    source_dir = '/home/ys/pycaffe/RRPN_dataset/Annotations/type2/'
    #dst_dir是存放生成的文本文件的文件夹
    dst_dir = '/home/ys/pycaffe/RRPN_dataset/gt/'
    k = 0
    for file in os.listdir(source_dir):
        tree = ET.parse(source_dir + file)
        objs = tree.findall('object')
        num_objs = len(objs)
        boxes_list=[]
        for ix, obj in enumerate(objs):
            bbox = obj.find('robndbox')
            # Make pixel indexes 0-based
            # .xml中的cx,cy,w,h都是小数,应该强制类型转换成int,angle就是float,不用转
            cx = int(float(bbox.find('cx').text)) - 1
            cy = int(float(bbox.find('cy').text)) - 1
            w = int(float(bbox.find('w').text))
            h = int(float(bbox.find('h').text))
            angle = float(bbox.find('angle').text)
            #将.xml格式的中心点坐标转换成MSRA格式的左上角顶点坐标
            x = cx - int(w/2)
            y = cy - int(h/2)
            #使用round()将小数点位数保留6位
            if angle<1.57:
                theta = round(angle, 6)
            else:
                theta = round(angle - np.pi, 6)
            lines = str(ix) + ' 0 ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' +str(h) + ' ' +str(theta) + '\n'
            boxes_list.append(lines)
        tmp = file.split(sep='.')
        #gt_name是要保存的文本格式的文件名
        gt_name = dst_dir + tmp[0] + '.gt'
        gt_file = open(gt_name, 'w')
        gt_file.writelines(boxes_list)
        gt_file.close()
        k+=1
    print('there are %d images in total' % int(k))
    print('done')

另外一种情形

当我们标注下面这种类型的图片时:

近似竖直方向的字符

此时,我们的标注方式可能就有所不同了:我们通常不是像上面的例子那样,画一个扁平的水平矩形,然后逆时针旋转一个超过90度的大角度,此时,往往我们的做法是画一个竖直方向细长的矩形,然后调整一个小角度。

这种情形和上面第一种情形相同吗?显然是不同的。在RRPN中,字符的宽w规定为长边,高h规定为短边,但是刚才说的这种标注方式得到的.xml信息是这样的:

<robndbox>
      <cx>43.5546</cx>
      <cy>157.4847</cy>
      <w>31.0</w>
      <h>292.0</h>
      <angle>3.061593</angle>
    </robndbox>

就是w变成的短边而h变成了长边。为了统一,针对这种类型的标注文件进行转换时需要重新修改一下转换python代码,下面是我使用的代码:

import os
import numpy as np
import xml.etree.ElementTree as ET

if __name__ == '__main__':
    #source_dir是存放.xml文件的文件夹
    source_dir = '/home/ys/pycaffe/RRPN_dataset/Annotations/type1/'
    #dst_dir是存放生成的文本文件的文件夹
    dst_dir = '/home/ys/pycaffe/RRPN_dataset/gt/'
    k = 0
    for file in os.listdir(source_dir):
        tree = ET.parse(source_dir + file)
        objs = tree.findall('object')
        num_objs = len(objs)
        boxes_list=[]
        for ix, obj in enumerate(objs):
            bbox = obj.find('robndbox')
            # Make pixel indexes 0-based
            # .xml中的cx,cy,w,h都是小数,应该强制类型转换成int,angle就是float,不用转
            cx = int(float(bbox.find('cx').text)) - 1
            cy = int(float(bbox.find('cy').text)) - 1
            w = int(float(bbox.find('w').text))
            h = int(float(bbox.find('h').text))
            angle = float(bbox.find('angle').text)
            # 这里需要注意,若要和MSRA-TD500中的标签格式统一,转换成左上角顶点坐标时,
            # cx应该减去长边h/2,cy应该减去短边w/2
            x = cx - int(h/2)
            y = cy - int(w/2)
            # 为了统一,w是长边,则w和h的值应该交换一下
            tmp = w
            w = h
            h = tmp
            # 同样,angle应该也要在第一种情况的基础上减去一个pi/2
            if angle<1.57:
                theta = round(angle - np.pi*0.5, 6)
            else:
                theta = round(angle - np.pi*1.5, 6)
            lines = str(ix) + ' 0 ' + str(x) + ' ' + str(y) + ' ' + str(w) + ' ' +str(h) + ' ' +str(theta) + '\n'
            boxes_list.append(lines)
        tmp = file.split(sep='.')
        gt_name = dst_dir + tmp[0] + '.gt'
        gt_file = open(gt_name, 'w')
        gt_file.writelines(boxes_list)
        gt_file.close()
        k+=1
    print('there are %d images in total' % int(k))
    print('done')

针对近似竖排字符,这样以上的处理才符合MSRA-TD500的格式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容