Python批量修改labelImg生成的XML文件:过滤类别与统一小写
本文介绍如何使用Python脚本批量处理labelImg生成的XML标注文件。通过解析ElementTree,程序可自动过滤掉不需要的目标类别(如仅保留people和cat),并强制将所有保留标签的名称统一转换为小写,以适配py-faster-rcnn等对标签大小写敏感的训练框架。该方法避免了重新打标的繁琐工作,高效完成数据预处理。
概述与背景
概述
在使用labelImg完成图像标注后,有时我们需要仅利用部分类别进行模型训练,而不希望重新进行全量标注并生成新的XML文件。为此,可以直接通过脚本处理现有的XML文件,删除其中不需要的标签类别及其相关属性。
在标注过程中,由于人工操作误差,标签名称可能出现大小写不一致的情况。若后续训练框架(如py-faster-rcnn)要求标签必须全部为小写,则需要在程序中增加将标签值统一转换为小写的逻辑。
以下以一个示例XML文件为基础,其中故意混入了大写标签以演示处理效果。
<annotation verified="yes">
<filename>test.jpg</filename>
<path>C:\Users\yasin\Desktop\test</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>400</width>
<height>300</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>People</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>80</xmin>
<ymin>69</ymin>
<xmax>144</xmax>
<ymax>89</ymax>
</bndbox>
</object>
<object>
<name>CAT</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>40</xmin>
<ymin>69</ymin>
<xmax>143</xmax>
<ymax>16</ymax>
</bndbox>
</object>
<object>
<name>dog</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>96</xmin>
<ymin>82</ymin>
<xmax>176</xmax>
<ymax>87</ymax>
</bndbox>
</object>
</annotation>
Python实现方案
具体实现
假设我们仅希望保留图像中的“people”和“cat”类别,并删除其他所有类别,可参考以下Python代码实现。
from xml.etree.ElementTree import ElementTree
from os import walk, path
def read_xml(in_path):
tree = ElementTree()
tree.parse(in_path)
return tree
def write_xml(tree, out_path):
tree.write(out_path, encoding="utf-8", xml_declaration=True)
def find_nodes(tree, path):
return tree.findall(path)
def del_node_by_target_classes(nodelist, target_classes_lower, tree_root):
for parent_node in nodelist:
children = parent_node.getchildren()
if (parent_node.tag == "object" and children[0].text.lower() not in target_classes_lower):
tree_root.remove(parent_node)
elif (parent_node.tag == "object" and children[0].text.lower() in target_classes_lower):
children[0].text = children[0].text.lower()
def get_fileNames(rootdir):
data_path = []
prefixs = []
for root, dirs, files in walk(rootdir, topdown=True):
for name in files:
pre, ending = path.splitext(name)
if ending != ".xml":
continue
else:
data_path.append(path.join(root, name))
prefixs.append(pre)
return data_path, prefixs
if __name__ == "__main__":
# get all the xml paths, prefixes if not used here
paths_xml, prefixs = get_fileNames("/home/yasin/old_labels/")
target_classes = ["PEOPLE", "CAT"] # target flags you want to keep
target_classes_lower = []
for i in range(len(target_classes)):
target_classes_lower.append(target_classes[i].lower()) # make sure your target is lowe-case
# print(target_classes_lower)
for i in range(len(paths_xml)):
# rename and save the corresponding xml
tree = read_xml(paths_xml[i])
# get tree node
tree_root = tree.getroot()
# get parent nodes
del_parent_nodes = find_nodes(tree, "./")
# get target classes and delete
target_del_node = del_node_by_target_classes(del_parent_nodes, target_classes_lower, tree_root)
# save output xml, 000001.xml
write_xml(tree, "/home/yasin/new_labels/{}.xml".format("%06d" % i))
执行上述代码后,原始XML文件将转换为新的XML格式。结果显示,除“people”和“cat”外的类别(如“dog”)已被移除,且保留类别的标签名称已统一转换为小写。
<?xml version='1.0' encoding='utf-8'?>
<annotation verified="yes">
<filename>test.jpg</filename>
<path>C:\Users\yasin\Desktop\test</path>
<source>
<database>Unknown</database>
</source>
<size>
<width>400</width>
<height>300</height>
<depth>3</depth>
</size>
<segmented>0</segmented>
<object>
<name>people</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>80</xmin>
<ymin>69</ymin>
<xmax>144</xmax>
<ymax>89</ymax>
</bndbox>
</object>
<object>
<name>cat</name>
<pose>Unspecified</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>40</xmin>
<ymin>69</ymin>
<xmax>143</xmax>
<ymax>16</ymax>
</bndbox>
</object>
</annotation>
以上即为本文的全部内容,希望对读者在数据预处理方面的学习有所帮助,也欢迎大家继续支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













