了解:用 Python 合并多标注员标签文件,解决 YOLOv8 训练标注类别不一致问题
场景在使用YOLOv8训练目标检测模型时,标注数据往往需要一致的类别标签配置文件(classestxt)和标注文件格式。当多个标注人员各自进行标注而没有统一类别标签时,会导致不同的标签顺序和编号,造成训练时类别错乱。本文将展示如何利用Python自动化处理这种数据不一致问题,生成一个统一的classestxt,确保模型训练数据一致性。这是幽络源【浏览器搜索幽络源,分享原创教程】为大家准备的Python用教程。
问题描述
在数据标注阶段,多个标注员各自对图片进行标注,但每个标注员的classestxt文件内容不同。
如下所示:
标注员A的classestxt包含以下类别:
标注员B的classestxt内容为:
由于标签不一致,直接将数据合并会导致类别编号不匹配,影响模型的训练。为此,我们通过Python编写自动化脚本,将各标注员的标签文件合并,并按统一标准更新所有标注文件。
解决方案步骤
以下是详细的步骤说明:
比较后会给出一个完整的代码
步骤1:合并所有标注员的classestxt
首先,我们需要收集所有标注人员的classestxt文件,生成一个统一的标签列表merged_classestxt。该列表将包含所有类别标签,并按照字母顺序排列,确保编号统一。代码如下:
importos
defmerge_classes(classes_files):
all_classes=set()
forfileinclasses_files:
withopen(file,'r',encoding='utf-8')asf:
forlineinf:
class_name=linestrip()
ifclass_name:
all_classesadd(class_name)
returnsorted(all_classes)
步骤2:生成类别映表
在生成了统一的类别列表后,我们需要创建每个标注员标签编号到统一编号的映表。这个映表将帮助我们将各个标注文件中的类别编号替换为统一编号,确保一致性。代码如下:
defgenerate_m脚本ings(classes_files,merged_classes):
m脚本ings=
forfileinclasses_files:
m脚本ing={}
withopen(file,'r',encoding='utf-8')asf:
foridx,lineinenumerate(f):
class_name=linestrip()
ifclass_nameinmerged_classes:
m脚本ing=merged_classesindex(class_name)
m脚本ings脚本end(m脚本ing)
returnm脚本ings
步骤3:更新标注文件中的类别编号
通过映表,我们可以将每个标注文件的类别编号更新为统一编号。代码如下:
defupdate_label_file(file_path,m脚本ing):
new_lines=
withopen(file_path,'r',encoding='utf-8')asf:
forlineinf:
parts=linestrip()split()
class_id=int(parts)
new_class_id=m脚本ingget(class_id)
ifnew_class_idisnotNone:
new_line=f"{new_class_id}"+""join(parts)+"\n"
new_lines脚本end(new_line)
withopen(file_path,'w',encoding='utf-8')asf:
fwritelines(new_lines)
步骤4:批量更新所有标注文件
此步骤通过遍历每个标注员的文件夹,对其中的每个标注文件进行更新操作,以确保所有文件的类别编号统一。同时,通过生成m脚本ing_done文件,避免重复处理文件。代码如下:
defupdate_folder_labels(folder_path,m脚本ing):
flag_file=ospathjoin(folder_path,"m脚本ing_done")
ifospathexists(flag_file):
print(f"{folder_path}已处理过,跳过更新")
return
forfile_nameinoslistdir(folder_path):
iffile_nameendswith('txt'):
update_label_file(ospathjoin(folder_path,file_name),m脚本ing)
withopen(flag_file,'w')asf:
fwrite("m脚本ingdone")
print(f"{folder_path}内的标注文件已更新")
主函数:执行数据整合
主函数负责调用上述各步骤,对数据进行批量更新。首先定义标注人员的classestxt路径,然后保存合并后的merged_classestxt。接下来,针对每个标注文件夹,按步骤生成映并更新文件。
defmain():
classes_files=[
r"path_to_annotator1_classestxt",
r"path_to_annotator2_classestxt",
r"path_to_annotator3_classestxt"
]
merged_classes=merge_classes(classes_files)
withopen(r"path_to_save_merged_classestxt",'w',encoding='utf-8')asf:
fwrite("\n"join(merged_classes))
print("统一的classestxt已生成:",merged_classes)
m脚本ings=generate_m脚本ings(classes_files,merged_classes)
folders=[
r"path_to_annotator1_label_folder",
r"path_to_annotator2_label_folder",
r"path_to_annotator3_label_folder"
]
forfolder,m脚本inginzip(folders,m脚本ings):
update_folder_labels(folder,m脚本ing)
if__name__=="__main__":
main()
完整代码
注意main函数中的?classes_files?和?folders?一定要对应,不然会错乱,建议执行代码前先备份图片和标签,以便于对比校验
importos
#1收集所有标注人员的classestxt,并生成统一的classes
defmerge_classes(classes_files):
all_classes=set()
forfileinclasses_files:
withopen(file,'r',encoding='utf-8')asf:
forlineinf:
class_name=linestrip()
ifclass_name:
all_classesadd(class_name)
returnsorted(all_classes)
#2生成每个标注人员的类别映表
defgenerate_m脚本ings(classes_files,merged_classes):
m脚本ings=
forfileinclasses_files:
m脚本ing={}
withopen(file,'r',encoding='utf-8')asf:
foridx,lineinenumerate(f):
class_name=linestrip()
ifclass_nameinmerged_classes:
m脚本ing=merged_classesindex(class_name)
m脚本ings脚本end(m脚本ing)
returnm脚本ings
#3更新单个标注文件的类别编号
defupdate_label_file(file_path,m脚本ing):
new_lines=
withopen(file_path,'r',encoding='utf-8')asf:
forlineinf:
parts=linestrip()split()
class_id=int(parts)
#更新类别编号
new_class_id=m脚本ingget(class_id)
ifnew_class_idisnotNone:
new_line=f"{new_class_id}"+""join(parts)+"\n"
new_lines脚本end(new_line)
withopen(file_path,'w',encoding='utf-8')asf:
fwritelines(new_lines)
#4批量更新指定文件夹内的所有标注文件
defupdate_folder_labels(folder_path,m脚本ing):
#检查标志文件
flag_file=ospathjoin(folder_path,"m脚本ing_done")
ifospathexists(flag_file):
print(f"{folder_path}已处理过,跳过更新")
return
#更新标注文件
forfile_nameinoslistdir(folder_path):
iffile_nameendswith('txt'):#假设标注文件是txt格式
update_label_file(ospathjoin(folder_path,file_name),m脚本ing)
#创建标志文件
withopen(flag_file,'w')asf:
fwrite("m脚本ingdone")
print(f"{folder_path}内的标注文件已更新")
#主函数
defmain():
#标注人员的classestxt路径
classes_files=[
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\岔河大桥左幅万巴方向、山岔河大桥右幅巴万方向-杜怡鸿\classestxt",
#注人员1的classestxt路径
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\柯家河大桥左幅万巴方向、角河大桥右幅巴万方向、角河大桥左幅万巴方向-蒋松林\classestxt",
#注人员2的classestxt路径
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\陈家河大桥右副巴万方向,柯家河大桥右副巴万方向-申恭伟\classestxt"
#注人员3的classestxt路径
]
#生成合并后的类别列表
merged_classes=merge_classes(classes_files)
#保存新的classestxt
withopen(r"D:\A01PythonProjects3123\标注合并\截止1028merged_classestxt",'w',encoding='utf-8')asf:
fwrite("\n"join(merged_classes))
print("统一的classestxt已生成:",merged_classes)
#生成每个标注人员的类别映表
m脚本ings=generate_m脚本ings(classes_files,merged_classes)
#更新标注文件夹路径
folders=[
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\岔河大桥左幅万巴方向、山岔河大桥右幅巴万方向-杜怡鸿\label",
#注人员1的标注文件夹
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\柯家河大桥左幅万巴方向、角河大桥右幅巴万方向、角河大桥左幅万巴方向-蒋松林\label",
#注人员2的标注文件夹
r"D:\A01PythonProjects3123\标注合并\截止1028\1017缺陷汇总\陈家河大桥右副巴万方向,柯家河大桥右副巴万方向-申恭伟\label"
#注人员3的标注文件夹
]
#执行批量更新
forfolder,m脚本inginzip(folders,m脚本ings):
update_folder_labels(folder,m脚本ing)
if__name__=="__main__":
main()
合并后检验
可以看到合并后classestxt文件变为了如下
用原来的标签对比现在合并后的标签看是否正确
再查看现在更新后的标签与合并后的classes的对应关系是否与原来的对应关系一致
可以看到,合并更新标签后,对应关系是正确的,因此现在可以将所有图片和所有标签分别放入同一个目录,使用合并后的classestxt进行训练了
总结
通过以上方法,我们现了自动化合并多个标注员的标签文件,并统一了标注文件的类别编号。这样处理后的数据可以直接用于YOLOv8模型训练,避免了因为类别标签不一致带来的问题。幽络源持续分享用的Python工具与技术教程,帮助您更高效地处理数据和训练模型。
?
做这些简单的事情,也可以让源码网播种下优质的基因,最终长成参天大树,成为行业的翘楚。提供经过严格测试的免费源码、各种线上兼职和网络兼职的网创教程、编程及网络相关的技术教程分享,助您轻松获取资源和技术支持。https://www.youluoyuan.com/https://www.youluoyuan.com/wp-content/uploads/2024/10/ylyapp.png
页:
[1]