批量删除LabelImg制作的.xml中的部分的节点值(标签值)

本文介绍了一种在XML文件中筛选特定类别标签并统一转换为小写的方法,适用于使用LabelImg进行图像标注后,需要调整训练集类别的情况。通过Python脚本实现了对目标类别的保留与非目标类别的删除,同时确保所有保留的标签名称统一为小写格式。

概述

       自己在用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>

具体实现

       假如我们只想保留图片上的people和cat类,其他都删除,代码如下:

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_path_prex(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_path_prex("/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>
### 如何在 LabelImg 中查找以前标注的所有标签 当使用 LabelImg 进行图像标注时,所有的标注信息通常会保存在一个特定的目录下,并以 `.xml` 或者 `.txt` 文件的形式存在。如果希望找到之前标注过的所有标签名称以及它们对应的信息,可以通过以下方法实现。 #### 方法一:通过 `classes.txt` 文件获取已定义的标签列表 LabelImg 工具会在每次创建新的标签类别时将其记录到当前工作目录中的 `classes.txt` 文件中[^1]。此文件包含了所有曾经被使用的标签名称。 - 如果该文件丢失或者被误删,则需要重新构建它。 - 可以手动检查 `classes.txt` 文件的内容来了解有哪些标签曾被使用过。 #### 方法二:解析 XML 文件提取标签信息 对于 VOC 格式的标注文件(`.xml`),每一份文件都描述了一张图片上的对象及其属性。可以编写脚本来批量读取这些文件并汇总其中的对象标签: ```python import os import xml.etree.ElementTree as ET def extract_labels_from_xml(xml_dir): label_set = set() for file_name in os.listdir(xml_dir): if not file_name.endswith('.xml'): continue try: tree = ET.parse(os.path.join(xml_dir, file_name)) root = tree.getroot() for obj in root.iter('object'): label = obj.find('name').text.strip() label_set.add(label) except Exception as e: print(f"Error parsing {file_name}: {e}") return sorted(list(label_set)) # 替换为实际存放XML文件的路径 xml_directory = 'path/to/your/xml/files' all_labels = extract_labels_from_xml(xml_directory) print("All used labels:", all_labels) ``` 这段代码能够遍历指定目录下的所有 `.xml` 文件,并收集每一个 `<object>` 节点内的 `<name>` 作为唯一的标签名称[^2]。 #### 方法三:利用 TXT 文件格式处理 YOLO 数据集 如果是采用 YOLO 格式存储的标注数据(`.txt`),则每一行代表一个边界框,其首列就是类别索引号。因此也可以写一段简单的 Python 程序统计不同编号所对应的类别数: ```python from collections import defaultdict import glob def count_yolo_classes(txt_folder_path): class_counts = defaultdict(int) txt_files = glob.glob(os.path.join(txt_folder_path, '*.txt')) for txt_file in txt_files: with open(txt_file, 'r') as f: lines = f.readlines() for line in lines: parts = line.split() if len(parts) >= 1 and parts[0].isdigit(): cls_id = int(parts[0]) class_counts[cls_id] += 1 return dict(class_counts) yolo_txts = '/path/to/yolo/txt/folder/' class_distribution = count_yolo_classes(yolo_txts) for cid, cnt in class_distribution.items(): print(f"Class ID {cid} appears {cnt} times.") ``` 以上程序适用于 YOLO 类型的数据集,能帮助快速定位哪些类别的实例数量较多或较少[^4]。 #### 总结 无论是哪种格式的标注文件,都可以借助编程手段高效地检索出历史标记记录。具体操作取决于项目选用的具体输出形式——VOC (.xml),YOLO(.txt),或其他自定义标准。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南洲.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值