合并使用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。
目标效果
原图:
标签1
标签2
标签合并
解决思路
通过分析 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)
参考链接:
本文地址:https://blog.csdn.net/weixin_41735859/article/details/107072533