☰
鸡蛋缺陷检测数据集:VOC/YOLO双格式目标检测训练实战
2026/10/1 4:46:10 网站建设 项目流程

简介:这是一份面向鸡蛋表面缺陷检测的视觉数据集,采用VOC与YOLO两种主流标注格式,适合计算机视觉学习者、工业质检算法工程师以及相关科研人员用于训练裂缝检测模型。压缩包共包含2000个文件,主要以Pascal VOC格式的xml标注文件为核心,同时提供YOLO格式的txt标注与配套说明文件,整体大小仅约64.39MB,体量轻巧,便于下载和本地处理。数据集覆盖2077张鸡蛋图像,标注类别分为正常鸡蛋与裂缝鸡蛋两类,总标注框数为2755个,其中裂缝类别拥有368个标注框,样本分布真实,可直接用于YOLO系列、Faster R-CNN等主流目标检测框架的训练与验证。标注工作经由labelImg工具完成,VOC和YOLO两种格式同时交付,免去自行转换的麻烦;附带说明文件还给出了类别名称与框数统计,方便使用者快速理解数据构成并评估检测任务难度。目前已有274人学习下载,可作为鸡蛋品质检测课题的基准数据或生产环境中的预训练来源。

1. 鸡蛋缺陷检测数据集:2077张、双格式、2类别的数据集,拿来就能训

做质检类视觉项目的人最头疼的一件事,不是模型选型,而是数据格式对不上。VOC标注好了,YOLO训练要txt,Faster R-CNN要VOC的xml,换来换去一不小心就丢框。这份鸡蛋缺陷检测数据集直接给了Pascal VOC和YOLO两种格式,2077张jpg、2077个xml、2077个txt一一对应,类别只有egg和egg-crack两类,总共2755个框。它的价值很直接:不用做格式转换,拿过去就能开始调训练,省掉的是数据准备阶段最容易翻车的环节。适合正在做蛋品检测、禽类分拣或者刚入门目标检测想找一份干净数据集的工程师。下面我从文件结构、解析逻辑、训练配置到排坑路径,把这份资源的实际操作完整过一遍。

2. VOC与YOLO双格式解析:xml/txt字段、标注统计与一键检查脚本

2.1 文件命名规律与对照关系

解压后你会看到每个样本对应三个文件。比如firc_egg_1.xml、firc_egg_1.txt,加上同名jpg图片。前缀firc_egg_是项目代号,后面是数字编号,命名上是连续的,但同一张图片的xml和txt完全同名,只是后缀不同。也就是说XML和txt的配对靠文件名,训练脚本读取时也默认按这个规则找文件。

这里有个实际用途:做数据划分时按文件名前缀挑train/val即可,不用维护单独的配对表。但如果某些训练框架允许xml和txt文件名不一致,你在划分时需要单独写一个匹配逻辑,这反而是多余工作。用这份数据集可以直接用最朴素的 glob 方式组织训练列表。

2.2 xml标注字段逐项拆解

VOC格式的xml是labelImg保存的原始标注。打开任意一个xml,核心内容集中在<object>标签内部。一个标签对应一个标注框,多类别就出现多个<object>。

<annotation> <folder>images</folder> <filename>firc_egg_1.jpg</filename> <size> <width>640</width> <height>480</height> <depth>3</depth> </size> <object> <name>egg</name> <bndbox> <xmin>120</xmin> <ymin>80</ymin> <xmax>310</xmax> <ymax>260</ymax> </bndbox> </object> <object> <name>egg-crack</name> <bndbox> <xmin>180</xmin> <ymin>100</ymin> <xmax>240</xmax> <ymax>160</ymax> </bndbox> </object> </annotation>

代码逻辑说明:<filename>是本图文件名,<size>里的width/height/depth是原始图片宽高和通道数,<object>里<name>是类别字符串,<bndbox>是标注框左上角和右下角的像素坐标。用labelImg标注时,<folder>不一定准确指向实际图片路径,很多预处理脚本会忽略它,只用<filename>加上自己定义的图片根目录来拼接路径。

需要特别注意的是,xml里所有坐标都是像素绝对值,直接换算成YOLO格式时,必须除以图片真实宽高,不能直接粘贴。如果图片被resize过,坐标也要同步变换,否则框就错位了。

