欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

合并使用labelimg标注的同一张图片的两个不同xml标签

程序员文章站 2023-11-26 22:42:16
待解决问题对于某个文件夹下的所有图片,使用labelimg进行标注,获得voc类型的xml格式的标签。由于在进行标注工作时,将不同的类别分开标注了,得到标签文件夹有两个,第一个文件夹存储了类别 a 和类别 b 的 labels ,第二个文件夹存储了类别 c, d, e… 的 labels 。现在想要将相同图片的标签进行,即原本一张图片可能有两个对应的 xml,现在要使得一张图片只有一个对应的 xml。目标效果原图:标签1标签2标签合并解决思路通过分析 xml 文件,得知关键的标签信息存在于...

待解决问题

对于某个文件夹下的所有图片,使用labelimg进行标注,获得voc类型的xml格式的标签。由于在进行标注工作时,将不同的类别分开标注了,得到标签文件夹有两个,第一个文件夹存储了类别 a 和类别 b 的 labels ,第二个文件夹存储了类别 c, d, e… 的 labels 。现在想要将相同图片的标签进行,即原本一张图片可能有两个对应的 xml,现在要使得一张图片只有一个对应的 xml。

目标效果

原图:
合并使用labelimg标注的同一张图片的两个不同xml标签标签1
合并使用labelimg标注的同一张图片的两个不同xml标签标签2
合并使用labelimg标注的同一张图片的两个不同xml标签标签合并
合并使用labelimg标注的同一张图片的两个不同xml标签

解决思路

通过分析 xml 文件,得知关键的标签信息存在于 xml 中的 object节点中,那么只要将相同图片的两个 xml 文件中的object进行拼接即可。

解决方案

例如:

xml1:

<annotation>
	<folder>images</folder>
	<filename>1_191.jpg</filename>
	<path>/home/shan/Desktop/VOC/images/1_191.jpg</path>
	<source>
		<database>Unknown</database>
	</source>
	<size>
		<width>1920</width>
		<height>1080</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>no</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>240</xmin>
			<ymin>37</ymin>
			<xmax>290</xmax>
			<ymax>79</ymax>
		</bndbox>
	</object>
	<object>
		<name>no</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>401</xmin>
			<ymin>23</ymin>
			<xmax>460</xmax>
			<ymax>65</ymax>
		</bndbox>
	</object>
</annotation>

xml2:

<annotation>
	<folder>xu</folder>
	<filename>1_191.jpg</filename>
	<path>/home/aibc/Desktop/xu/1_191.jpg</path>
	<source>
		<database>Unknown</database>
	</source>
	<size>
		<width>1920</width>
		<height>1080</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>excavator arm</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>938</xmin>
			<ymin>38</ymin>
			<xmax>1063</xmax>
			<ymax>116</ymax>
		</bndbox>
	</object>
	<object>
		<name>person</name>
		<pose>Unspecified</pose>
		<truncated>1</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>1037</xmin>
			<ymin>1</ymin>
			<xmax>1047</xmax>
			<ymax>28</ymax>
		</bndbox>
	</object>
</annotation>

xml1 和 xml2 都是 图片1_191.jpg 的标签,我们现在将它们合并。

先了解一些 xml 基本操作

读取

读取文档:tree = ET.parse()
获得根节点:root = tree.getroot()
获得所有子节点:list()
查找子节点,注意这里不会递归查找所有子节点:root.findall('object')
查找子节点,递归查找所有子节点:root.iter()
查看节点名称:root.tag

写入

创建节点:root = ET.Element)
创建文档:tree = ET.ElementTree()
设置文本值:element.text
设置属性:element.set()
添加节点:root.append()
写入文档:tree.write()

代码奉上

单张图片

from xml.etree.ElementTree import ElementTree, Element, parse
import xml.etree.ElementTree as ET

# 缩进对齐
def __indent(elem, level=0):
    i = "\n" + level*"\t"
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "\t"
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            __indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i

