标注工具
labelimg
数据清洗
在收集好所有图片以及对应的标签后,需要对数据进行清洗工作。包括剔除没有标签的图片,剔除没有图片的标签,统计数据中的标签名以及数量,重命名打错字的标签,合并标签,标签替换等等。
1. 统计数据中标签名
有时,可能出现标错标签名的情况,如有些 ‘peroson’ 的标签名打错了,写成了 ‘people’,或者同一类别中,有的写了下划线,有的使用空格表示,例如 ‘cargo_house’ 和 ‘cargo house’ 。统计出所有标签名以及类别数后,我们能够很方便的将错误找出来。
import os
import xml.dom.minidom
xml_path = '/home/jim/Desktop/合并/VOCdir/xml'
xmls = os.listdir(xml_path)
rate = {} # 创建一个字典用于存放标签名和对应的出现次数
for xml_file in xmls:
if xml_file.endswith('.xml'):
fullname = os.path.join(xml_path,xml_file)
dom = xml.dom.minidom.parse(fullname) # 打开XML文件
collection = dom.documentElement # 获取元素对象
objectlist = collection.getElementsByTagName('object') # 获取标签名为object的信息
for object in objectlist:
namelist = object.getElementsByTagName('name') # 获取子标签name的信息
objectname = namelist[0].childNodes[0].data # 取到name具体的值
if objectname not in rate: # 判断字典里有没有标签,如无添加相应字段
rate[objectname] = 0
rate[objectname] += 1
print(rate)
# 画图
rate = sorted(rate.items(), key=lambda x: x[1],reverse = True)
import matplotlib.pyplot as plt
object = []
number = []
for key in rate:
object.append(key[0])
number.append(key[1])
plt.figure()
plt.bar(object, number)
plt.title('result')
plt.show()
输出为:
{‘yes’: 2837, ‘person’: 10107, ‘cargo_house’: 3264, ‘crane boom’: 339, ‘console’: 4732, ‘no’: 2126, ‘excavator arm’: 2206, ‘hook arm’: 1500}
2. 批量重命名
如果存在类似于 ‘people’ 和 ‘person’ 这种标签名错误的情况,可以通过以下代码批量重命名。
import os
import os.path
from xml.etree.ElementTree import parse, Element
def changeName(xml_fold, origin_name, new_name):
files = os.listdir(xml_fold)
cnt = 0
for xmlFile in files:
if xmlFile.endswith('.xml'):
file_path = os.path.join(xml_fold, xmlFile)
dom = parse(file_path)
root = dom.getroot()
for obj in root.iter('object'):#获取object节点中的name子节点
tmp_name = obj.find('name').text
if tmp_name == origin_name: # 修改
obj.find('name').text = new_name
print("change %s to %s." % (origin_name, new_name))
cnt += 1
dom.write(file_path,encoding='utf-8')#保存到指定文件
print("有%d个文件被成功修改。" % cnt)
changeName('/home/jim/Desktop/合并/VOCdir/xml/','people','peroson')
3. 合并xml
对于某个文件夹下的所有图片,使用labelimg进行标注,获得voc类型的xml格式的标签。由于在进行标注工作时,将不同的类别分开标注了,得到标签文件夹有两个,第一个文件夹存储了类别 a 和类别 b 的 labels ,第二个文件夹存储了类别 c, d, e… 的 labels 。现在想要将相同图片的标签进行,即原本一张图片可能有两个对应的 xml,现在要使得一张图片只有一个对应的 xml。之前的博客有详细介绍:合并使用labelimg标注的同一张图片的两个不同xml标签
4. 标签替换
类似于3,同样对于某个文件夹下的所有图片,第一个文件夹存储了类别 a, b, c, d 的 labels,第二个文件夹存储了d的labels,发现第二个文件夹的d标签标注的更好,要将第一个文件夹下的d替换。
from xml.etree.ElementTree import ElementTree, Element, parse
import xml.etree.ElementTree as ET
import os
import shutil
# 以person类别为例,d表示person
old_path = 'a,b,c,d的labels路径'
new_xml_path = 'd类别标注的更好的xml路径'
out_path = '输出路径'
# 格式化
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
ori = 0
wen = 0
for file in os.listdir(old_path):
if file.endswith('.xml'):
# 找到同名的xml
if os.path.exists(os.path.join(new_xml_path, file)):
print('fusing',file)
# 原始标签
tree_ori = parse(os.path.join(old_path, file))
root_ori = tree_ori.getroot() # annotation
# 更好的person标签
tree_img = parse(os.path.join(new_xml_path,file))
root_img = tree_img.getroot() # annotation
# 找到差标签中所有的object
object_out = tree_ori.findall('object')
# print(object_person)
object_person_name = []
# 删除差xml中的person
for i in range(len(object_out)):
for leaf in list(object_out[i]):
if leaf.tag == 'name' and leaf.text == 'person':
root_ori.remove(object_out[i])
# 找到更好标签中所有的object
object_wen = tree_img.findall('object')
# 将更好标签中的person加入到差xml中
for i in range(len(object_wen)):
for leaf in list(object_wen[i]):
if leaf.tag == 'name' and leaf.text == 'person':
root_ori.append(object_wen[i])
__indent(root_ori)
tree_ori.write(os.path.join(out_path, file))
else:
# 将不同名的直接复制
print(file)
shutil.copy(os.path.join(old_path,file),os.path.join(out_path, file))