2.3 txt标注格式与归一化换算逻辑

每个txt文件里,每行代表一个目标,格式是class_id x_center y_center width height。注意这里x_center、y_center是相对于图片宽高的比例值,范围是0到1,不是像素坐标。

0 0.3125 0.4167 0.2344 0.3125 1 0.3281 0.2708 0.0938 0.1250

代码逻辑说明:第一行0代表类别egg(类别ID从0开始编号),后面四个值分别是归一化后的中心点x、中心点y、框宽、框高。第二行1代表类别egg-crack。拿xml里的xmin=120举例,图片宽度是640,那么中心点x就是(120+310)/2/640=0.3359,宽度是(310-120)/640=0.2969。这份数据集的txt是已经算好的,但理解换算关系能帮你手动校验标注有没有出错。

037批次样本:如果用labelImg打开xml和图片,把txt里的数值点画回去,你会发现egg-crack的框往往嵌套在egg的框内部或者边缘。这说明标注逻辑是把裂缝作为蛋体内部缺陷来标,而不是单独标一个裂缝区域。这种嵌套标注在训练时需要留意,后面我会细说。

2.4 标注统计脚本:验证类别数和框数是否匹配

拿到数据集第一件事不是训练,而是用脚本核对一下声明里的数字。正文声称egg框数2387、egg-crack框数368、总框数2755。我一般先扫一遍所有xml,统计一下再决定要不要做类别均衡处理。

import xml.etree.ElementTree as ET import glob xml_files = glob.glob('./*.xml') counts = {'egg': 0, 'egg-crack': 0} total_boxes = 0 for xml_file in xml_files: tree = ET.parse(xml_file) root = tree.getroot() for obj in root.findall('object'): name = obj.find('name').text if name in counts: counts[name] += 1 total_boxes += 1 print('类别框数:', counts) print('总框数:', total_boxes) print('最大单图框数上限检查: 图片数', len(xml_files))

代码逻辑说明:用ElementTree解析每个xml,遍历object节点并统计类别名出现的次数。这个脚本只做基础核对,不检查坐标是否越界、框是否重叠。输出结果应该和数据集描述一致,如果不一致,说明某个xml在标注完后又手动改过,后续训练会不稳定。

参数说明:glob.glob('./*.xml')默认只扫当前目录,如果你的数据集里xml放在子目录,改成glob.glob('./**/*.xml', recursive=True)。实际执行时,我建议把图片数和xml数、txt数一起比对,三方缺一不可。

3. YOLO训练落地:数据划分、yaml配置与损失曲线判读

3.1 数据划分脚本:固定随机种子保证复现

2077张图,按训练/验证8:2划分,大概1661张训练、416张验证。划分的时候优先保证类别分布和原数据集一致,不能把带裂缝的样本全切到验证集里。

import os import random from collections import defaultdict images = [f for f in os.listdir('.') if f.endswith('.jpg')] random.seed(42) labels_dict = defaultdict(list) for img in images: stem = os.path.splitext(img)[0] with open(f'{stem}.txt', 'r') as f: lines = f.read().strip().splitlines() has_crack = any(line.split()[0] == '1' for line in lines) labels_dict['crack' if has_crack else 'normal'].append(stem) train_files, val_files = [], [] for group in labels_dict.values(): random.shuffle(group) split_idx = int(len(group) * 0.8) train_files += group[:split_idx] val_files += group[split_idx:] with open('train.txt', 'w') as f: f.write('\n'.join([f'images/{name}.jpg' for name in train_files])) with open('val.txt', 'w') as f: f.write('\n'.join([f'images/{name}.jpg' for name in val_files])) print(f'train: {len(train_files)}, val: {len(val_files)}')

代码逻辑说明:先把所有图片按是否有egg-crack分成两组,再在组内按8:2比例切分,这样训练集和验证集中裂缝类别的样本比例基本一致。random.seed(42)固定随机种子,确保每次复现时划分结果完全一样,不会因为重新跑一次脚本导致训练集变了。

参数说明:split_idx用的0.8是常规经验值。如果数据集偏小或者裂缝样本特别少,可以考虑把验证集比例降到0.15,或者用K折交叉验证。另外labels_dict['crack']的判断逻辑依赖类别ID,如果txt里类别顺序变了,这里也要同步改。

