VOC格式转YOLO格式,xml文件转txt文件简单通用代码

目录

前言

思路介绍

代码

完整代码

拓展代码


前言

        很多人在进行目标检测训练时习惯将得到的数据标注为XML文件的VOC格式,或者在网上获取的数据集被标注为XML文件,但是不同的标注工具进行的标注会产生不同的标注xml文件,这里我写了一种通用的针对含有最基本图片和标注坐标信息的xml进行转换,在这里简单介绍并分享出来

思路介绍

        xml文件中最基本需要含有的信息为size,object下的name和bndbox,具体示例如下图(如果xml文件中没有size也就是图片的宽和高则需要单独对每个图片进行读取,感兴趣可以私聊,这里不展开介绍)

 

        可以看到这几个标签下包含了标注的全部信息,接着进行转换

代码

        核心代码为,提取所需要的信息

size = root.find('size')
width = int(size.find('width').text)
height = int(size.find('height').text)
# 存储name和对应的归一化坐标
objects = []
# 遍历XML中的object标签
for obj in root.findall('object'):
	name = obj.find('name').text
	if name in category_to_index:
		category_index = category_to_index[name]
	else:
		continue  # 如果name不在指定类别中,跳过该object
	bndbox = obj.find('bndbox')
	xmin = int(bndbox.find('xmin').text)
	ymin = int(bndbox.find('ymin').text)
	xmax = int(bndbox.find('xmax').text)
	ymax = int(bndbox.find('ymax').text)

        归一化代码如下,这也是YOLO格式的通用归一化代码

x_center = (xmin + xmax) / 2.0
y_center = (ymin + ymax) / 2.0
w = xmax - xmin
h = ymax - ymin

x = x_center / width
y = y_center / height
w = w / width
h = h / height

        这里最下边四行代码即为txt中每一行后四位数字

完整代码

        完整代码如下

import os
import xml.etree.ElementTree as ET

# 定义类别顺序
categories = ['eggplant']
category_to_index = {category: index for index, category in enumerate(categories)}

# 定义输入文件夹和输出文件夹
input_folder = r'D:\Annotations'  # 替换为实际的XML文件夹路径
output_folder = r'D:\labels'  # 替换为实际的输出TXT文件夹路径

# 确保输出文件夹存在
os.makedirs(output_folder, exist_ok=True)

# 遍历输入文件夹中的所有XML文件
for filename in os.listdir(input_folder):
	if filename.endswith('.xml'):
		xml_path = os.path.join(input_folder, filename)
		# 解析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)
		# 存储name和对应的归一化坐标
		objects = []

		# 遍历XML中的object标签
		for obj in root.findall('object'):
			name = obj.find('name').text
			if name in category_to_index:
				category_index = category_to_index[name]
			else:
				continue  # 如果name不在指定类别中,跳过该object

			bndbox = obj.find('bndbox')
			xmin = int(bndbox.find('xmin').text)
			ymin = int(bndbox.find('ymin').text)
			xmax = int(bndbox.find('xmax').text)
			ymax = int(bndbox.find('ymax').text)

			# 转换为中心点坐标和宽高
			x_center = (xmin + xmax) / 2.0
			y_center = (ymin + ymax) / 2.0
			w = xmax - xmin
			h = ymax - ymin

			# 归一化
			x = x_center / width
			y = y_center / height
			w = w / width
			h = h / height

			objects.append(f"{category_index} {x} {y} {w} {h}")

		# 输出结果到对应的TXT文件
		txt_filename = os.path.splitext(filename)[0] + '.txt'
		txt_path = os.path.join(output_folder, txt_filename)
		with open(txt_path, 'w') as f:
			for obj in objects:
				f.write(obj + '\n')

拓展代码

        这个代码类别还需要自己获取并填写,这里给出一种更简单的方法,可以省去填写标签列表的环节并且自动类别编号,完整代码如下

import os
import xml.etree.ElementTree as ET
names_set = set()

input_folder = r'D:\Annotations'  # 替换为实际的XML文件夹路径
output_folder = r'D:\labels'  # 替换为实际的输出TXT文件夹路径

for filename in os.listdir(input_folder):
	if filename.endswith('.xml'):
		tree = ET.parse(os.path.join(input_folder, filename))
		root = tree.getroot()

		for obj in root.findall('object'):
			name = obj.find('name').text
			names_set.add(name)
# 输出所有的name
categories = []
for name in names_set:
	categories.append(name)
print(categories)

category_to_index = {category: index for index, category in enumerate(categories)}
os.makedirs(output_folder, exist_ok=True)

# 遍历输入文件夹中的所有XML文件
for filename in os.listdir(input_folder):
	if filename.endswith('.xml'):
		xml_path = os.path.join(input_folder, filename)
		# 解析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)
		# 存储name和对应的归一化坐标
		objects = []
		# 遍历XML中的object标签
		for obj in root.findall('object'):
			name = obj.find('name').text
			if name in category_to_index:
				category_index = category_to_index[name]
			else:
				continue  # 如果name不在指定类别中,跳过该object
			bndbox = obj.find('bndbox')
			xmin = int(bndbox.find('xmin').text)
			ymin = int(bndbox.find('ymin').text)
			xmax = int(bndbox.find('xmax').text)
			ymax = int(bndbox.find('ymax').text)
			# 转换为中心点坐标和宽高
			x_center = (xmin + xmax) / 2.0
			y_center = (ymin + ymax) / 2.0
			w = xmax - xmin
			h = ymax - ymin
			# 归一化
			x = x_center / width
			y = y_center / height
			w = w / width
			h = h / height
			objects.append(f"{category_index} {x} {y} {w} {h}")
		# 输出结果到对应的TXT文件
		txt_filename = os.path.splitext(filename)[0] + '.txt'
		txt_path = os.path.join(output_folder, txt_filename)
		with open(txt_path, 'w') as f:
			for obj in objects:
				f.write(obj + '\n')

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值