目標檢測任務常用腳本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.pswp.cn/diannao/82816.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82816.shtml
英文地址,請注明出處:http://en.pswp.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. 小…