3.2 YOLO训练yaml配置:认准路径字段

以YOLOv8为例,数据配置yaml里需要指定path、train、val、names四个关键字段。这个数据集自带类别顺序是["egg", "egg-crack"],对应ID 0和1。

path: /your/absolute/path/to/firc_egg_dataset train: train.txt val: val.txt names: 0: egg 1: egg-crack

配置说明:path指向数据集根目录,train和val可以写相对路径,也可以直接写txt文件的绝对路径列表。names是类别名字典,顺序必须和txt标注里的ID严格对应,不能调换。如果写成{0: 'egg-crack', 1: 'egg'},整个训练就废了,模型会把裂缝当成完好蛋。

实操时我习惯把train.txt和val.txt放在yaml同目录下,path用绝对路径,避免在服务器上换路径后找不到文件。需要注意,train.txt里的图片路径如果写的是相对路径,是相对于yaml里path字段的,不是相对于txt本身的。

3.3 超参数设定:只看这一份数据集的收敛逻辑

训练参数本质上取决于负样本比例。这份数据集里,egg-crack只有368个框,占全部框数的13.4%左右,属于典型的正负样本不均衡场景。直接默认参数训练,模型大概率把裂缝类别的置信度压得很低。

yolo detect train \ data=egg_dataset.yaml \ model=yolov8n.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ mosaic=0.5 \ close_mosaic=10

命令说明:model用yolov8n是为了先跑通流程,如果你的显卡显存足够,可以换yolov8s.pt或yolov8m.pt。epochs=150是起步值,如果验证集mAP50在100轮内已经稳定不涨,提前停掉也行。imgsz=640和数据集原始分辨率匹配,不额外resize太多。mosaic=0.5是马赛克增强的概率,裂缝属于细小纹理,马赛克拼接时裂缝特征容易被其他蛋体区域淹没,所以从默认的1.0降到0.5。close_mosaic=10表示训练最后10轮关闭马赛克,让模型在真实分布上微调。

参数说明:lr0=0.01是初始学习率,数据量小的时候调低一点更稳,调成0.005也常见。batch=16取决于显存,如果显卡只有8GB,batch降到8比较保险。最关键的是别把mosaic直接设成1.0,后面我会说为什么。

3.4 训练日志判读:从loss曲线看出数据集的问题

训练过程中重点看两个指标:box_loss和cls_loss。如果cls_loss下降很慢,或者验证集的mAP50-95一直在0.5以下徘徊,先不要调网络结构,大概率是数据标注里裂缝框太小,模型没学到足够的纹理上下文。

tail -f runs/detect/train/exp/weights/last.pt

这条tail命令的意义:last.pt是每轮结束保存的权重,配合train目录下的results.csv,可以实时看每一轮各loss变化。如果发现class_loss在30轮后开始震荡,说明正样本特征太弱,需要回头做数据增强或者重新审视egg-crack的标注框是否覆盖完整。

还有一个容易被忽略的位置:训练结束后看confusion_matrix.png。如果egg-crack的漏检率高,混淆矩阵里会出现大量把egg-crack预测成background的格子。这说明不是模型不会检测,而是正样本太少导致置信度阈值偏高,需要调低conf或者对裂缝类别加权。

4. 常见问题排查:类别失衡、标注边界与训练翻车的四个真实坑

4.1 现象:xml和txt类别ID对不上

有一次训练中我发现模型输出的egg类别置信度特别高,裂缝几乎框不出来。查了训练日志,发现cls_loss不收敛,然后抽查了几个txt文件,第一行居然是1 0.32 0.41 0.28 0.31。

原因:txt里类别ID为1,对应的类别名是egg-crack,但训练脚本里names顺序写成了['egg-crack', 'egg'],导致模型把完好的鸡蛋学成了裂缝。

解决:检查所有txt第一列数字的最大值是否等于类别数减1。做一次全局校验,用脚本提取所有txt里的类别ID,和yaml里names的key做映射对比。从那以后,每次换数据集我第一件事就是跑一遍这个ID映射校验脚本。

4.2 现象:egg-crack的召回率奇低,模型几乎不输出裂缝框

