2026/1/10 10:43:22
网站建设
项目流程
婚庆公司网站,六安论坛网,网站怎么快速收录,珠宝网站制作的理念划分基准推荐度优点缺点/风险适用场景JPEGImages#xff08;图片#xff09;★★★★★源头操作#xff0c;保证每张有效样本必有图#xff1b;通过相同 ID 加载标注天然同步#xff1b;无图无标注可立即发现并清理#xff1b;主流框架均以图片列表为准需额外检查标注文件…划分基准推荐度优点缺点/风险适用场景JPEGImages图片★★★★★源头操作保证每张有效样本必有图通过相同 ID 加载标注天然同步无图无标注可立即发现并清理主流框架均以图片列表为准需额外检查标注文件是否存在但本身利于发现脏数据通用首选尤其适合自定义数据集、工业项目、可能存在脏数据的场景Annotations原始 XML★★★☆☆传统 VOC 官方做法老代码常见若数据 100% 干净可直接使用若存在“只有 XML 无图”或“只有图无 XML”会导致加载崩溃易掩盖数据问题仅确信数据集严格 1:1 且干净时使用如官方 VOC2007/20121.Pascal VOC 格式数据集的目录做目标检测时只需 Annotations、ImageSets/Main、JPEGImages 三个目录SegmentationClass 与 SegmentationObject 仅在分割任务中使用。当然train.txt、val.txt也可以自定义路径不按以下存放。所有文件名不含扩展名一一对应方便按行读取 ImageSets 里的列表后快速定位图片与标注。VOCdevkit #举例 └── VOC2012 ├── Annotations # 每张图片对应的 XML 标注文件 │ ├── 2007_000001.xml │ ├── 2007_000002.xml │ └── … ├── ImageSets # 训练/验证/测试集的切分列表 │ ├── Action # 动作识别任务列表可选 │ ├── Layout # 人体部位布局列表可选 │ ├── Main # 目标检测/分类任务列表 │ │ ├── train.txt │ │ ├── val.txt │ │ └── trainval.txt │ └── Segmentation # 分割任务列表 │ ├── train.txt │ ├── val.txt │ └── trainval.txt ├── JPEGImages # 原始 JPG 图片 │ ├── 2007_000001.jpg │ ├── 2007_000002.jpg │ └── … ├── SegmentationClass # 语义分割标签图类别级 PNG │ ├── 2007_000001.png │ ├── 2007_000002.png │ └── … └── SegmentationObject # 实例分割标签图实例级 PNG ├── 2007_000001.png ├── 2007_000002.png └── …2.基于 JPEGImages图片文件夹进行划分Pascal VOC格式的数据集时划分训练集train和验证集val的标准做法是对JPEGImages文件夹里的所有图片文件或等价的XML列表进行划分生成ImageSets/Main下的train.txt、val.txt、trainval.txt等文件。这里注意的是按JPEGImages文件夹生成train.txt、val.txt也会是同样的。标准、最安全的操作流程如下操作对象针对 JPEGImages/ 文件夹里的所有图片文件提取它们的纯文件名不包含.jpg扩展名形成一个ID列表。划分动作将这个ID列表随机打乱然后按比例如8:2划分为train_ids和val_ids。更多的训练过程也会有text.txt测试集标签。保存结果将这两个ID列表分别保存为 ImageSets/Main/train.txt 和 ImageSets/Main/val.txt。每个.txt文件的内容就是一行一个图片ID。早期 “经典 VOC 格式 传统 Faster R-CNN 实现”如 py-faster-rcnn、jwyang/faster-rcnn.pytorch场景那些代码确实默认读取 ImageSets/Main/*.txt而官方 VOC 数据集就是基于 XML 生成这些 txt 的。但在实际工程中尤其是自定义数据集图片缺失几乎不可能但标注缺失或损坏很常见标注漏了、文件损坏、路径错等。因此以图片为源头划分更安全、更鲁棒已成为现代最佳实践。一个ID统领全局train.txt/val.txt里的一个ID是你整个数据流水线的唯一钥匙用来打开对应的图片文件、标注文件和后续的缓存文件。与缓存机制协同VOCDataset类会使用img_id作为缓存的键。因此不同的划分train/val会自动生成不同的缓存文件互不干扰。对比基于两个文件夹不同3.数据加载逻辑-以图片ID为中心训练代码加载时读取 txt 中的 ID加载 JPEGImages/{ID}.jpg加载对应的标注文件Annotations/{ID}.xml 或 labels/{ID}.txt如果标注文件不存在报错或跳过便于你清理数据# 你的代码逻辑解读def__getitem__(self,idx):img_idself.ids[idx]# 1. 首先从 train.txt/val.txt 获取一个图片ID (如2007_000032)img_pathos.path.join(self.img_dir,img_id.jpg)# 2. 拼接出图片路径annot_pathos.path.join(self.annot_dir,img_id.txt)# 3. 拼接出标注文件路径# ... 然后加载图片和对应的标注4.根据模型/框架的具体建议模型 / 框架类型推荐划分依据理由经典 Faster R-CNNpy-faster-rcnn、jwyang 等老代码Annotations (XML)这些代码硬依赖 VOC 标准结构ImageSets/Main/*.txt 通常基于 XML 生成。现代 PyTorch 实现bubbliiiing、AIZOOTech 等国内仓库JPEGImages (图片)它们通常用自定义 voc_annotation.py你可以修改脚本让它遍历图片文件夹生成 train.txt。torchvision FasterRCNN、MMDetection、Detectron2JPEGImages (图片)官方都以图片路径列表或 COCO json 中的 images 字段为主。YOLO 系列Ultralytics YOLOv5/v8/v10JPEGImages (图片)data.yaml 中 train/val 指向图片文件夹自动查找同名 .txt 标注。自定义数据集强烈建议JPEGImages (图片)最安全、最灵活。5.数据集划分代码按8:1:1比例随机划分训练集、验证集、测试集#!/usr/bin/env python3# 带数据一致性检查功能获取所有图片的ID。为每个ID查找对应的标注文件即检查 Annotations_txt/2007_000032.txt 是否存在。报告缺失情况告诉你哪些图片没有标注帮你提前发现并修复数据问题。 数据集划分脚本按8:1:1比例随机划分训练集、验证集、测试集 保存为ImageSets/Main/train.txt, val.txt, test.txt importosimportrandomimportargparsefrompathlibimportPathfromtypingimportList,Tupledefsplit_dataset(data_root:str,jpeg_dir:strJPEGImages,output_dir:strImageSets/Main,train_ratio:float0.8,val_ratio:float0.1,test_ratio:float0.1,seed:int42,shuffle:boolTrue)-None: 随机划分数据集并保存划分结果 参数: data_root: VOC数据集根目录 jpeg_dir: 图片文件夹名称 output_dir: 输出文件夹名称 train_ratio: 训练集比例 val_ratio: 验证集比例 test_ratio: 测试集比例 seed: 随机种子确保可复现 shuffle: 是否打乱数据 # 验证比例总和为1total_ratiotrain_ratioval_ratiotest_ratioifabs(total_ratio-1.0)0.001:raiseValueError(f比例总和应为1.0当前为{total_ratio})# 设置随机种子random.seed(seed)# 构建路径jpeg_pathPath(data_root)/jpeg_dir output_pathPath(data_root)/output_dirprint(*60)print(数据集划分工具)print(*60)# 1. 获取所有图片文件ifnotjpeg_path.exists():raiseFileNotFoundError(f图片文件夹不存在:{jpeg_path})# 支持多种图片格式image_extensions{.jpg,.jpeg,.png,.bmp}image_files[]forextinimage_extensions:image_files.extend(jpeg_path.glob(f*{ext}))image_files.extend(jpeg_path.glob(f*{ext.upper()}))ifnotimage_files:raiseFileNotFoundError(f在{jpeg_path}中未找到任何图片文件)# 提取纯文件名不带扩展名image_ids[]forimg_fileinimage_files:# 保留原始文件名去除扩展名image_ids.append(img_file.stem)print(f找到{len(image_ids)}张图片)# 2. 去重并排序确保可复现image_idslist(set(image_ids))# 去重image_ids.sort()# 排序确保每次相同的顺序# 3. 打乱顺序ifshuffle:print(f使用随机种子{seed}打乱数据顺序...)random.shuffle(image_ids)# 4. 计算划分点total_countlen(image_ids)train_countint(total_count*train_ratio)val_countint(total_count*val_ratio)# 处理可能的舍入误差确保测试集包含所有剩余图片test_counttotal_count-train_count-val_count# 5. 执行划分train_idsimage_ids[:train_count]val_idsimage_ids[train_count:train_countval_count]test_idsimage_ids[train_countval_count:]print(\n划分结果统计:)print(-*40)print(f训练集:{len(train_ids)}张 ({len(train_ids)/total_count*100:.1f}%))print(f验证集:{len(val_ids)}张 ({len(val_ids)/total_count*100:.1f}%))print(f测试集:{len(test_ids)}张 ({len(test_ids)/total_count*100:.1f}%))print(f总计:{total_count}张)# 6. 创建输出目录output_path.mkdir(parentsTrue,exist_okTrue)# 7. 保存划分文件train_fileoutput_path/train.txtval_fileoutput_path/val.txttest_fileoutput_path/test.txtwithopen(train_file,w,encodingutf-8)asf:f.write(\n.join(train_ids))withopen(val_file,w,encodingutf-8)asf:f.write(\n.join(val_ids))withopen(test_file,w,encodingutf-8)asf:f.write(\n.join(test_ids))print(\n划分文件已保存:)print(f{train_file})print(f{val_file})print(f{test_file})# 8. 保存划分详情可选detail_fileoutput_path/split_details.txtwithopen(detail_file,w,encodingutf-8)asf:f.write(数据集划分详情\n)f.write(*40\n)f.write(f数据根目录:{data_root}\n)f.write(f随机种子:{seed}\n)f.write(f总图片数:{total_count}\n)f.write(f训练集:{len(train_ids)}({train_ratio*100:.1f}%)\n)f.write(f验证集:{len(val_ids)}({val_ratio*100:.1f}%)\n)f.write(f测试集:{len(test_ids)}({test_ratio*100:.1f}%)\n)f.write(\n划分比例: 训练集:验证集:测试集 )f.write(f{train_ratio}:{val_ratio}:{test_ratio}\n)print(f划分详情:{detail_file})print(*60)defcheck_annotation_compatibility(data_root:str,jpeg_dir:strJPEGImages,annot_dir:strAnnotations_txt)-None: 检查图片文件和标注文件的对应关系 参数: data_root: 数据集根目录 jpeg_dir: 图片文件夹 annot_dir: 标注文件夹 print(\n检查标注文件兼容性...)jpeg_pathPath(data_root)/jpeg_dir annot_pathPath(data_root)/annot_dir# 获取图片文件image_fileslist(jpeg_path.glob(*.jpg))image_ids[f.stemforfinimage_files]missing_annotations[]forimg_idinimage_ids:annot_fileannot_path/f{img_id}.txtifnotannot_file.exists():missing_annotations.append(img_id)ifmissing_annotations:print(f警告:{len(missing_annotations)}张图片缺少对应的标注文件)iflen(missing_annotations)10:print(缺失标注的图片ID:)forimg_idinmissing_annotations:print(f -{img_id})else:print(前10个缺失标注的图片ID:)forimg_idinmissing_annotations[:10]:print(f -{img_id})print(f ... 共{len(missing_annotations)}个)else:print(✓ 所有图片都有对应的标注文件)defmain():命令行入口函数parserargparse.ArgumentParser(description数据集划分工具 (8:1:1比例))parser.add_argument(--data_root,typestr,default./VOC2012,helpVOC数据集根目录 (默认: ./VOC2012))parser.add_argument(--jpeg_dir,typestr,defaultJPEGImages,help图片文件夹名称 (默认: JPEGImages))parser.add_argument(--output_dir,typestr,defaultImageSets/Main,help输出文件夹名称 (默认: ImageSets/Main))parser.add_argument(--train_ratio,typefloat,default0.8,help训练集比例 (默认: 0.8))parser.add_argument(--val_ratio,typefloat,default0.1,help验证集比例 (默认: 0.1))parser.add_argument(--test_ratio,typefloat,default0.1,help测试集比例 (默认: 0.1))parser.add_argument(--seed,typeint,default42,help随机种子 (默认: 42))parser.add_argument(--no_shuffle,actionstore_true,help不打乱数据顺序)parser.add_argument(--check_annotations,actionstore_true,help检查标注文件兼容性)argsparser.parse_args()try:# 执行划分split_dataset(data_rootargs.data_root,jpeg_dirargs.jpeg_dir,output_dirargs.output_dir,train_ratioargs.train_ratio,val_ratioargs.val_ratio,test_ratioargs.test_ratio,seedargs.seed,shufflenotargs.no_shuffle)# 如果需要检查标注文件ifargs.check_annotations:check_annotation_compatibility(data_rootargs.data_root,jpeg_dirargs.jpeg_dir,annot_dirAnnotations_txt# 你的标注文件夹)exceptExceptionase:print(f错误:{e})return1return0if__name____main__:exit(main())6.Annotations文件夹XML标注转换为TXT格式输出文件夹路径nnotations_txtimportosimportxml.etree.ElementTreeasET# VOC2012的20个类别按顺序对应class_id 1-20VOC_CLASSES[aeroplane,bicycle,bird,boat,bottle,bus,car,cat,chair,cow,diningtable,dog,horse,motorbike,person,pottedplant,sheep,sofa,train,tvmonitor]defxml_to_txt(xml_dir,txt_dir): 将VOC XML标注转换为TXT格式 :param xml_dir: XML标注文件所在目录如VOCdevkit/VOC2007/Annotations :param txt_dir: 输出TXT文件的保存目录 # 创建输出目录如果不存在os.makedirs(txt_dir,exist_okTrue)# 遍历所有XML文件forxml_filenameinos.listdir(xml_dir):ifnotxml_filename.endswith(.xml):continue# 只处理.xml文件# 解析XMLxml_pathos.path.join(xml_dir,xml_filename)treeET.parse(xml_path)roottree.getroot()# 获取图像尺寸可选用于验证坐标是否合理sizeroot.find(size)widthint(size.find(width).text)heightint(size.find(height).text)# 提取所有目标的标注txt_content[]forobjinroot.iter(object):# 获取类别名称cls_nameobj.find(name).text.strip().lower()ifcls_namenotinVOC_CLASSES:continue# 跳过不在VOC类别中的目标# 转换类别名称为class_id1-20cls_idVOC_CLASSES.index(cls_name)1# 索引1确保从1开始# 获取边界框坐标xmin, ymin, xmax, ymaxbboxobj.find(bndbox)x1float(bbox.find(xmin).text)y1float(bbox.find(ymin).text)x2float(bbox.find(xmax).text)y2float(bbox.find(ymax).text)# 确保坐标在图像范围内可选防止越界x1max(0,min(x1,width))y1max(0,min(y1,height))x2max(x1,min(x2,width))y2max(y1,min(y2,height))# 写入TXT内容格式x1 y1 x2 y2 class_idtxt_content.append(f{x1}{y1}{x2}{y2}{cls_id})# 保存为TXT文件与XML同名后缀改为.txttxt_filenamexml_filename.replace(.xml,.txt)txt_pathos.path.join(txt_dir,txt_filename)withopen(txt_path,w)asf:f.write(\n.join(txt_content))# 打印进度if(len(os.listdir(txt_dir))%1000):print(f已转换{len(os.listdir(txt_dir))}个文件...)print(f转换完成共处理{len(os.listdir(txt_dir))}个XML文件保存至{txt_dir})# -------------------------- 运行转换脚本 --------------------------if__name____main__:# 请替换为你的实际路径xml_dir./VOC2012/Annotations# VOC原始XML标注目录txt_dir./VOC2012/Annotations_txt# 输出TXT目录需与RCNN代码中的annot_dir一致# 执行转换xml_to_txt(xml_dir,txt_dir)