⽬标检测(四)——xml快速上⼿(可完整实现)⽂章⽬录
什么是xml
XML 被设计⽤来传输和存储数据。
XML于HTML的标签语⾔类似,都是使⽤标签来进⾏约束!【相对于html标签,xml标签可以⾃定义】
(对于上⾯的定义,我们简单了解⼀下就可以了,并不会对我们解析它有影响!)
下⾯展⽰⼀份简单的xml⽂件!(AI识⾍的⼀份⽤于存储⽬标检测相关数据的xml⽂件)
<annotation>
<folder>刘霏霏</folder>
<filename>1.jpeg</filename>
<path>/home/fion/桌⾯/刘霏霏/1.jpeg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>1344</width>
<height>1344</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>Leconte</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>473</xmin>
<ymin>578</ymin>
<xmax>612</xmax>
<ymax>727</ymax>
</bndbox>
</object>
<object>
<name>Boerner</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>822</xmin>
<ymin>505</ymin>
<xmax>948</xmax>
<ymax>639</ymax>
</bndbox>
</object>
<object>
<name>linnaeus</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>607</xmin>
<ymin>781</ymin>
python处理xml文件<xmax>690</xmax>
<ymax>842</ymax>
</bndbox>
</object>
<object>
<name>armandi</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>756</xmin>
<ymin>786</ymin>
<xmax>841</xmax>
<ymax>856</ymax>
</bndbox>
</object>
<object>
<name>coleoptera</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>624</xmin>
<ymin>488</ymin>
<xmax>711</xmax>
<ymax>554</ymax>
</bndbox>
</object>
</annotation>
xml解析讲解
⼀、标签介绍
上边的⽰例xml⽂件中,其中最主要的是filename(⽂件–图⽚名称),size(图⽚⼤⼩和通道数), object(就是我们需要关注的⽬标检测的边界框位置等)。
【再次说明,xml中的标签可以⾃定义,只需要满⾜命名规范和标签闭合即可】
标签闭合: <filename> </filename>
⾃中,filename就是⾃定义的标签,前后对账,后者添加⼀个 /即可
命名规范:
名称可以包含字母、数字以及其他的字符
名称不能以数字或者标点符号开始
名称不能以字母 xml(或者 XML、Xml 等等)开始
名称不能包含空格
下⾯就对之前提到的三个部分做简要的说明!
filename标签
<filename>1.jpeg</filename>
这个标签在AI识⾍中对应着每⼀个xml⽂件对应的图⽚名称
size标签
<size>
<width>1344</width>
<height>1344</height>
<depth>3</depth>
</size>
可以看出,size中还包含其它的内部标签,此时我们需要解析内部的数据就需要继续继续往⾥边查标签——⽐如对width标签就对应着图⽚的宽度。【depth:常常表⽰通道数】
object标签
<object>
<name>Leconte</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>473</xmin>
<ymin>578</ymin>
<xmax>612</xmax>
<ymax>727</ymax>
</bndbox>
</object>
object通常就是我们⽬标检测中检测物体边界框所在的外层标签。
其中<difficult>0</difficult>,表⽰该物体是否检测存在困难;
更重要的是:
<bndbox>
<xmin>473</xmin>
<ymin>578</ymin>
<xmax>612</xmax>
<ymax>727</ymax>
</bndbox>
在bndbox标签中,包含我们检测的边界框的左上顶点(xmin, ymin) ,右下顶点(xmax, ymax)!
以上,基本上就对⼀份xml⽂件内容和确定解析标签做了⼀个基本的阐述。
⼆、xml解析的API
python中解析xml是通过操做xml库实现的!
1. ⾸先导⼊库: import xml
2. 具体解析xml⽂件是使⽤ElementTree来构建xml解析树实现的
3. 该ElementTree下的parse可以快速解析xml⽂件
形式如下:
4. 此时获取的解析树还只是⼀个直接的数据,不能直接获取我们想要的东西,此时需要对解析树使⽤find(标签)来对指定标签进⾏查讯,
并返回该标签对象,然后,再跟上⼀个text获取该标签的属性(注意:text针对仅含有具体字符数据的标签,如 <fname> 1</fname>,⽽不是<img_data><img_name>1</img_name></img_data>这样的复合标签)。
eg:
# xml解析树
xml_tree = ElementTree.parse(xml_path)
# find从解析对象中到id标签对象,然后使⽤text属性,获取xml中该标签对应的数据
id_data = xml_tree.find('id').text # 此时返回的数据是str,如果是数值,可以使⽤int/float进⾏转换
总结起来,xml解析最主要的操作就是:
使⽤ElementTree.parse(xml_path)创建⼀个初始的xml树对象;
然后对返回的树对象使⽤find进⾏标签查;
到所需要的⼦标签时,再使⽤text属性进⾏读取即可!
xml单个⽂件的解析
在解析⽂件前,我们来确定⼀下分析xml中所要关注的标签。
在AI识⾍的xml中,我们需要关注的标签:
filename : 解析出当前xml对应的图⽚⽂件名
size -> width , height, depth : 解析出图⽚的⼤⼩和通道数
objeck -> bndbox : 解析出objeck下的bbox数据
difficult : 解析当前bbox是否存在识别困难
接下来就从以上四个⽅⾯进⾏解析——
构建类别索引(category_id)
模型的输出与损失计算都是数值的,⽽xml中有时候会出现category(类别)名称,需要进⾏转换!
因此,我们就AI识⾍数据集中的xml所存在的类别进⾏整理:
# 其对应的cotegory有7种
INSECT_NAMES =['Boerner','Leconte','Linnaeus','acuminatus','armandi','coleoptera','linnaeus']
我们就对以上的类别设置顺序索引,从Boerner-0, Leconte-1, … , linnaeus-6进⾏映射!
这⾥利⽤字典来存取这样的映射,⽅便我们后边解析出xml中的类别时,可以使⽤key直接获取类别的id!构建字典的函数如下:
# 构造类别对应的索引字典
def get_insect_name():
"""获取类别索引字典
return a dict,
{'Boerner': 0,
'Leconte': 1,
'Linnaeus': 2,
'acuminatus': 3,
'armandi': 4,
'coleoptera': 5,
'linnaeus': 6
}
"""
insect_category2id ={}# 从类别到id的⼀个映射
for i, item in enumerate(INSECT_NAMES):# 利⽤enumerate⾃带的索引序号进⾏映射
insect_category2id[item]= i
return insect_category2id
构建xml解析
通过上边的操作,我们就得到了类别映射,后边的的类别名称就可以很⽅便的解析成id序列号了!
代码具体如下:(所有的重要注释都已经在代码中覆盖了!)
# 传⼊类别索引字典和xml数据地址进⾏xml解析
# 返回xml中解析到的所有⽬标数据
def get_annotation(category2id, data_path):
'''
category2id: 类别索引字典
data_path: 数据地址(单个xml的数据的相对地址——1.xml)
return records
(这是⼀个list,包含其中返回的字典数据)
'''
# data_path: 1.xml => [:-4] = '1' , [-4:] = '.xml'
fid = data_path[:-4]# id:既是xml的,通常也是img的
tree = ET.parse(data_path)
objs = tree.findall('object')# 返回所有的object对象(标签)
img_w = tree.find('size').find('width').text # 通过text属性调⽤xml指定标签的数据
img_h = tree.find('size').find('height').text
# print('Image Width:', img_w, 'px , Image Height:', img_h, 'px')
# 预置我们需要从xml中读取的数据格式——这⾥的bbox采⽤xywh的数据格式存储
# 根据objs的个数,分配此时解析xml需要多少个bbox
gt_bbox = np.zeros((len(objs),4), dtype=np.float32)# 4:表⽰bbox的数据: xywh
gt_class = np.zeros((len(objs),1), dtype=np.int32)# 1:对应类别索引
is_difficult = np.zeros((len(objs),1), dtype=np.int32)# 1:对应是否识别困难
# 预置后数据格式之后,开始遍历objs,依次读取其中的数据,存⼊到预置的数据格式中
for i, obj in enumerate(objs):# 利⽤enumerate返回⾃带索引,来⽅便多个obj的索引和遍历
# 类别读取
category_name = obj.find('name').text # 获取类别名
category_id = category2id[category_name]# 获取对应的类别索引
gt_class[i]= category_id # 添加识别类别索引
# 识别是否困难
_difficult =int(obj.find('difficult').text)# 获取识别难度
is_difficult[i]= _difficult # 读取⽬标识别是否困难
# bbox读取
x1 =float(obj.find('bndbox').find('xmin').text)# 将读取的左上点的x数据转换为float数据
y1 =float(obj.find('bndbox').find('ymin').text)
x2 =float(obj.find('bndbox').find('xmax').text)# 右下点
y2 =float(obj.find('bndbox').find('ymax').text)
# 将读取的bbox数据转换为:xywh的格式存⼊gt_bbox中
bbox_center_x =(x2 + x1)/2.0
bbox_center_y =(y2 + y1)/2.0
bbox_w =(x2 - x1)+1.0
bbox_h =(y2 - y1)+1.0
# 写⼊gt_bbox
gt_bbox[i]=[bbox_center_x, bbox_center_y, bbox_w, bbox_h]# 这⾥等价于将gt_bbox[i,:] = [....] # 将当前xml中的所有obj遍历完后,对数据进⾏⼀个汇总
# 具体实践时的数据解析和汇总可以做适当的调整
xml_rcd ={
'image_name': fid+'.png',# 图⽚名称
'image_id': fid,# 图⽚id
'difficult': is_difficult,# 数据识别困难情况
'categoty_id': gt_class,# 识别对象的类别情况
'gt_bbox': gt_bbox # 识别对象的bbox情况
}
# 返回解析完归总后的xml内容
return xml_rcd
代码测试
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论