前言
在做计算机视觉和深度学习的时候经常会有大量的数据集,这就牵扯到数据标注这件事情,尤其在一张图片进行多分类的情况下,手工标注费时费力,对于一些就是数据框每次创建,修订,以及分类选择,大部分时间都是花在了这些事情上,下面我从各个方向写了几个代码进行自动生成标注文件的内容(作者使用的是labelImg,标注文件选择的是xml文件,代码是使用的Python)。
- 创建xml标注文件
- 生成特定分类内容
- 特定标注文件内容的更改
- 特定数据框的删除
- 数据框的增加
注意,本篇代码写的较为简陋,能用,如有不懂得地方,在下方评论区评论即可。
创建xml文件以及生成默认的数据框
使用lxml创建一系列的数据标注xml标签文件,生成文件的元信息。
将分类信息写在data字典里面。注意改写图像的元信息(例如图片的尺寸啥的,不过这个一般在LabelImg同时打开图片的对应的xml会自动改写这些元信息,不过文件名称一定得对应起来。)
python
# _*_ coding: utf-8 _*_
# @Time : 2024/3/7 22:18
# @Author : jiaojiao
from lxml.etree import Element, tostring, parse
from lxml.etree import SubElement as subElement
def xml_construct(save_path, folder, filename, pngfilename, width=1920, height=1080, depth=3, segmented=0):
default_text = 'default'
node_root = Element('annotation') # 根节点
node_folder = subElement(node_root, 'folder') # 在节点下添加名为'folder'的子节点
node_folder.text = folder # 设定节点的文字
node_filename = subElement(node_root, 'filename')
node_filename.text = filename
node_path = subElement(node_root, 'path')
node_path.text = pngfilename
node_source = subElement(node_root, 'source')
node_source_width = subElement(node_source, 'database')
node_source_width.text = '%s' % 'Unknown'
node_size = subElement(node_root, 'size')
node_size_height = subElement(node_size, 'width')
node_size_height.text = '%s' % int(width)
node_size_height = subElement(node_size, 'height')
node_size_height.text = '%s' % int(height)
node_size_depth = subElement(node_size, 'depth')
node_size_depth.text = '%s' % int(depth)
node_segmented = subElement(node_root, 'segmented')
node_segmented.text = '%s' % int(segmented)
#上面都是xml文件的元信息
#下面是分类数据的标注信息
# # withers
# bust1_data = data["bust1"]
# node_object = subElement(node_root, 'object')
# node_name = subElement(node_object, "name")
# node_name.text = '%s' % 'bust1'
# node_pose = subElement(node_object, "pose")
# node_pose.text = '%s' % 'Unspecified'
# node_truncated = subElement(node_object, "truncated")
# node_truncated.text = '%s' % '0'
# node_difficult = subElement(node_object, "difficult")
# node_difficult.text = '%s' % '0'
# node_bndbox = subElement(node_object, "bndbox")
# node_xmin = subElement(node_bndbox, 'xmin')
# node_xmin.text = '%s' % bust1_data[0]
# node_ymin = subElement(node_bndbox, 'ymin')
# node_ymin.text = '%s' % bust1_data[1]
# node_xmax = subElement(node_bndbox, 'xmax')
# node_xmax.text = '%s' % bust1_data[2]
# node_ymax = subElement(node_bndbox, 'ymax')
# node_ymax.text = '%s' % bust1_data[3]
xml = tostring(node_root, pretty_print=True) # 将上面设定的一串节点信息导出
with open(save_path, 'wb') as f: # 将节点信息写入到文件路径中
f.write(xml)
return
def process_directory(begannum,endnum):
filename0 = "0275-1." #生成的标注文件文件名
files = [filename0 + f"{str(i):0>3}" + ".xml" for i in range(begannum,endnum+1)]
pngs = [filename0 + f"{str(i):0>3}" + ".png" for i in range(begannum,endnum+1)]
# print(files)
# 处理每个XML文件
for index in range(len(files)):
folder = './imgs_xml/' #存放特定文件夹下面
filename = pngs[index]
pngfilename = pngs[index] #标注文件名
xml_construct(folder+files[index], folder, filename, pngfilename)
if __name__ == "__main__":
# 标注文件数字
begannum = 1
endnum = 3
# 标注文件数据框数据(有多少分类写多少个)
data = {"withers":[1056,291,1127,357]}
process_directory(begannum,endnum)
删除特定文件的标注框
需要批量删除数据框的时候使用这段代码,
注意更改begannum和endnum,还有数据框名字
python
# _*_ coding: utf-8 _*_
# @Time : 2024/1/27 19:19
# @Author : jiaojiao
import os
from lxml import etree as ET
def remove_content_from_xml(xml_file, target_tag, remove_content):
try:
tree = ET.parse(xml_file)
root = tree.getroot()
# 查找目标标签
target_elements = root.xpath(f"{target_tag}")
print(target_elements)
# 删除目标标签下的内容
for element in target_elements:
name = element.xpath("name")[0]
if name.text==remove_content:
root.remove(element)
break
# 保存修改后的XML文件
tree.write(xml_file)
print("成功删除 {} 下的内容".format(target_tag))
except ET.ParseError as e:
print("解析XML文件时发生错误: {}".format(e))
except Exception as e:
print("发生错误: {}".format(e))
def process_directory(directory_path, target_tag, remove_content):
begannum = 34 #文件的开始和结束位置
endnum = 141
string = "0275-1.mp4L.mp4-"
files = [string + str(i) + ".xml" for i in range(begannum,endnum+1)]
# 处理每个XML文件
for file in files:
file_path = os.path.join(directory_path, file)
# print(file_path)
remove_content_from_xml(file_path, target_tag, remove_content)
if __name__ == "__main__":
# 指定要处理的目录和目标标签
directory_path = "F:\labelimg"
target_tag = "object"
remove_content = "joint1" #删除得数据框名称
# 处理目录下的所有XML文件
process_directory(directory_path, target_tag, remove_content)
删除冗余的图像文件
有时候会出现几张没有用的标注文件,可能失误操作导致的,这段代码就可以很好的解决。
bash
import os
folder1_path = 'F:\labelimg\labelimg\labelimg_first\1-2000\'
folder2_path = 'F:\labelimg\labelimg\labelimg_first\1-2000_Annotations\'
# 获取两个文件夹中的文件名(不包括后缀)
files_folder1 = [os.path.splitext(file)[0] for file in os.listdir(folder1_path)]
files_folder2 = [os.path.splitext(file)[0] for file in os.listdir(folder2_path)]
# 找到在folder2中存在但在folder1中不存在的文件
extra_files_folder2 = set(files_folder1) - set(files_folder2)
print(extra_files_folder2,len(set(files_folder1)))
# # 删除folder2中多余的文件
# for file in extra_files_folder2:
# file_path = os.path.join(folder2_path, file)
# # os.remove(file_path+".xml")
# print(f"Deleted: {file}")
print("Cleanup completed.")
修改标注文件个别标注的信息
ini
# _*_ coding: utf-8 _*_
# @Time : 2024/1/28 1:05
# @Author : jiaojiao
import os
from lxml import etree as ET
def set_content_from_xml(xml_file, set_content, data):
try:
tree = ET.parse(xml_file)
root = tree.getroot()
# 查找目标标签
target_elements = root.xpath("object")
# print(target_elements)
# 修改目标标签下的内容
for element in target_elements:
name = element.find("name")
# print(name.text)
if name.text == set_content:
xmin,ymin,xmax,ymax = data[0],data[1],data[2],data[3]
print(xmin)
bndbox = element.find("bndbox")
bndbox[0].text = xmin
bndbox[1].text = ymin
bndbox[2].text = xmax
bndbox[3].text = ymax
# 保存修改后的XML文件
tree.write(xml_file)
print("成功修改 {} 下的内容".format(xml_file))
except ET.ParseError as e:
print("解析XML文件时发生错误: {}".format(e))
except Exception as e:
print("发生错误: {}".format(e))
def process_directory(directory_path, set_content, data):
# 获取目录下的所有文件
# files = [f for f in os.listdir(directory_path) if f.endswith(".xml")]
begannum = 34
endnum = 41
string = "0275-1.mp4L.mp4-"
files = [string + f"{str(i):0>3}" + ".xml" for i in range(begannum,endnum+1)]
print(files)
# 处理每个XML文件
for file in files:
file_path = os.path.join(directory_path, file)
set_content_from_xml(file_path, set_content, data)
if __name__ == "__main__":
# 指定要处理的目录和目标标签
directory_path = "F:\labelimg\labelimg\labelimg_first\zszz\3-2000\Annotations\Annotations\Annotations\"
# directory_path = 'F:\labelImg(1)\脚本\'
set_content = "joint2"
data = ["1426","336","1508","415"]
# < xmin > 1426 < / xmin >
# < ymin > 336 < / ymin >
# < xmax > 1508 < / xmax >
# < ymax > 415 < / ymax >
# 处理目录下的所有XML文件
process_directory(directory_path, set_content, data)