在目标检测中,我们通常需要分析数据的属性,例如各类目标的个数、大小分布等。本文以统计斜框检测中的角度分布、目标数量为例。详细讲解怎么读取xml文件并绘制直方图。
首先是导入各种库,添加几个变量,如文件夹、存储角度和目标数量的列表:
import os
import xml.etree.ElementTree as ET
import math
import matplotlib.pyplot as plt
import numpy as np
xmls_dir = 'E:/Projects/xmls_origin'
angle_list = [] # 存储角度的列表
object_list = [] # 存储目标(正框、斜框)的列表
接下来是读取xml的函数:
# 读取xml文件,存储其中的angle值
def read_xml(xml_file):
"""
读取xml文件,找到角度并存储进列表
:param xml_file:xml文件的路径
:return:
"""
tree = ET.parse(xml_file)
objs = tree.findall('object')
for ix, obj in enumerate(objs):
robndbox = obj.find('robndbox')
if robndbox is not None:
object_list.append('robndbox') # 斜框的数量+1
angle = float(robndbox.find('angle').text)
angle = angle * 180 / math.pi # 弧度转化为角度
angle_list.append(angle) # 添加进列表
bndbox =obj.find('bndbox')
if bndbox is not None:
object_list.append('bndbox') # 正框的数量+1
同时,我们来看一下我的xml文件长啥样,可以看到,里面包含两种目标,一种是robndbox,一种是bndbox,其中robndbox里头包含有angle值。我要做的就是统计每种目标的个数和angle的分布。如果要统计别的也是类似的。
<annotation verified="no">
<folder>ship_data</folder>
<filename>1</filename>
<path>D:\data\1.jpg</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>600</width>
<height>600</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<type>robndbox</type>
<name>ship</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<robndbox>
<cx>148.3626</cx>
<cy>534.0</cy>
<w>41.0</w>
<h>12.0</h>
<angle>2.301593</angle>
</robndbox>
</object>
<object>
<type>bndbox</type>
<name>ship</name>
<pose>Unspecified</pose>
<truncated>1</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>259</xmin>
<ymin>568</ymin>
<xmax>298</xmax>
<ymax>600</ymax>
</bndbox>
</object>
</annotation>
然后是绘制直方图的函数,在直方图的绘制中,添加了x/y轴的标签及刻度。
def plt_hist(angle_list,robndbox_num,bndbox_num):
# 绘制直方图
plt.hist(x=angle_list, # 指定绘图数据
bins = 30, # 指定直方图中条块的个数
color = 'steelblue', # 指定直方图的填充色
edgecolor = 'black') # 指定直方图的边框色
# 添加x轴和y轴标签
plt.xlabel('Angle ( ro: %d,bnd: %d )'%(robndbox_num,bndbox_num))
plt.ylabel('Object_num ( all:%d )'%(robndbox_num + bndbox_num))
# 添加标题
plt.title('xml-angle')
# 设置横坐标刻度和起始
my_xticks = np.arange(-90,95,15)
plt.xticks(my_xticks)
# 显示图形
plt.show()
最后是主函数:
if __name__ == '__main__':
xmls = os.listdir(xmls_dir)
for xml in xmls:
read_xml(os.path.join(xmls_dir,xml)) # 读取一遍xml文件,存储角度值
# 正框和斜框的数量
robndbox_num = object_list.count('robndbox')
bndbox_num = object_list.count('bndbox')
plt_hist(angle_list,robndbox_num,bndbox_num)
下面就是绘制出来的直方图,显示了角度的分布,横轴是角度值,纵轴是各个角度对应的目标个数。并且在横轴和纵轴上显示了目标的总个数和正框、斜框目标分别的数量。
完整脚本如下,运行环境为 python3.6+:
# *_* coding : UTF-8 *_*
# 开发人员 :csu·pan-_-||
# 开发时间 :2020/11/4 9:16
# 文件名称 :hist_angle.py
# 开发工具 :PyCharm
# 功能描述 :计算roLableImg标注的xml文件中angle的直方图
import os
import xml.etree.ElementTree as ET
import math
import matplotlib.pyplot as plt
import numpy as np
xmls_dir = 'E:/Projects/xmls_origin'
angle_list = [] # 存储角度的列表
object_list = [] # 存储目标(正框、斜框)的列表
# 读取xml文件,存储其中的angle值
def read_xml(xml_file):
"""
读取xml文件,找到角度并存储进列表
:param xml_file:xml文件的路径
:return:
"""
tree = ET.parse(xml_file)
objs = tree.findall('object')
for ix, obj in enumerate(objs):
robndbox = obj.find('robndbox')
if robndbox is not None:
object_list.append('robndbox') # 斜框的数量+1
angle = float(robndbox.find('angle').text)
angle = angle * 180 / math.pi # 弧度转化为角度
angle_list.append(angle) # 添加进列表
bndbox =obj.find('bndbox')
if bndbox is not None:
object_list.append('bndbox') # 正框的数量+1
def plt_hist(angle_list,robndbox_num,bndbox_num):
# 绘制直方图
plt.hist(x=angle_list, # 指定绘图数据
bins = 30, # 指定直方图中条块的个数
color = 'steelblue', # 指定直方图的填充色
edgecolor = 'black') # 指定直方图的边框色
# 添加x轴和y轴标签
plt.xlabel('Angle ( ro: %d,bnd: %d )'%(robndbox_num,bndbox_num))
plt.ylabel('Object_num ( all:%d )'%(robndbox_num + bndbox_num))
# 添加标题
plt.title('xml-angle')
# 设置横坐标刻度和起始
my_xticks = np.arange(-90,95,15)
plt.xticks(my_xticks)
# 显示图形
plt.show()
if __name__ == '__main__':
xmls = os.listdir(xmls_dir)
for xml in xmls:
read_xml(os.path.join(xmls_dir,xml)) # 读取一遍xml文件,存储角度值
# 正框和斜框的数量
robndbox_num = object_list.count('robndbox')
bndbox_num = object_list.count('bndbox')
plt_hist(angle_list,robndbox_num,bndbox_num)
总结:之前觉得操作xml文件是一件比较痛苦的事,做了几次之后,反倒xml文件的组织形式很清晰(难道这就是所谓的"悟"?)。这个脚本没花多少时间就写出来了,希望给后面有需要的朋友一点帮助。毕竟互联网精神是开放、平等、协作、快速、分享(大家都是面向百度、谷歌、优快云、Github编程的,O(∩_∩)O哈哈~)