xml转txt格式
import glob
import os
import xml.etree.ElementTree as ET
classes={0: 'hat',1: 'masks',2: 'aprons',3: 'no_hat',4: 'no_mask',5: 'no_aprons',6: 'people',7:"mouse",8:'overflow',9:'garbage',10:'garbage_bin',11:'smoke',12:"phone",13:"chefclothes",14:"nochefclothes"}
classes = list(classes.values())
def xml_to_yolo(xml_path,out_path):
global classes
# 解析XML文件
tree = ET.parse(xml_path)
root = tree.getroot()
# 获取图像尺寸
size = root.find('size')
width = int(size.find('width').text)
height = int(size.find('height').text)
# 初始化输出文件内容
output_lines = []
# 遍历所有对象
for obj in root.findall('object'):
# 获取类别名
cls_name = obj.find('name').text
if cls_name == 'without_mask':
cls_name = 'no_mask'
if cls_name == 'with_correct_mask':
cls_name = 'masks'
# 确保类别存在于类别列表中
if cls_name not in classes:
classes.append(cls_name)
if cls_name in classes:
cls_id = classes.index(cls_name)
# 计算YOLO格式的坐标和尺寸
bbox = obj.find('bndbox')
xmin = float(bbox.find('xmin').text)
ymin = float(bbox.find('ymin').text)
xmax = float(bbox.find('xmax').text)
ymax = float(bbox.find('ymax').text)
x_center = (xmin + xmax) / 2 / width
y_center = (ymin + ymax) / 2 / height
width_bbox = (xmax - xmin) / width
height_bbox = (ymax - ymin) / height
# 添加到输出行
output_line = f"{cls_id} {x_center:.6f} {y_center:.6f} {width_bbox:.6f} {height_bbox:.6f}"
output_lines.append(output_line)
# 写入到TXT文件
with open(out_path+xml_path[:-4].split('\\')[-1]+".txt", 'w') as f:
f.write('\n'.join(output_lines))
# 示例用法
# 类别列表
directory_path = 'train/Annotations'
out_path = 'train/labels_origin\\'
# 使用glob模块匹配所有.xml文件
xml_files = glob.glob(os.path.join(directory_path, '*.xml'))
# 打印所有找到的.xml文件路径
for file in xml_files:
print(file)
print(out_path + file[:-4].split('\\')[-1] + ".txt")
xml_to_yolo(file,out_path)
print(classes)
with open(out_path+'classes.txt', 'w') as f:
f.write('\n'.join(classes))
# output_path = 'path_to_output_txt_file.txt' # 输出TXT文件路径
#
yolo格式校验
import glob
import os
def validate_yolo_annotation(file_path):
"""
验证一个TXT文件是否符合YOLO格式。
:param file_path: TXT文件的路径。
:return: 如果文件格式正确返回True,否则返回False以及错误信息。
"""
with open(file_path, 'r') as file:
lines = file.readlines()
# 检查每行
for line_number, line in enumerate(lines, start=1):
parts = line.strip().split(" ")
# 每行应该至少有5个部分(class_id, x_center, y_center, width, height)
if len(parts) != 5:
return False, f"Line {line}"
try:
class_id = int(parts[0])
x_center = float(parts[1])
y_center = float(parts[2])
width = float(parts[3])
height = float(parts[4])
# 检查坐标和尺寸是否在合理范围内
if not (0 <= x_center <= 1 and 0 <= y_center <= 1 and 0 < width <= 1 and 0 < height <= 1):
return False, f"Line {line}"
except ValueError:
return False, f"Line {line}"
# 如果所有行都通过了检查
return True, ""
# 示例用法
directory_path = r'E:\zyx_project\new_shian\endtrain_img\labels'
# 使用glob模块匹配所有.xml文件
xml_files = glob.glob(os.path.join(directory_path, '*.txt'))
# 打印所有找到的.xml文件路径
for file in xml_files:
#print(file)
is_valid, message = validate_yolo_annotation(file)
if is_valid:
print(message,end='')
else:
print(file)
print(f"Validation failed: {message}")
txt里面标签混乱,批量替换。
import os
def count_classes_in_yolo_labels(directory):
class_counts = {}
# 遍历指定目录下的所有 .txt 文件
for filename in os.listdir(directory):
if filename.endswith("classes.txt"):
continue
if filename.endswith(".txt"):
filepath = os.path.join(directory, filename)
# 打开文件并读取每一行
with open(filepath, 'r') as file:
for line in file:
parts = line.strip().split()
if len(parts) > 0:
class_id = parts[0]
# 如果类别ID不在字典中,则添加它并设置计数为1;否则增加计数
if class_id not in class_counts:
class_counts[class_id] = 1
else:
class_counts[class_id] += 1
return class_counts
def replace_class_in_yolo_labels(directory, new_path,replace_class_dict):
# 遍历指定目录下的所有 .txt 文件
for filename in os.listdir(directory):
if filename.endswith("classes.txt"):
continue
if filename.endswith(".txt"):
filepath = os.path.join(directory, filename)
# 读取文件内容
with open(filepath, 'r') as file:
lines = file.readlines()
# 处理每一行,替换类别序号
modified_lines = []
for line in lines:
parts = line.strip().split()
if len(parts) > 0:
class_id = int(parts[0])
if class_id in replace_class_dict:
parts[0] = str(replace_class_dict[class_id])
modified_lines.append(' '.join(parts) + '\n')
# 写回修改后的内容
with open(new_path+filepath.split("\\")[-1], 'w') as file:
file.writelines(modified_lines)
if __name__ == '__main__':
type_dict = {0: 'hat', 1: 'mask', 2: 'aprons', 3: 'no_hat', 4: 'no_mask', 5: 'no_aprons', 6: 'people', 7: "mouse",
8: 'overflow', 9: 'garbage', 10: 'garbage_bin', 11: 'smoke', 12: "phone", 13: "chefclothes",
14: "nochefclothes"}
directory_path = 'new_image/labels_origin' # 替换为你的文件路径
new_path="new_image/labels/"
with open(directory_path+'/classes.txt', 'r') as f:
lines = f.readlines()
classes = [line.strip() for line in lines]
classes_dict = {i: classes[i] for i in range(len(classes))}
print(classes)
print("classes_dict:", classes_dict)
for i in type_dict.values():
print(i)
for key in list(classes_dict.keys()):
try:
classes_dict[key]=int(classes_dict[key])
except:
if classes_dict[key] in type_dict.values():
print(next((key for key, value in type_dict.items() if value == 1), None))
classes_dict[key]=list(type_dict.values()).index(classes_dict[key])
else:
print("classes_dict_no_have:", classes_dict[key])
del classes_dict[key]
print("classes_dict:", classes_dict)
# 使用函数
counts = count_classes_in_yolo_labels(directory_path)
# 输出结果
for class_id, count in counts.items():
print(f"Class {class_id}: {count} times")
replace_class_in_yolo_labels(directory_path,new_path,classes_dict)