目标检测任务常用脚本1——将YOLO格式的数据集转换成VOC格式的数据集

在目标检测任务中,不同框架使用的标注格式各不相同。常见的框架中,YOLO 使用 .txt 文件进行标注,而 PASCAL VOC 则使用 .xml 文件。如果你需要将一个 YOLO 格式的数据集转换为 VOC 格式以便适配其他模型,本文提供了一个结构清晰、可维护性强的 Python 脚本。

🧩 输入输出目录结构

✅ 输入目录结构(YOLO 格式)

<YOLO数据集名称>
├── train/
│   ├── images/
│   │   ├── img_000001.bmp
│   │   └── ...
│   └── labels/
│       ├── img_000001.txt
│       └── ...
└── val/├── images/│   ├── img_000100.bmp│   └── ...└── labels/├── img_000100.txt└── ...

✅ 输出目录结构(VOC 格式)

<VOC格式数据集名称>
├── JPEGImages/      # 转换后的图像文件(.jpg)
├── Annotations/     # 对应的XML标注文件
└── ImageSets/└── Main/├── train.txt└── val.txt

🛠️ 配置参数说明

YOLO_DATASET_ROOT = ''            # YOLO格式数据集根目录(输入)
VOC_OUTPUT_DIR = ''               # VOC格式输出目录(输出)
CLASS_NAMES = []                  # 类别名称列表,示例:['person', 'car', 'dog']
SPLITS = ['train', 'val']         # 数据集划分类型(训练集、验证集等)
VERBOSE = True                    # 是否输出详细日志

⚠️ 注意:你需要根据自己的项目路径和类别信息填写 YOLO_DATASET_ROOTVOC_OUTPUT_DIRCLASS_NAMES

目前脚本默认处理 .bmp 图像并将其转为 .jpg,你可以根据需求修改扩展名以支持 .png、.jpeg 等格式。
完整代码如下:

import os
import xml.etree.ElementTree as ET
from xml.dom import minidom
import cv2# -----------------------------
# 超参数配置(Hyperparameters)
# -----------------------------
YOLO_DATASET_ROOT = ''            # YOLO格式数据集根目录(输入)
VOC_OUTPUT_DIR = ''               # VOC格式输出目录(输出)
CLASS_NAMES = []                  # 类别名称列表,示例:['person', 'car', 'dog']
SPLITS = ['train', 'val']         # 数据集划分类型(训练集、验证集等)
VERBOSE = True                    # 是否输出详细日志def create_voc_annotation(image_path, label_path, annotations_output_dir):"""根据图像和YOLO标签生成PASCAL VOC格式的XML标注文件。"""image = cv2.imread(image_path)height, width, depth = image.shapeannotation = ET.Element('annotation')# .bmp -> .jpgfilename = os.path.basename(image_path).replace('.bmp', '.jpg')ET.SubElement(annotation, 'folder').text = 'JPEGImages'ET.SubElement(annotation, 'filename').text = filenameET.SubElement(annotation, 'path').text = os.path.join(VOC_OUTPUT_DIR, 'JPEGImages', filename)source = ET.SubElement(annotation, 'source')ET.SubElement(source, 'database').text = 'Custom Dataset'size = ET.SubElement(annotation, 'size')ET.SubElement(size, 'width').text = str(width)ET.SubElement(size, 'height').text = str(height)ET.SubElement(size, 'depth').text = str(depth)ET.SubElement(annotation, 'segmented').text = '0'if os.path.exists(label_path):with open(label_path, 'r') as f:for line in f.readlines():data = line.strip().split()class_id = int(data[0])x_center = float(data[1]) * widthy_center = float(data[2]) * heightbbox_width = float(data[3]) * widthbbox_height = float(data[4]) * heightxmin = int(x_center - bbox_width / 2)ymin = int(y_center - bbox_height / 2)xmax = int(x_center + bbox_width / 2)ymax = int(y_center + bbox_height / 2)obj = ET.SubElement(annotation, 'object')ET.SubElement(obj, 'name').text = CLASS_NAMES[class_id]ET.SubElement(obj, 'pose').text = 'Unspecified'ET.SubElement(obj, 'truncated').text = '0'ET.SubElement(obj, 'difficult').text = '0'bndbox = ET.SubElement(obj, 'bndbox')ET.SubElement(bndbox, 'xmin').text = str(xmin)ET.SubElement(bndbox, 'ymin').text = str(ymin)ET.SubElement(bndbox, 'xmax').text = str(xmax)ET.SubElement(bndbox, 'ymax').text = str(ymax)# 保存XML文件xml_str = minidom.parseString(ET.tostring(annotation)).toprettyxml(indent="   ")xml_filename = filename.replace('.jpg', '.xml')xml_path = os.path.join(annotations_output_dir, xml_filename)  # 确保这里只有一层Annotations目录with open(xml_path, "w") as f:f.write(xml_str)if VERBOSE:print(f"✅ 已生成标注文件: {xml_filename}")def convert_dataset(input_dir, output_dir):"""将YOLO格式的数据集转换为VOC格式。包括图像格式转换(.bmp -> .jpg)、生成XML标注文件,并创建ImageSets/Main/train.txt/val.txt。"""print("🔄 开始转换YOLO格式数据集到VOC格式...")if not os.path.exists(output_dir):os.makedirs(output_dir)for split in SPLITS:images_dir = os.path.join(input_dir, split, 'images')labels_dir = os.path.join(input_dir, split, 'labels')output_images_dir = os.path.join(output_dir, 'JPEGImages')output_annotations_dir = os.path.join(output_dir, 'Annotations')output_imagesets_dir = os.path.join(output_dir, 'ImageSets', 'Main')os.makedirs(output_images_dir, exist_ok=True)os.makedirs(output_annotations_dir, exist_ok=True)os.makedirs(output_imagesets_dir, exist_ok=True)set_file_path = os.path.join(output_imagesets_dir, f"{split}.txt")set_file = open(set_file_path, 'w')count = 0for filename in os.listdir(images_dir):if filename.endswith('.bmp'):image_path = os.path.join(images_dir, filename)label_path = os.path.join(labels_dir, filename.replace('.bmp', '.txt'))# 图像转换new_image_name = filename.replace('.bmp', '.jpg')new_image_path = os.path.join(output_images_dir, new_image_name)image = cv2.imread(image_path)cv2.imwrite(new_image_path, image)# 写入ImageSets/Main/train.txt或val.txtbase_name = new_image_name.replace('.jpg', '')set_file.write(f"{base_name}\n")# 生成XML标注文件create_voc_annotation(new_image_path, label_path, output_annotations_dir)  # 确保传入的是Annotations目录路径count += 1if VERBOSE and count % 10 == 0:print(f"🖼️ 已处理 {count} 张图片...")set_file.close()print(f"✅ 完成 [{split}] 分割集处理,共处理 {count} 张图片")print("🎉 数据集转换完成!")if __name__ == "__main__":convert_dataset(YOLO_DATASET_ROOT, VOC_OUTPUT_DIR)

转换后效果:
在这里插入图片描述
验证生成的VOC数据集中图片质量和数量是否合适可以用下面的脚本:

import os
import cv2
from xml.etree import ElementTree as ET# -----------------------------
# 超参数配置(Hyperparameters)
# -----------------------------
DATASET_ROOT = ''  # VOC格式数据集根目录
CLASS_NAMES = []  # 类别列表, 示例: ['car', 'person', 'dog']
VERBOSE = True  # 是否输出详细日志def count_images_in_set(imagesets_dir, set_name):"""统计ImageSets/Main目录下指定集合(train/val)的图片数量。"""set_file_path = os.path.join(imagesets_dir, f"{set_name}.txt")if not os.path.exists(set_file_path):print(f"[警告] 找不到 {set_name}.txt 文件,请确认是否生成正确划分文件。")return 0with open(set_file_path, 'r') as f:lines = [line.strip() for line in f.readlines() if line.strip()]return len(lines)def check_images(jpeg_dir):"""检查JPEGImages目录下的所有图片是否都能正常加载。"""print("[检查] 验证图像是否可读...")error_images = []for filename in os.listdir(jpeg_dir):if filename.lower().endswith(('.jpg', '.jpeg', '.png')):image_path = os.path.join(jpeg_dir, filename)try:img = cv2.imread(image_path)if img is None:raise ValueError("无法加载图像")except Exception as e:error_images.append(filename)if VERBOSE:print(f"  ❌ 图像加载失败: {filename} | 原因: {str(e)}")return error_imagesdef validate_annotations(annotations_dir, jpeg_dir):"""验证Annotations目录下的XML标注文件是否与对应的图片匹配。"""print("[检查] 验证XML标注文件是否有效...")error_annotations = []for filename in os.listdir(annotations_dir):if filename.endswith('.xml'):xml_path = os.path.join(annotations_dir, filename)try:tree = ET.parse(xml_path)root = tree.getroot()jpg_filename = root.find('filename').textif not os.path.exists(os.path.join(jpeg_dir, jpg_filename)):raise FileNotFoundError(f"找不到对应的图像:{jpg_filename}")except Exception as e:error_annotations.append(filename)if VERBOSE:print(f"  ❌ 标注文件异常: {filename} | 原因: {str(e)}")return error_annotationsdef verify_imagesets(imagesets_dir, jpeg_dir):"""确保ImageSets/Main中列出的所有图像都存在于JPEGImages中。"""print("[检查] 验证ImageSets/Main中列出的图像是否存在...")missing_files = []for set_name in ['train', 'val']:set_file_path = os.path.join(imagesets_dir, f"{set_name}.txt")if not os.path.exists(set_file_path):continuewith open(set_file_path, 'r') as f:for line in f:img_id = line.strip()if not img_id:continueimg_path = os.path.join(jpeg_dir, f"{img_id}.jpg")if not os.path.exists(img_path):missing_files.append(f"{img_id}.jpg")if VERBOSE:print(f"  ❌ 图像缺失: {img_id}.jpg")return missing_filesdef main():print("🔍 开始验证VOC格式数据集...\n")# 构建路径jpeg_dir = os.path.join(DATASET_ROOT, 'JPEGImages')annotations_dir = os.path.join(DATASET_ROOT, 'Annotations')imagesets_dir = os.path.join(DATASET_ROOT, 'ImageSets', 'Main')# 检查是否存在必要目录for dir_path in [jpeg_dir, annotations_dir, imagesets_dir]:if not os.path.exists(dir_path):print(f"[错误] 必要目录不存在: {dir_path}")exit(1)# 1. 检查图像是否可读error_images = check_images(jpeg_dir)if error_images:print(f"⚠️ 共发现 {len(error_images)} 张图片加载失败:")for img in error_images:print(f"   - {img}")else:print("✅ 所有图像均可正常加载。\n")# 2. 检查XML标注文件是否有效error_annotations = validate_annotations(annotations_dir, jpeg_dir)if error_annotations:print(f"⚠️ 共发现 {len(error_annotations)} 个无效或不匹配的XML标注文件:")for ann in error_annotations:print(f"   - {ann}")else:print("✅ 所有XML标注文件均有效且与对应图像匹配。\n")# 3. 检查ImageSets/Main中引用的图像是否存在missing_files = verify_imagesets(imagesets_dir, jpeg_dir)if missing_files:print(f"⚠️ 共发现 {len(missing_files)} 张图像在ImageSets中被引用但实际不存在:")for img in missing_files:print(f"   - {img}")else:print("✅ ImageSets/Main中引用的所有图像均存在。\n")# 4. 输出训练集和验证集的图像数量train_count = count_images_in_set(imagesets_dir, 'train')val_count = count_images_in_set(imagesets_dir, 'val')total_count = train_count + val_countprint("📊 数据集统计:")print(f"   - 训练集: {train_count} 张")print(f"   - 验证集: {val_count} 张")print(f"   - 总数: {total_count} 张\n")print("🎉 验证完成!")if __name__ == "__main__":main()