训练正常结束,验证集mAP50看着还行,但业务场景里裂缝蛋漏检率特别高。

原因:egg-crack只有368个框,而egg有2387个框,正负样本比例约1:6.5。模型倾向把不确定性分类归到大类别上。另外裂缝框偏小,在640分辨率下占的面积只有几十个像素,特征原本就不明显。

解决:两种手段并用。第一,训练时给egg-crack类别的loss加权,在YOLOv8里可以通过cls参数配合自定义损失权重实现,或者直接复制裂缝样本做离线增强。第二,推理时把conf阈值从默认0.25降到0.1,再用NMS把重复框过滤掉。实际项目里这两种手段能把召回率从60%提到80%左右。

4.3 现象:训练早期loss就出现NaN,直接崩掉

日志出现NaN后,模型权重无法保存,只能重来。

原因:最常见的是学习率太大,加上某些标注框里width或height为0。框宽高为0时,loss计算对数会出现无穷大,进而梯度爆炸。这种情况通常发生在txt文件里某一行坐标算出来是0。

解决:用脚本对每行txt做范围检查,过滤掉width==0或height==0的样本;把lr0从默认0.01降到0.005。建议逐项排查数据文件:

先执行脚本检查txt里是否有异常值,再做过滤。

import os for f in os.listdir('.'): if f.endswith('.txt'): with open(f) as fp: lines = fp.read().strip().splitlines() new_lines = [] for line in lines: parts = line.split() w, h = float(parts[3]), float(parts[4]) if w > 0 and h > 0 and w < 1 and h < 1: new_lines.append(line) with open(f, 'w') as fp: fp.write('\n'.join(new_lines))

代码逻辑说明:遍历所有txt文件,把宽高不在0到1之间或者为0的行删掉。没删除任何内容的行就保持原样,最后写回文件。注意这样改了原始标注文件,执行前最好备份原文件。如果你发现大量行被过滤,说明标注环节出过坐标导出bug,要排查labelImg版本或转换脚本的除法逻辑。

4.4 现象:验证集loss比训练集低很多,训练指标正常但实测不行

训练和验证指标都不错,但拿到现场摄像头采集的图片上,大量错检。

原因:这份数据集里裂缝框和蛋体框是嵌套关系,模型可能学会了把蛋体内部某个亮度纹理区域当成裂缝。验证集和训练集来自同一批采集环境,没有覆盖现场的光照变化,所以指标虚高。

解决:现场采集20到30张不同角度的图片,单独跑一次推理看框的输出位置。如果裂缝框总是落在蛋体边缘高光区域,考虑把现场图片加入训练集做增量训练。另外要注意,这份数据集的采集背景可能比较单一,泛化到传送带场景需要额外补充数据,这不是模型参数能解决的。

5. 进阶:把裂缝这类小目标检出率做上去的验证与增强技巧

先做一步mAP验证,把不同置信度阈值下的精度召回曲线拉出来看。YOLOv8自带验证命令,在训练完成后执行:

yolo detect val \ data=egg_dataset.yaml \ model=runs/detect/train/exp/weights/best.pt \ conf=0.1

重点看类别的mAP50-95,如果egg-crack的mAP50比egg低10个百分点以上,说明这个模型对裂缝的定位能力不够,需要单独优化。

增强的常见做法是复制粘贴法。把裂缝区域从原图抠出来,随机贴到同一张图的其他蛋体上,同时生成对应的标注框。这个方法在小目标检测里效果很明显,因为裂缝本身是局部纹理突变,复制出来再贴回去,模型能学到更丰富的背景组合。每张训练图附加2到3个裂缝副本,类别比例会从368框提升到700到900框,训练时的cls_loss更容易收敛。

做了这几步之后,egg-crack的mAP50通常能从0.7提高到0.85以上。如果还不够,下一步才是换检测头或者加注意力模块。先把数据层面的问题榨干,再改模型结构,这是我一直以来的习惯。这份数据集格式规范,文件名规整,拿来直接跑通全流程没什么压力,但越是规整的数据集越容易忽略标注框嵌套和类别比例这两个隐藏问题。从那以后我每次做质检类数据集训练,都强制先跑一遍标注统计。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询