在目标检测任务中,标注文件的格式往往需要与训练框架相匹配。YOLO 系列模型通常采用 TXT 格式的标注文件,而很多标注工具(如 LabelImg)默认生成的是 XML 格式标注。今天分享一段实用代码,可快速将 XML 标注文件转换为 YOLO 所需的 TXT 格式,同时自动整理图片和标注文件路径,方便后续模型训练。
代码功能概述
该代码主要实现以下功能:
-
解析 XML 格式的标注文件,提取目标类别、边界框坐标等信息
-
将 XML 中的边界框坐标(xmin, ymin, xmax, ymax)转换为 YOLO 格式的归一化坐标(中心 x, 中心 y, 宽,高)
-
为每张图片生成对应的 TXT 标注文件
-
自动复制图片到指定目录,与标注文件对应存放
完整代码
#xml文件转txt文件
import os
import random
import xml.etree.ElementTree as ET
import glob
import shutil
#img存放文件夹
ImgPath = 'datasets/images'
#xml存放文件夹
XmlPath = 'datasets/Annotations'
#处理后数据集存放位置
txtSavePath = 'datasets/labels'
fileWriteText = txtSavePath + '/' + ImgPath.split('/')[-1]+'_v8.txt'
xmls = glob.glob(os.path.join(XmlPath,'*.xml'))
txtpathdir = fileWriteText
savetxtpath = txtSavePath + '/labels'
txtpathdir = fileWriteText
saveimgpath = txtSavePath + '/images'
# 不存在,创建目录
if not os.path.exists(savetxtpath):
os.makedirs(savetxtpath)
if not os.path.exists(saveimgpath):
os.makedirs(saveimgpath)
list_file = open(fileWriteText,'w',encoding='utf-8')
#标签类别
classes = ['自定义'] #修改为自己的类别
def convert(size, box):
dw = 1. / size[0]
dh = 1. / size[1]
x = (box[0] + box[2]) / 2.0
y = (box[1] + box[3]) / 2.0
w = min(size[0], box[2] - box[0])
h = min(size[1], box[3] - box[1])
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
def convert_annotation(xml, list_file):
in_file = open(os.path.join(xml), encoding='utf-8')
tree = ET.parse(in_file)
root = tree.getroot()
xmlsize = root.find('size')
w = int(xmlsize.find('width').text)
h = int(xmlsize.find('height').text)
for obj in root.iter('object'):
difficult = 0
if obj.find('difficult') != None:
difficult = obj.find('difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
print(cls + "----------------------------------" + '\n')
continue
cls_id = classes.index(cls)
print(cls, cls_id)
xmlbox = obj.find('bndbox')
x0 = float(xmlbox.find('xmin').text)
y0 = float(xmlbox.find('ymin').text)
x1 = float(xmlbox.find('xmax').text)
y1 = float(xmlbox.find('ymax').text)
xmin = min(x0, x1)
ymin = min(y0, y1)
xmax = max(x0, x1)
ymax = max(y0, y1)
b = (float(xmin), float(ymin), float(xmax), float(ymax))
bb = convert((w, h), b)
list_file.write(" " + str(cls_id) + ',' + ','.join([str(a) for a in bb]))
for xml in xmls:
img = xml.replace(XmlPath,ImgPath).replace('.xml','.jpg')
if not os.path.exists(img):
print(img,'is not exit')
continue
list_file.write(img)
convert_annotation(xml,list_file)
list_file.write('\n')
list_file.close()
file = open(txtpathdir,'r',encoding='utf-8')
lines = file.readlines()
for line in lines:
line = line.split('\n')[0]
imgdir = line.split(' ')[0]
bboxinfo = line.split(' ')[1:]
savetxtdir = os.path.join(savetxtpath,imgdir.split('\\')[-1].replace('.jpg','.txt'))
file = open(savetxtdir,'w',encoding='utf-8')
for i in range(len(bboxinfo)):
info = bboxinfo[i].split(',')
info1 = ' '.join(info)
print(info1)
file.write(info1+'\n')
file.close()
file = open(txtpathdir,'r',encoding='utf-8')
lines = file.readlines()
for line in lines:
line = line.split('\n')[0]
imgdir = line.split(' ')[0]
print('imgdir',imgdir)
#print(imgdir.split('\\')[-1])
saveimgdir = os.path.join(saveimgpath,imgdir.split('\\')[-1])
print(saveimgdir)
shutil.copy(imgdir,saveimgdir)
file.close()
核心代码解析
1. 环境与路径配置
首先导入必要的库,并设置文件路径(需根据自身数据集修改):
import os
import xml.etree.ElementTree as ET
import glob
import shutil
# 图片存放路径
ImgPath = 'datasets/images'
# XML标注文件存放路径
XmlPath = 'datasets/Annotations'
# 转换后数据集保存路径
txtSavePath = 'datasets/labels'
# 输出目录自动创建(确保路径存在)
savetxtpath = txtSavePath + '/labels' # TXT标注文件保存路径
saveimgpath = txtSavePath + '/images' # 图片复制目标路径
if not os.path.exists(savetxtpath):
os.makedirs(savetxtpath)
if not os.path.exists(saveimgpath):
os.makedirs(saveimgpath)
2. 类别设置
需根据自己的数据集类别修改classes列表,类别顺序决定了 YOLO 标注中的类别 ID:
# 标注类别(需替换为自己的目标类别)
classes = ['自定义'] # 例如:['person', 'car', 'dog']
3. 坐标转换函数(核心)
YOLO 格式要求边界框坐标以图片宽高为基准归一化,转换公式如下:
-
中心 x = (xmin + xmax) / 2 / 图片宽度
-
中心 y = (ymin + ymax) / 2 / 图片高度
-
宽 = (xmax - xmin) / 图片宽度
-
高 = (ymax - ymin) / 图片高度
def convert(size, box):
dw = 1. / size[0] # 1/图片宽度(归一化系数)
dh = 1. / size[1] # 1/图片高度(归一化系数)
x = (box[0] + box[2]) / 2.0 # 中心x坐标
y = (box[1] + box[3]) / 2.0 # 中心y坐标
w = min(size[0], box[2] - box[0]) # 宽(避免超出图片范围)
h = min(size[1], box[3] - box[1]) # 高(避免超出图片范围)
x = x * dw # 归一化
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
使用注意事项
-
路径修改:根据自己的数据集位置,修改
ImgPath、XmlPath、txtSavePath -
类别修改:必须将
classes列表替换为自己的目标类别(如['cat', 'dog']) -
图片格式:代码默认图片为.jpg 格式,若为.png 等其他格式,需修改
replace('.xml', '.jpg')部分 -
异常处理:代码会自动跳过无对应图片的 XML 文件,以及不在类别列表中的目标
3507

被折叠的 条评论
为什么被折叠?



