
简介这是一份面向鸡蛋表面缺陷检测的视觉数据集采用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训练要txtFaster 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 folderimages/folder filenamefirc_egg_1.jpg/filename size width640/width height480/height depth3/depth /size object nameegg/name bndbox xmin120/xmin ymin80/ymin xmax310/xmax ymax260/ymax /bndbox /object object nameegg-crack/name bndbox xmin180/xmin ymin100/ymin xmax240/xmax ymax160/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里的xmin120举例图片宽度是640那么中心点x就是(120310)/2/6400.3359宽度是(310-120)/6400.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, recursiveTrue)。实际执行时我建议把图片数和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([fimages/{name}.jpg for name in train_files])) with open(val.txt, w) as f: f.write(\n.join([fimages/{name}.jpg for name in val_files])) print(ftrain: {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 \ dataegg_dataset.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ mosaic0.5 \ close_mosaic10命令说明model用yolov8n是为了先跑通流程如果你的显卡显存足够可以换yolov8s.pt或yolov8m.pt。epochs150是起步值如果验证集mAP50在100轮内已经稳定不涨提前停掉也行。imgsz640和数据集原始分辨率匹配不额外resize太多。mosaic0.5是马赛克增强的概率裂缝属于细小纹理马赛克拼接时裂缝特征容易被其他蛋体区域淹没所以从默认的1.0降到0.5。close_mosaic10表示训练最后10轮关闭马赛克让模型在真实分布上微调。参数说明lr00.01是初始学习率数据量小的时候调低一点更稳调成0.005也常见。batch16取决于显存如果显卡只有8GBbatch降到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做范围检查过滤掉width0或height0的样本把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 \ dataegg_dataset.yaml \ modelruns/detect/train/exp/weights/best.pt \ conf0.1重点看类别的mAP50-95如果egg-crack的mAP50比egg低10个百分点以上说明这个模型对裂缝的定位能力不够需要单独优化。增强的常见做法是复制粘贴法。把裂缝区域从原图抠出来随机贴到同一张图的其他蛋体上同时生成对应的标注框。这个方法在小目标检测里效果很明显因为裂缝本身是局部纹理突变复制出来再贴回去模型能学到更丰富的背景组合。每张训练图附加2到3个裂缝副本类别比例会从368框提升到700到900框训练时的cls_loss更容易收敛。做了这几步之后egg-crack的mAP50通常能从0.7提高到0.85以上。如果还不够下一步才是换检测头或者加注意力模块。先把数据层面的问题榨干再改模型结构这是我一直以来的习惯。这份数据集格式规范文件名规整拿来直接跑通全流程没什么压力但越是规整的数据集越容易忽略标注框嵌套和类别比例这两个隐藏问题。从那以后我每次做质检类数据集训练都强制先跑一遍标注统计。希望帮到你。本文还有配套的精品资源点击获取