验证效果为:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python作业练习2

任务简述 if_name__main_的含义&#xff0c;why? 问题解答 在Python中&#xff0c;if __name__ __main__:是一种常见的惯用法&#xff0c;用于检查当前模块是否是主程序入口点。要理解其含义和用途&#xff0c;首先需要了解两个概念&#xff1a; 1. __name__: 这是一个特…

ppy/osu构建

下载 .NET (Linux、macOS 和 Windows) | .NET dotnet还行 构建&#xff1a;f5 运行&#xff1a;dotnet run --project osu.Desktop -c Debug

NY182NY183美光固态颗粒NY186NY188

NY182NY183美光固态颗粒NY186NY188 在存储技术的竞技场上&#xff0c;美光科技&#xff08;Micron&#xff09;始终扮演着革新者的角色。其NY系列固态颗粒凭借前沿的3D NAND架构和精准的工艺控制&#xff0c;成为企业级存储和数据中心的关键支柱。本文将围绕NY182、NY183、NY1…

C++的历史与发展

目录 一、C 的诞生与早期发展 &#xff08;一&#xff09;C 语言的兴起与局限 &#xff08;二&#xff09;C 的雏形&#xff1a;C with Classes &#xff08;三&#xff09;C 命名与早期特性丰富 二、C 的主要发展历程 &#xff08;一&#xff09;1985 年&#xff1a;经典…

DedeCMS-Develop-5.8.1.13-referer命令注入研究分析 CVE-2024-0002

本次文章给大家带来代码审计漏洞挖掘的思路&#xff0c;从已知可控变量出发或从函数功能可能照成的隐患出发&#xff0c;追踪参数调用及过滤。最终完成代码的隐患漏洞利用过程。 代码审计挖掘思路 首先flink.php文件的代码执行逻辑&#xff0c;可以使用php的调试功能辅助审计 …

计算机网络|| 常用网络命令的作用及工作原理

1.hostname 作用&#xff1a;显示计算机的完整计算机名的主机名部分。仅当 Internet 协议 (TCP/IP) 协议作为组件安装在网络的网络适配器的属性中时&#xff0c;此命令才可用。 2.ping 作用&#xff1a; 1.用来检测网络的连通情况和分析网络速度 2.根据域名得到服务器 IP …

用户态到内核态:Linux信号传递的九重门(二)

1. 保存信号 1.1. 信号其他相关常见概念 实际执⾏信号的处理动作称为信号递达(Delivery)。 信号从产⽣到递达之间的状态,称为信号未决(Pending)。 进程可以选择阻塞 (Block )某个信号。 被阻塞的信号产⽣时将保持在未决状态,直到进程解除对此信号的阻塞,才执⾏递达的动作。 1.…

tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/这个代码的解释

tar -zxvf jdk-8u212-linux-x64.tar.gz -C /opt/module/ 这条命令的解释如下&#xff1a; 1. tar&#xff1a;这是 Linux 系统中用于归档和压缩文件的命令行工具。 2. -z&#xff1a;表示通过 gzip 压缩格式来处理文件&#xff0c;因为文件 jdk-8u212-linux-x64.tar.gz 是一个经…

SysAid On-Prem XML注入漏洞复现(CVE-2025-2776)

免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 前…

Nginx的增强与可视化!OpenResty Manager - 现代化UI+高性能反向代理+安全防护

以下是对OpenResty Manager的简要介绍&#xff1a; OpenResty Manager &#xff08;Nginx 增强版&#xff09;&#xff0c;是一款容易使用、功能强大且美观的反向代理工具 &#xff0c;可以作为OpenResty Edge 的开源替代品基于 OpenResty 开发&#xff0c;支持并继承 OpenRes…

旅游推荐数据分析可视化系统——讯飞AI助手(超级v2版本)+论文+数据+源码

