首页 > Python资料 博客日记
python:VOC格式数据集转换为YOLO数据集格式
2024-10-03 08:00:05Python资料围观27次
这篇文章介绍了python:VOC格式数据集转换为YOLO数据集格式,分享给大家做个参考,收藏Python资料网收获更多编程知识
作者:CSDN @ _养乐多_
本文将介绍如何将目标检测中常用的VOC格式数据集转换为YOLO数据集,并进行数据集比例划分,从而方便的进行YOLO目标检测。
如果不想分两步,可以直接看第三节代码。
一、将VOC格式数据集转换为YOLO格式数据集
执行以下脚本将VOC格式数据集转换为YOLO格式数据集。
但是需要注意的是:
- 转换之后的数据集只有Images和labels两个文件。还需要执行第二节中的脚本进行数据集划分,将总的数据集划分为训练、验证、测试数据集;
- 使用的话,需要修改 class_mapping 中类别名和对应标签,还有VOC数据集路径、YOLO数据集路径。
import os
import shutil
import xml.etree.ElementTree as ET
# VOC格式数据集路径
voc_data_path = 'E:\\DataSet\\helmet-VOC'
voc_annotations_path = os.path.join(voc_data_path, 'Annotations')
voc_images_path = os.path.join(voc_data_path, 'JPEGImages')
# YOLO格式数据集保存路径
yolo_data_path = 'E:\\DataSet\\helmet-YOLO'
yolo_images_path = os.path.join(yolo_data_path, 'images')
yolo_labels_path = os.path.join(yolo_data_path, 'labels')
# 创建YOLO格式数据集目录
os.makedirs(yolo_images_path, exist_ok=True)
os.makedirs(yolo_labels_path, exist_ok=True)
# 类别映射 (可以根据自己的数据集进行调整)
class_mapping = {
'head': 0,
'helmet': 1,
'person': 2,
# 添加更多类别...
}
def convert_voc_to_yolo(voc_annotation_file, yolo_label_file):
tree = ET.parse(voc_annotation_file)
root = tree.getroot()
size = root.find('size')
width = float(size.find('width').text)
height = float(size.find('height').text)
with open(yolo_label_file, 'w') as f:
for obj in root.findall('object'):
cls = obj.find('name').text
if cls not in class_mapping:
continue
cls_id = class_mapping[cls]
xmlbox = obj.find('bndbox')
xmin = float(xmlbox.find('xmin').text)
ymin = float(xmlbox.find('ymin').text)
xmax = float(xmlbox.find('xmax').text)
ymax = float(xmlbox.find('ymax').text)
x_center = (xmin + xmax) / 2.0 / width
y_center = (ymin + ymax) / 2.0 / height
w = (xmax - xmin) / width
h = (ymax - ymin) / height
f.write(f"{cls_id} {x_center} {y_center} {w} {h}\n")
# 遍历VOC数据集的Annotations目录,进行转换
for voc_annotation in os.listdir(voc_annotations_path):
if voc_annotation.endswith('.xml'):
voc_annotation_file = os.path.join(voc_annotations_path, voc_annotation)
image_id = os.path.splitext(voc_annotation)[0]
voc_image_file = os.path.join(voc_images_path, f"{image_id}.jpg")
yolo_label_file = os.path.join(yolo_labels_path, f"{image_id}.txt")
yolo_image_file = os.path.join(yolo_images_path, f"{image_id}.jpg")
convert_voc_to_yolo(voc_annotation_file, yolo_label_file)
if os.path.exists(voc_image_file):
shutil.copy(voc_image_file, yolo_image_file)
print("转换完成!")
二、YOLO格式数据集划分(训练、验证、测试)
参考:https://docs.ultralytics.com/datasets/detect/#ultralytics-yolo-format
随机将数据集按照0.7-0.2-0.1比例划分为训练、验证、测试数据集。
注意,修改代码中图片的后缀,如果是.jpg,就把.png修改为.jpg。
最终结果,
2.1 版本1
用版本1划分就行,不用版本2了。
import os
import shutil
import random
from math import floor
# 创建输出目录的函数
def create_dirs(output_dir):
images_dir = os.path.join(output_dir, 'images')
labels_dir = os.path.join(output_dir, 'labels')
for split in ['train', 'val', 'test']:
os.makedirs(os.path.join(images_dir, split), exist_ok=True)
os.makedirs(os.path.join(labels_dir, split), exist_ok=True)
return images_dir, labels_dir
# 获取图片和对应txt标签的列表
def get_files(images_path, labels_path):
image_files = [f for f in os.listdir(images_path) if f.endswith(('jpg', 'png', 'jpeg'))]
label_files = [f for f in os.listdir(labels_path) if f.endswith('.txt')]
# 检查图片和标签是否配对
paired_files = []
for image_file in image_files:
base_name = os.path.splitext(image_file)[0]
label_file = base_name + '.txt'
if label_file in label_files:
paired_files.append((image_file, label_file))
return paired_files
# 将文件按比例划分并拷贝到相应目录
def split_and_copy(paired_files, images_path, labels_path, images_dir, labels_dir, train_ratio, val_ratio):
random.shuffle(paired_files) # 随机打乱
total_files = len(paired_files)
train_count = floor(total_files * train_ratio)
val_count = floor(total_files * val_ratio)
test_count = total_files - train_count - val_count
splits = {
'train': paired_files[:train_count],
'val': paired_files[train_count:train_count + val_count],
'test': paired_files[train_count + val_count:]
}
for split, files in splits.items():
for image_file, label_file in files:
shutil.copy(os.path.join(images_path, image_file), os.path.join(images_dir, split, image_file))
shutil.copy(os.path.join(labels_path, label_file), os.path.join(labels_dir, split, label_file))
print(f'{split}: {len(files)} files')
# 主函数
def main():
# 写死的路径
images_path = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\totalImages" # 替换为实际图片文件夹路径
labels_path = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\totalLabels" # 替换为实际txt文件夹路径
output_dir = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\output" # 替换为实际输出主目录路径
# 数据划分比例
train_ratio = 0.7
val_ratio = 0.3
test_ratio = 0
# 容差值用于浮点数比较
epsilon = 1e-6
# 确保比例之和等于1
assert abs(train_ratio + val_ratio + test_ratio - 1) < epsilon, "比例之和必须等于1"
# 创建目录
images_dir, labels_dir = create_dirs(output_dir)
# 获取文件列表
paired_files = get_files(images_path, labels_path)
# 进行划分并拷贝
split_and_copy(paired_files, images_path, labels_path, images_dir, labels_dir, train_ratio, val_ratio)
# 调用主函数
if __name__ == "__main__":
main()
2.2 版本2
import os
import shutil
import random
# YOLO格式数据集保存路径
yolo_images_path1 = 'E:\\DataSet\\helmet-VOC'
yolo_labels_path1 = 'E:\\DataSet\\helmet-YOLO'
yolo_data_path = yolo_labels_path1
yolo_images_path = os.path.join(yolo_images_path1, 'JPEGImages')
yolo_labels_path = os.path.join(yolo_labels_path1, 'labels')
# 创建划分后的目录结构
train_images_path = os.path.join(yolo_data_path, 'train', 'images')
train_labels_path = os.path.join(yolo_data_path, 'train', 'labels')
val_images_path = os.path.join(yolo_data_path, 'val', 'images')
val_labels_path = os.path.join(yolo_data_path, 'val', 'labels')
test_images_path = os.path.join(yolo_data_path, 'test', 'images')
test_labels_path = os.path.join(yolo_data_path, 'test', 'labels')
os.makedirs(train_images_path, exist_ok=True)
os.makedirs(train_labels_path, exist_ok=True)
os.makedirs(val_images_path, exist_ok=True)
os.makedirs(val_labels_path, exist_ok=True)
os.makedirs(test_images_path, exist_ok=True)
os.makedirs(test_labels_path, exist_ok=True)
# 获取所有图片文件名(不包含扩展名)
image_files = [f[:-4] for f in os.listdir(yolo_images_path) if f.endswith('.png')]
# 随机打乱文件顺序
random.shuffle(image_files)
# 划分数据集比例
train_ratio = 0.7
val_ratio = 0.2
test_ratio = 0.1
train_count = int(train_ratio * len(image_files))
val_count = int(val_ratio * len(image_files))
test_count = len(image_files) - train_count - val_count
train_files = image_files[:train_count]
val_files = image_files[train_count:train_count + val_count]
test_files = image_files[train_count + val_count:]
# 移动文件到相应的目录
def move_files(files, src_images_path, src_labels_path, dst_images_path, dst_labels_path):
for file in files:
src_image_file = os.path.join(src_images_path, f"{file}.png")
src_label_file = os.path.join(src_labels_path, f"{file}.txt")
dst_image_file = os.path.join(dst_images_path, f"{file}.png")
dst_label_file = os.path.join(dst_labels_path, f"{file}.txt")
if os.path.exists(src_image_file) and os.path.exists(src_label_file):
shutil.move(src_image_file, dst_image_file)
shutil.move(src_label_file, dst_label_file)
# 移动训练集文件
move_files(train_files, yolo_images_path, yolo_labels_path, train_images_path, train_labels_path)
# 移动验证集文件
move_files(val_files, yolo_images_path, yolo_labels_path, val_images_path, val_labels_path)
# 移动测试集文件
move_files(test_files, yolo_images_path, yolo_labels_path, test_images_path, test_labels_path)
print("数据集划分完成!")
三、一步到位
如果不想分两步进行格式转换,那么以下脚本结合了以上两步,直接得到最后按比例划分训练、验证、测试的数据集结果。
注意:需要修改 voc_data_path ,yolo_data_path ,class_mapping 以及 ‘.png’ 后缀。
import os
import shutil
import random
import xml.etree.ElementTree as ET
from tqdm import tqdm
# VOC格式数据集路径
voc_data_path = 'E:\\DataSet-VOC'
voc_annotations_path = os.path.join(voc_data_path, 'Annotations')
voc_images_path = os.path.join(voc_data_path, 'JPEGImages')
# YOLO格式数据集保存路径
yolo_data_path = 'E:\\DataSet-YOLO'
yolo_images_path = os.path.join(yolo_data_path, 'images')
yolo_labels_path = os.path.join(yolo_data_path, 'labels')
# 创建YOLO格式数据集目录
os.makedirs(yolo_images_path, exist_ok=True)
os.makedirs(yolo_labels_path, exist_ok=True)
# 类别映射 (可以根据自己的数据集进行调整)
class_mapping = {
'head': 0,
'helmet': 1,
'person': 2,
# 添加更多类别...
}
def convert_voc_to_yolo(voc_annotation_file, yolo_label_file):
tree = ET.parse(voc_annotation_file)
root = tree.getroot()
size = root.find('size')
width = float(size.find('width').text)
height = float(size.find('height').text)
with open(yolo_label_file, 'w') as f:
for obj in root.findall('object'):
cls = obj.find('name').text
if cls not in class_mapping:
continue
cls_id = class_mapping[cls]
xmlbox = obj.find('bndbox')
xmin = float(xmlbox.find('xmin').text)
ymin = float(xmlbox.find('ymin').text)
xmax = float(xmlbox.find('xmax').text)
ymax = float(xmlbox.find('ymax').text)
x_center = (xmin + xmax) / 2.0 / width
y_center = (ymin + ymax) / 2.0 / height
w = (xmax - xmin) / width
h = (ymax - ymin) / height
f.write(f"{cls_id} {x_center} {y_center} {w} {h}\n")
# 遍历VOC数据集的Annotations目录,进行转换
print("开始VOC到YOLO格式转换...")
for voc_annotation in tqdm(os.listdir(voc_annotations_path)):
if voc_annotation.endswith('.xml'):
voc_annotation_file = os.path.join(voc_annotations_path, voc_annotation)
image_id = os.path.splitext(voc_annotation)[0]
voc_image_file = os.path.join(voc_images_path, f"{image_id}.png")
yolo_label_file = os.path.join(yolo_labels_path, f"{image_id}.txt")
yolo_image_file = os.path.join(yolo_images_path, f"{image_id}.png")
convert_voc_to_yolo(voc_annotation_file, yolo_label_file)
if os.path.exists(voc_image_file):
shutil.copy(voc_image_file, yolo_image_file)
print("VOC到YOLO格式转换完成!")
# 划分数据集
train_images_path = os.path.join(yolo_data_path, 'train', 'images')
train_labels_path = os.path.join(yolo_data_path, 'train', 'labels')
val_images_path = os.path.join(yolo_data_path, 'val', 'images')
val_labels_path = os.path.join(yolo_data_path, 'val', 'labels')
test_images_path = os.path.join(yolo_data_path, 'test', 'images')
test_labels_path = os.path.join(yolo_data_path, 'test', 'labels')
os.makedirs(train_images_path, exist_ok=True)
os.makedirs(train_labels_path, exist_ok=True)
os.makedirs(val_images_path, exist_ok=True)
os.makedirs(val_labels_path, exist_ok=True)
os.makedirs(test_images_path, exist_ok=True)
os.makedirs(test_labels_path, exist_ok=True)
# 获取所有图片文件名(不包含扩展名)
image_files = [f[:-4] for f in os.listdir(yolo_images_path) if f.endswith('.png')]
# 随机打乱文件顺序
random.shuffle(image_files)
# 划分数据集比例
train_ratio = 0.7
val_ratio = 0.2
test_ratio = 0.1
train_count = int(train_ratio * len(image_files))
val_count = int(val_ratio * len(image_files))
test_count = len(image_files) - train_count - val_count
train_files = image_files[:train_count]
val_files = image_files[train_count:train_count + val_count]
test_files = image_files[train_count + val_count:]
# 移动文件到相应的目录
def move_files(files, src_images_path, src_labels_path, dst_images_path, dst_labels_path):
for file in tqdm(files):
src_image_file = os.path.join(src_images_path, f"{file}.png")
src_label_file = os.path.join(src_labels_path, f"{file}.txt")
dst_image_file = os.path.join(dst_images_path, f"{file}.png")
dst_label_file = os.path.join(dst_labels_path, f"{file}.txt")
if os.path.exists(src_image_file) and os.path.exists(src_label_file):
shutil.move(src_image_file, dst_image_file)
shutil.move(src_label_file, dst_label_file)
# 移动训练集文件
print("移动训练集文件...")
move_files(train_files, yolo_images_path, yolo_labels_path, train_images_path, train_labels_path)
# 移动验证集文件
print("移动验证集文件...")
move_files(val_files, yolo_images_path, yolo_labels_path, val_images_path, val_labels_path)
# 移动测试集文件
print("移动测试集文件...")
move_files(test_files, yolo_images_path, yolo_labels_path, test_images_path, test_labels_path)
print("数据集划分完成!")
# 删除原始的 images 和 labels 文件夹
shutil.rmtree(yolo_images_path)
shutil.rmtree(yolo_labels_path)
print("原始 images 和 labels 文件夹删除完成!")
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签:
相关文章
最新发布
- 【Python】selenium安装+Microsoft Edge驱动器下载配置流程
- Python 中自动打开网页并点击[自动化脚本],Selenium
- Anaconda基础使用
- 【Python】成功解决 TypeError: ‘<‘ not supported between instances of ‘str’ and ‘int’
- manim边学边做--三维的点和线
- CPython是最常用的Python解释器之一,也是Python官方实现。它是用C语言编写的,旨在提供一个高效且易于使用的Python解释器。
- Anaconda安装配置Jupyter(2024最新版)
- Python中读取Excel最快的几种方法!
- Python某城市美食商家爬虫数据可视化分析和推荐查询系统毕业设计论文开题报告
- 如何使用 Python 批量检测和转换 JSONL 文件编码为 UTF-8
点击排行
- 版本匹配指南:Numpy版本和Python版本的对应关系
- 版本匹配指南:PyTorch版本、torchvision 版本和Python版本的对应关系
- Python 可视化 web 神器:streamlit、Gradio、dash、nicegui;低代码 Python Web 框架:PyWebIO
- 相关性分析——Pearson相关系数+热力图(附data和Python完整代码)
- Python与PyTorch的版本对应
- Anaconda版本和Python版本对应关系(持续更新...)
- Python pyinstaller打包exe最完整教程
- Could not build wheels for llama-cpp-python, which is required to install pyproject.toml-based proj