695张辣椒缺陷数据集:VOC转YOLO与YOLOv8训练实战指南
发布时间:2026/9/28 23:03:42 作者:尧图编辑部 阅读量:1,286

简介这份辣椒缺陷检测数据集面向计算机视觉目标检测任务包含约700张辣椒图像的VOC与YOLO双格式标注覆盖Defect、Fly-bites、Grade-A、Grade-B、striped五个类别每张图像均为单个辣椒便于聚焦局部缺陷特征。资源包共2000个文件主要由jpg图像、xml标注文件与txt标注文件组成压缩后仅11.74MB轻量易下载。标注由labelImg生成xml与txt内容一一对应可直接用于YOLO、Faster R-CNN等模型的训练与验证总计1219个标注框中Fly-bites类488框、Grade-A类237框、Grade-B类243框、Defect类148框、striped类103框类别数量差异较大适合研究类别不平衡或小样本学习场景。目前已有253人学习下载对需要快速获取带精确框标注的农产品外观检测数据集的开发者具有较高实用价值。1. 为什么拿695张辣椒图做缺陷检测还要VOC和YOLO格式各留一份做农业视觉的人都有同感缺陷检测的模型结构不是瓶颈数据才是。辣椒表面缺陷种类多、个体差异大自己下地拍照再标注一个类别凑满几百张就要折腾几周。如果手头这份辣椒缺陷检测数据集正好是695张、5个类别、VOC和YOLO两种格式都齐了那省下的不只是标注时间还有格式转换的坑。VOC格式保留了XML里的原始框坐标适合做数据清洗和分析YOLO格式的txt标注开箱即用配合ultralytics的训练脚本下午解压、晚上就能出第一版指标。这份数据量不算大但足够验证一个检测方案跑不跑得通也适合做迁移学习的起点。文章下面按实际使用顺序展开解压、读格式、转格式、训练、审数据、避坑最后聊怎么把695张的价值榨干。2. 从7z压缩包到可训练的YOLO数据集解压与目录结构2.1 解压7z文件Linux与Windows两条命令拿到手是一个.7z压缩包第一步就是解压。常见做法是Linux服务器上解压训练Windows本机解压做标注浏览两条路命令不太一样。Linux下先装p7zip# Debian / Ubuntu sudo apt-get install -y p7zip-full # CentOS / RHEL sudo yum install -y p7zip p7zip-plugins # 解压到当前目录 7z x 辣椒缺陷检测数据集VOCYOLO格式695张5类别.7z -o./pepper_defect # 如果只是查看内容列表不完整解压 7z l 辣椒缺陷检测数据集VOCYOLO格式695张5类别.7z7z x会保留压缩包内的完整目录结构-o后面指定输出目录注意不能有空格。Windows下建议直接用7-Zip图形界面右键解压命令行版则是7z.exe x 文件名.7z。装完先跑一次7z l确认内部目录名避免解压出来一堆文件散落在当前目录。见过不少人在这一步就翻车解压后文件夹嵌套三层训练脚本写错路径第一轮跑的其实是空数据集。2.2 读懂VOC格式的JPEGImages、Annotations和标签映射VOC格式长什么样老手都清楚新接触的同学记住这个结构就行pepper_defect/ ├── VOCdevkit/ │ └── VOC2007/ │ ├── JPEGImages/ # 695张JPG原图 │ ├── Annotations/ # 695个XML标注文件与图片同名 │ ├── ImageSets/ │ │ └── Main/ # train.txt / val.txt 划分列表 │ └── labels/ # 可能预生成的YOLO格式txt └── data.yaml # YOLO训练用的类别配置文件Annotations里的XML是关键每个文件对应一张图片记录文件名、图片尺寸、每个目标的类别和边界框。核心字段是object下的name和bndboxxmin/ymin/xmax/ymax这四个值是像素绝对值。数据集的5个类别具体是什么以XML里name字段或data.yaml里的names列表为准不同来源的辣椒缺陷集定义不完全一样常见的有病斑、裂口、畸形、烂点、正常果这几类但不要凭经验猜动手数一遍最稳。2.3 VOC转YOLO坐标归一化脚本与边界处理YOLO格式的训练要求每张图一个txt文件每行是类别ID x_center y_center width height全部归一化到0到1。VOC的XML不能直接用需要转换。下面是标准转换脚本带注释按实际目录改三个前缀变量就能跑import xml.etree.ElementTree as ET import os voc_annot_dir VOCdevkit/VOC2007/Annotations # XML目录 yolo_label_dir VOCdevkit/VOC2007/labels # 输出的YOLO标签目录 class_list [class_a, class_b, class_c, class_d, class_e] # 以实际data.yaml为准 os.makedirs(yolo_label_dir, exist_okTrue) for xml_file in os.listdir(voc_annot_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_annot_dir, xml_file)) root tree.getroot() # 图片宽高必须从XML里读不能假设尺寸 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) if img_w 0 or img_h 0: print(f跳过 {xml_file}图片尺寸为0) continue txt_path os.path.join(yolo_label_dir, xml_file.replace(.xml, .txt)) with open(txt_path, w) as f: for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in class_list: print(f跳过未知类别 {cls_name} 在 {xml_file}) continue cls_id class_list.index(cls_name) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 计算中心点和宽高全部除以图片尺寸完成归一化 x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 越界框做裁剪避免训练时算损失出现负数坐标 x_center max(0, min(x_center, 1)) y_center max(0, min(y_center, 1)) width max(0, min(width, 1)) height max(0, min(height, 1)) f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) print(转换完成输出目录, yolo_label_dir)脚本逻辑分三步解析XML拿到图片尺寸和每个目标的类别与边界框把左上右下两点换成中心点加宽高最后除图片宽高转成0到1之间的比例。最关键的一行是尺寸读取如果图片是640x480而你在别处写死成img_w1920所有框位置都会错位且不容易发现。转换完随便挑三五个txt文件人工核对一下框的中心点坐标乘以图片宽度回算像素坐标应该和你原图里的目标位置对得上。数据集中如果已经带了labels目录这步可以跳过但建议仍然抽样验证一遍有些转换脚本会漏掉小目标或把畸形框写进去。3. 用YOLOv8训练辣椒缺陷模型最小可复现命令与参数3.1 划分训练集验证集按目录还是按列表695张对深度学习来说属于小样本划分方式直接影响结果可信度。常见做法是8:1:1或7:2:1也可以按VOC自带的ImageSets/Main里的train.txt和val.txt走。如果是第一次用这个数据集建议用脚本随机划分并固定随机种子保证后续对比实验可复现import random import os from collections import defaultdict image_dir VOCdevkit/VOC2007/JPEGImages label_dir VOCdevkit/VOC2007/labels images [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.seed(42) random.shuffle(images) train_ratio 0.8 val_ratio 0.1 train_cut int(len(images) * train_ratio) val_cut int(len(images) * (train_ratio val_ratio)) train_files images[:train_cut] val_files images[train_cut:val_cut] test_files images[val_cut:]划分完检查一下每个集合里5个类别是否都有分布特别是缺陷类别这类目标往往只占一小部分。如果某个缺陷类别只出现在训练集、验证集里没有那验证指标会虚高反过来训练集缺失某个类别模型直接不认识它。检验类别是否覆盖很简单读对应图片的txt文件统计第一列数字就行。验证集的分配比训练集更敏感农业数据里同一株辣椒的多个果往往长得很像随机划分可能把相似样本分到两边导致验证分数虚高。更严格的做法是按拍摄批次或植株划分但695张的规模下按批次划分会损失太多训练样本我一般先随机划分跑通后面做精调时再考虑批次维度。3.2 数据配置YAML与模型选型n、s还是mYOLO训练数据配置就是一个YAML文件把这个数据集的情况写清楚# data.yaml path: ./pepper_defect train: images/train val: images/val nc: 5 names: 0: class_a 1: class_b 2: class_c 3: class_d 4: class_epath是数据集根目录train和val指向图片目录ultralytics会自动在同级的labels目录找对应的txt。names顺序必须和转换脚本里的class_list一致这一步错了训练不报错但指标和结果图全错位模型把病斑当正常果你还不知道。模型选型方面695张小数据集推荐从YOLOv8n起步或者用YOLOv8s。n模型参数最少、跑得最快先验证数据质量s模型精度更高适合直接做最终方案。m以上在大数据集上优势明显但这数据量下容易过拟合验证集分数反而不如s。显卡是V100或A100的话可以开大batch但先别急着上大模型小数据集上先把训练流程跑通、确认loss下降曲线正常比追求模型容量更实际。3.3 训练命令参数epochs、imgsz与loss观察训练命令如下这是ultralytics的标准调用方式yolo detect train \ data./data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ project./runs \ namepepper_defect \ seed42参数拆开讲modelyolov8n.pt表示加载COCO预训练权重不是随机初始化这对小数据集非常重要迁移学习能把收敛速度和最终精度拉高一大截。epochs150在小数据集上偏充裕配合patience30验证集指标30轮不涨就自动停止省时间。imgsz640是输入分辨率辣椒缺陷属于小目标不要盲目降到320太小会把细小病斑直接抹掉显存不够时优先减batch而不是减分辨率。lr00.01是YOLOv8默认值小数据集可以降到0.005后面讲BN崩溃时会细说。训练开始后重点看两个实时输出box_loss和cls_loss应该在前10个epoch明显下降如果训练集loss一直在高位震荡而验证集指标不涨先怀疑数据标注质量不要急着改模型结构。训练结束后在runs/pepper_defect/weights/下会得到best.pt和last.pt后续推理和部署都用best.pt。4. 先把数据审一遍类别分布、标注质量和遮挡问题4.1 统计类别分布和边框尺寸找出严重不均衡695张数据听起来不多但很多时候真正有效的样本只有一部分。训练前先静态统计确认5个类别各自有多少目标、边框大小分布怎么样。下面的脚本读YOLO格式txt输出类别频次和边框宽高像素值分布import os label_dir VOCdevkit/VOC2007/labels class_count {} box_stats [] # (cls_id, width_px, height_px) for txt_file in os.listdir(label_dir): if not txt_file.endswith(.txt): continue with open(os.path.join(label_dir, txt_file)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f异常行: {txt_file} - {line}) continue cls_id int(parts[0]) w_norm float(parts[3]) h_norm float(parts[4]) class_count[cls_id] class_count.get(cls_id, 0) 1 # 这里以640作为参考宽度得到近似像素尺寸 box_stats.append((cls_id, w_norm * 640, h_norm * 640)) for cls_id in sorted(class_count): print(f类别 {cls_id}: {class_count[cls_id]} 个目标)统计结果会暴露几个典型问题某个类别只有几十个目标这类模型基本学不出来需要考虑数据增强或当作稀有类特殊对待框的平均宽度如果小于50像素说明缺陷以小目标为主训练时要开启增强里的近景裁剪推理时也要适当降低置信度阈值。还有一种情况是txt里出现了6列或4列数据说明有人手动编辑过标签直接修复对应文件不要带着脏数据训练。4.2 用OpenCV做像素级检查排除损坏图片和错误通道标注格式没问题不代表图片没问题。农业场景下图片来源杂手机、工业相机、无人机拍出来的都有容易出现灰度图、带Alpha通道的PNG、EXIF旋转导致的方向错乱。OpenCV快速筛一遍import cv2 import os image_dir VOCdevkit/VOC2007/JPEGImages for img_name in os.listdir(image_dir): path os.path.join(image_dir, img_name) img cv2.imread(path, cv2.IMREAD_COLOR) if img is None: print(f无法读取: {img_name}) # 文件损坏或格式伪装 continue h, w, c img.shape if c ! 3: print(f通道异常: {img_name}, channels{c}) # 全黑或全白图片标注没有意义 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if gray.min() gray.max(): print(f纯色图片: {img_name})这类检查脚本跑一遍只有几十秒但能避免训练到一半因为某张损坏图片导致loss爆炸。实际项目中遇到过jpg后缀但内容其实是BMP的文件Windows上预览正常OpenCV读取报错ultralytics训练直接中断。另外注意灰度图如果数据集中混进了灰度图c ! 3会拦下来转换时cv2.imread默认按BGR三通道读问题不大但如果有单通道标注过最好统一转成三通道存一遍。4.3 哪些样本该删、哪些该留影响模型上限的三个原则审数据的目标不是把所有脏数据删光而是决定哪些样本对训练有正向贡献。我一般按三个原则处理。第一标注框明显偏离目标的删比如框只框住了一半病斑或者把两片叶子背景也包进框里这类错标会让模型学到错误位置。第二严重遮挡且无法辨认目标的删一张辣椒被叶子挡住大半人眼都看不清缺陷在哪模型更学不会。第三同一目标重复标注的合并特别是密集场景下一个病斑被标了两个框训练时一个目标两个真值模型输出不知道该对齐哪个验证时还可能出现重复检测。小数据集经不起脏数据消耗删掉20张问题样本往往比增加20张有效样本更值。还有一种情况是类别边界模糊比如早期腐坏和正常色泽在视觉上很难区分这类样本不在删除范围但要单独记录推理阶段高频误检往往就集中在这条边界上后面做误检闭环时要用。5. 数据集落地避坑7z解压、转换和训练阶段的5个典型问题5.1 7z压缩文件密码是正确的但一直报错现象解压时输入密码7-Zip提示密码错误但密码明明是对的。原因大概率不是密码本身而是压缩包创建时加密了文件名列表或使用了较新的压缩特性而解压工具版本太旧——Linux下常见的p7zip 16.02不支持某些新算法变体Windows下老版本7-Zip同样会误报。解决先升级解压工具Linux用sudo apt-get install p7zip-full并确认版本在16.02以上Windows装7-Zip 22.01及以上如果还不行在命令行加-p密码参数而不是手动输入。文件名加密的情况7z l只显示文件列表摘要而不显示真实文件名解压命令本身不变但个别图形前端会表现异常命令行是更稳的选择。5.2 VOC转YOLO后框越界归一化除法和浮点精度现象训练时某些图片的gt框中心点在0.5附近但宽高超过1loss出现异常峰值。原因转换脚本里用了整数除法、或读XML时xmax和xmin顺序写反也可能是某些目标本身就贴着图片边缘标注时框略微出界。解决在转换脚本里加上越界裁剪前文代码已包含并回算校验——把归一化坐标乘以图片原始宽高确认得到的像素坐标和原XML里的bndbox误差不超过2个像素。还有一种隐蔽情况是某些工具生成的XML里坐标是浮点数而非整数转换脚本如果用int()截断小数部分丢失会导致几个像素的偏移直接保留float就行。这里不要只做一个方向的裁剪四个坐标都要处理。5.3 类别ID错位txt标签和data.yaml对不上现象训练正常、指标正常但验证集的可视化结果里病斑被标成正常果类别标签张冠李戴。原因VOC转YOLO时的class_list顺序和data.yaml里的names顺序不一致。YOLO的txt只存数字ID不存类别名模型输出时按配置文件里的映射还原名称顺序一错全错。解决转换时把class_list直接硬编码成和data.yaml一致转换脚本里加一行校验读一个txt文件打印所有类别ID和data.yaml逐项核对。另外一个容易被忽略的点如果data.yaml里names定义了5类但某个txt里出现了class_id5ultralytics不会报错而是直接忽略这条标注造成目标漏检检查手段是统计所有txt里的最大ID是否等于nc-1。5.4 loss变成NaN学习率过高和BN崩溃现象训练到第20个epochloss突然变成nan后面所有指标归零。原因学习率过高导致梯度爆炸或数据里有全黑图片、空标注文件使BatchNorm计算方差为0出现除零。解决办法分两步先检查数据遍历所有txt文件找到0字节文件删掉对应图片再用脚本检查图片里是否存在全黑或纯色区域。数据没问题就降学习率lr00.005不行就降到0.001同时把batch调小减小单次batch内的方差波动。值得提的是amp混合精度训练在某些老显卡上也会触发nanultralytics里设置ampFalse直接关闭小数据集上训练时间增加有限但稳定性大幅提升。5.5 混淆矩阵总合不唯一理解背景类与验证集划分现象训练完看验证集的混淆矩阵各行列加起来对不上100%怀疑是bug。原因YOLO的混淆矩阵每一行代表真实标签的样本数每一列代表预测结果矩阵内数字来源于逐张图片的目标级匹配既要处理漏检预测不到任何一个框也要处理背景误检预测了框但真实没有任何目标。行归一化和列归一化之后总和自然不会一致。这里不需要修任何代码要看的是对角线的占比——如果某个类别对角线明显偏小且相邻列数值偏高说明模型把该类和其他类别混淆常见的改进方向是对该类做增强而不是调loss权重。另一个观察角度是我们是在验证集上算矩阵如果验证集本身包含训练时没见过的拍摄环境矩阵数值会整体偏低但这不代表模型差。6. 用695张把模型做到能用的进阶迁移学习与误检闭环小数据集训练的最大风险是模型记住了照片而不是缺陷。一个我常用的验证手段是把训练集里某一张辣椒图的背景换掉或加严重的椒盐噪声看模型是否还能检出缺陷。如果检出框消失说明模型在靠背景纹理做判断需要加强数据增强。先做迁移学习的精细调整加载COCO预训练权重yolov8n.pt前50个epoch冻结backbone用小学习率只训练检测头后面解冻全部层改用lr00.001微调。冻结方式在ultralytics里不直接支持但可以在训练开始时把backbone参数requires_gradFalse50个epoch后再手动解冻继续训练。这是一个笨办法但对小数据集效果明显能压制过拟合。再针对混淆严重的缺陷类别做专项增强把该类所有样本抠出来做随机旋转、亮度扰动、高斯模糊扩到200个实例再合并回训练集。注意增强后的样本不要进验证集否则指标虚高。推理阶段用一个200张的独立测试集做误检分析统计每张图的高置信度误报按误报类型分组环境误检土壤、水滴被当成病斑和类别混淆早期病斑被归成正常果。环境误检靠加大背景类负样本解决类别混淆靠调整conf阈值。有一个习惯值得坚持每次调完阈值导出20张最难样本的推理结果图肉眼过一遍再决定下一步而不是只看mAP。数据只有695张单靠加数据永远追不上大数据集的效果但把预训练权重、迁移学习、误检分析这三步做好满足产线上的粗糙分类需求没有问题。我的教训是别一开始追高精度先把稳定检出率跑上来再逐步收紧阈值。希望这些记录对你有帮助训练顺利。本文还有配套的精品资源点击获取