旅游推荐数据分析可视化系统——讯飞AI助手(超级v2版本)论文数据源码 项目介绍 本项目是一个基于Django框架开发的旅游推荐数据分析可视化系统&#xff0c;集成了讯飞AI大模型助手功能。系统通过对去哪儿网的旅游数据进行采集、分析和可视化&#xff0c;为用户提供个性化的旅…

大疆无人机(全系列,包括mini)拉流至电脑,实现直播

参考视频 【保姆级教程】大疆无人机rtmp推流直播教程_哔哩哔哩_bilibili VLC使用教程&#xff1a; VLC工具使用指南-CSDN博客 目录 实现效果&#xff1a; 电脑端 ​编辑 ​编辑 无人机端 VLC拉流 分析 实现效果&#xff1a; (实验机型&#xff1a;大疆mini4kRC-N2遥控器、大…

windows系统使用phpstudy安装ssl证书

一、证书准备与上传 获取证书文件‌ 免费证书&#xff08;如阿里云、Lets Encrypt&#xff09;&#xff1a;下载包含.crt&#xff08;证书&#xff09;、.key&#xff08;私钥&#xff09;、chain.crt&#xff08;证书链&#xff09;的文件包 自签名证书&#xff08;测试用&a…

Spring Validation中9个数据校验工具

Spring Validation作为Spring生态系统的重要组成部分&#xff0c;提供了一套强大而灵活的数据校验机制。 1. Bean Validation基础注解 Spring Validation集成了JSR-380 (Bean Validation 2.0)规范&#xff0c;提供了一系列开箱即用的校验注解。 常用注解示例 Data public c…

AI 搜索引擎 MindSearch

背景 RAG是一种利用文档减少大模型的幻觉&#xff0c;AI搜索也是 AI 搜索引擎 MindSearch 是一个开源的 AI 搜索引擎框架&#xff0c;具有与 Perplexity.ai Pro 相同的性能。您可以轻松部署它来构建您自己的搜索引擎&#xff0c;可以使用闭源 LLM&#xff08;如 GPT、Claude…

Java高频面试之并发编程-16

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;volatile 实现原理是什么&#xff1f; volatile 关键字的实现原理 volatile 是 Java 中用于解决多线程环境下变量可见性…

《零基础学机器学习》学习大纲

《零基础学机器学习》学习大纲 《零基础学机器学习》采用对话体的形式&#xff0c;通过人物对话和故事讲解机器学习知识&#xff0c;使内容生动有趣、通俗易懂&#xff0c;降低了学习门槛&#xff0c;豆瓣高分9.1分&#xff0c;作者权威。 接下来的数篇文章&#xff0c;我将用…

C# 中 static的使用

静态(static)是C#中一个重要的关键字&#xff0c;它可以应用于类、方法、属性和字段。 静态类 静态类的特点&#xff1a; 不能实例化只能包含静态成员密封的&#xff08;sealed&#xff09;,不能被继承 应用场景&#xff1a; 工具类/辅助类数学计算类&#xff1a;如Math类…

C++蓝桥杯真题(题目+解析+流程图)(特殊运算符+四叶玫瑰数+质因数的个数+最大的矩形纸片+数字游戏+活动人数)

C++蓝桥杯真题 蓝桥杯省赛C++题目分析1. 特殊运算符题目描述输入描述输出描述输入输出样例正确代码错误代码分析流程图2. 四叶玫瑰数题目描述输入描述输出描述输入输出样例正确代码错误代码分析流程图3. 质因数的个数题目描述输入描述输出描述输入输出样例正确代码错误代码分析…

MYSQL 索引与数据结构笔记

MYSQL 索引与数据结构笔记 文章目录 MYSQL 索引与数据结构笔记1. B-Tree 与 B Tree 基础对比一、B 树的优势二、B 树的进一步优化三、综合对比结论 2. MySQL 为何选择 B Tree3. 索引使用示例与性能分析3.1 整数字段索引查询3.2 字符字段索引查询 4. 索引失效与类型转换陷阱5. 小…