# 读取xml1文件
# 为了方便演示,我将两个 1_191.xml 分别手动重命名为 1_191_1.xml 和 1_191_2.xml
tree_1 = parse('1_191_1.xml')
root_1 = tree_1.getroot()   # annotation
new_1 = tree_1

# 读取xml2文件
tree_2 = parse('1_191_2.xml')
root_2 = tree_2.getroot()   # annotation
# 找到所有object节点
object = (tree_2.findall('object'))
length = len(object)
# 将tree_2中的所有object节点加入到root_1中
for i in range(length):
    root_1.append(object[i])
__indent(root_1)
new_1.write('./1_191.xml')

得到的输出结果为:

<annotation>
	<folder>images</folder>
	<filename>1_191.jpg</filename>
	<path>/home/shan/Desktop/VOC/images/1_191.jpg</path>
	<source>
		<database>Unknown</database>
	</source>
	<size>
		<width>1920</width>
		<height>1080</height>
		<depth>3</depth>
	</size>
	<segmented>0</segmented>
	<object>
		<name>no</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>240</xmin>
			<ymin>37</ymin>
			<xmax>290</xmax>
			<ymax>79</ymax>
		</bndbox>
	</object>
	<object>
		<name>no</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>401</xmin>
			<ymin>23</ymin>
			<xmax>460</xmax>
			<ymax>65</ymax>
		</bndbox>
	</object>
	<object>
		<name>excavator arm</name>
		<pose>Unspecified</pose>
		<truncated>0</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>938</xmin>
			<ymin>38</ymin>
			<xmax>1063</xmax>
			<ymax>116</ymax>
		</bndbox>
	</object>
	<object>
		<name>person</name>
		<pose>Unspecified</pose>
		<truncated>1</truncated>
		<difficult>0</difficult>
		<bndbox>
			<xmin>1037</xmin>
			<ymin>1</ymin>
			<xmax>1047</xmax>
			<ymax>28</ymax>
		</bndbox>
	</object>
</annotation>

可以看到,问题解决_

批量操作

from xml.etree.ElementTree import ElementTree, Element, parse
import xml.etree.ElementTree as ET
import os
import shutil

hole_path = './Annotations'
arm_path = './Anno'
out_path = './Fusing'

# 格式化
def __indent(elem, level=0):
    i = "\n" + level*"\t"
    if len(elem):
        if not elem.text or not elem.text.strip():
            elem.text = i + "\t"
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
        for elem in elem:
            __indent(elem, level+1)
        if not elem.tail or not elem.tail.strip():
            elem.tail = i
    else:
        if level and (not elem.tail or not elem.tail.strip()):
            elem.tail = i


for hole_xml in os.listdir(hole_path):
    # 将同名xml合并
    if os.path.exists(os.path.join(arm_path,hole_xml)):
        print('fusing',hole_xml)
        tree_hole = parse(os.path.join(hole_path,hole_xml))
        root_hole = tree_hole.getroot()  # annotation

        new_hole = tree_hole

        tree_arm = parse(os.path.join(arm_path,hole_xml))
        root_arm = tree_arm.getroot()  # annotation
        object = (tree_arm.findall('object'))
        for i in range(len(object)):
            root_hole.append(object[i])
        __indent(root_hole)
        new_hole.write(os.path.join(out_path,hole_xml))
    # 不同名xml复制
    else:
        print('copying',hole_xml)
        shutil.copy(os.path.join(hole_path,hole_xml), out_path)


# 将不同名xml复制
for arm_xml in os.listdir(arm_path):
    if not os.path.exists(os.path.join(out_path,arm_xml)):
        print('copying')
        shutil.copy(os.path.join(arm_path, arm_xml), out_path)

参考链接:

Python中XML的读写总结

本文地址:https://blog.csdn.net/weixin_41735859/article/details/107072533