最近在使用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的格式。
网友评论