ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

蘑菇识别数据集VOC转YOLO与YOLOv8训练实战指南

蘑菇识别数据集VOC转YOLO与YOLOv8训练实战指南 简介面向深度学习目标检测的蘑菇类型识别数据集整体包含8430张jpg蘑菇图像覆盖21个类别采用Pascal VOC格式xml与YOLO格式txt双标注适用于目标检测入门、蘑菇识别模型训练及毒蘑菇甄别、生态调查、农业分类等应用场景。压缩包采用7z封装共2000个文件以xml标注文件为主要类型并附md使用说明包体大小约193.56MB便于直接查看数据组织方式。目前已有429人学习资源提供md说明和VOC/YOLO两种主流标注可快速接入YOLO、Faster R-CNN等常见检测框架进行训练与验证。标注类别较细、图像背景多样适合需要在真实场景中检验蘑菇识别效果的开发者和研究人员。1. 蘑菇类型识别检测数据集8430张21类VOC与YOLO格式要怎么用做食用菌分拣、野外蘑菇识别或者智能农业巡检的工程师找数据集时大概率会碰到这样一个压缩包8430张图片、21个蘑菇类别、VOC与YOLO两种标注格式一起打包。它解决的是检测任务里的一个实际痛点——标注格式不统一。LabelImg这类工具默认导出的VOC XML适合人读、适合人工复核而YOLO系列训练直接消费txt归一化坐标两套格式摆在一起既能追溯原始标注又能直接跑训练省掉来回转换的麻烦。7z后缀意味着下载后用常规解压工具打不开的人不在少数这一步处理不好后面所有环节都动不了。适合刚拿到数据准备训练yolov5或yolov8的工程师也适合想彻底搞懂VOC与YOLO标注字段差异的入门者。2. 蘑菇识别数据集解压与目录结构7z命令、VOC标注布局和21类标签表2.1 用7z解压8430张蘑菇图片数据集的最小命令与参数拿到.7z文件先别急着双击。图片数据集用7z压缩压缩率通常比zip高20%到30%但解压后占用的磁盘空间会明显膨胀。我先说Linux服务器上的做法这也是我日常处理这类数据集的主要环境。先装p7zipsudo apt install -y p7zip-full然后执行解压7z x mushroom_types_voc_yolo_8430.7z -omushroom_datasetx表示解压并保留压缩包内的目录结构-o指定输出目录。注意-o与后面的目录名之间不能加空格写成-o mushroom_dataset会生成一个名字都带横杠的非法目录。解压完成后用du -sh mushroom_dataset看一眼实际大小再对照df -h确认磁盘剩余空间够不够。7z压缩率高解压后体积膨胀两到三倍很正常尤其JPEG图片本身压缩性一般空间不够时训练到一半报磁盘写满排查起来非常被动。Windows下如果装了7-Zip命令行可以这样调用C:\Program Files\7-Zip\7z.exe x 文件名.7z -o输出目录。如果解压时提示密码错误但确认密码无误常见原因是压缩包内文件名用了特殊编码先试试在7-Zip里把“文件名编码”切到UTF-8别急着断言密码有问题。2.2 VOC目录结构JPEGImages、Annotations、ImageSets/Main 各放什么解压后典型的VOC风格目录结构是这样mushroom_dataset/ ├── JPEGImages/ ├── Annotations/ ├── ImageSets/ │ └── Main/ └── yolo_labels/JPEGImages里是全部8430张原图命名一般是纯数字或编号比如000001.jpg。Annotations里是等量的XML文件文件名必须和图片完全一致只差扩展名这一步是后面所有脚本能跑通的前提。ImageSets/Main下面放的是train.txt、val.txt、test.txt内容仅是图片文件名不带扩展名每行一个这是VOC训练时代的划分方式。yolo_labels是数据集作者额外生成的txt标签目录每个txt对应一张图每行一条标注。常见误区是把ImageSets里的txt误当成标签文件直接丢给训练脚本或者把JPEGImages整个目录作为训练输入。对YOLO训练来说真正起作用的是yolo_labels里的txt和图片名配对关系。拿到数据后我一般先抽查三到五个XML和对应txt确认两种格式的框坐标是否一致、类别名是否对得上再决定用现成YOLO标签还是自己重新转一遍。2.3 21个类别的标签映射表与类别索引核对VOC的XML里类别名是字符串YOLO的txt里类别是整数索引转换时最怕两类表对不上。我建议先看数据集根目录有没有classes.txt有就以它为准。常见的21类蘑菇识别数据集合集会覆盖这些类别具体名称和顺序以你下载的包内文件为准下面这个表只是说明映射规则类别索引VOC中的nameYOLO classes.txt0buttonbutton_mushroom1shiitakeshiitake2oysteroyster_mushroom3enokienoki4king_oysterking_oyster5portobelloportobello6chanterellechanterelle7morelmorel核对时重点看三点一是VOC里name是否与classes.txt顺序一致二是classes.txt里有没有重复行三是索引是否从0开始连续。YOLO训练要求类别索引必须是0 到 nc-1的连续整数中间缺一个就会在计算损失时越界。先把这张映射表钉死后面转格式和训练才不会翻车。3. 从VOC转YOLO蘑菇标注转换脚本与归一化坐标计算3.1 为什么要自己跑一遍转换而不是直接用现成标签很多人拿到数据集直接就用yolo_labels里的txt我一般会先自己写脚本把VOC转一次原因有三。第一现成txt的类别顺序未必和后续训练时的classes.txt一致差一位就意味着所有标注整体错位模型学出来的东西全乱。第二转换过程能把XML里的坐标边界问题暴露出来比如某个框超出图像宽高或某个object缺少bndbox节点。第三自己转一遍就知道坐标怎么来的训练阶段出现标签相关报错时能快速定位是原始标注问题还是转换脚本问题。YOLO格式每行是class_id x_center y_center width height前四个数值全部除以图像宽高做归一化取值范围应落在0到1之间。VOC格式则是xmin ymin xmax ymax的像素绝对值单位是像素两种格式不能混用。3.2 XML转YOLO的Python脚本与坐标换算逻辑下面这个脚本是标准做法直接放在数据集根目录运行会读取Annotations下所有XML并在yolo_labels_converted目录下生成txtimport xml.etree.ElementTree as ET import os from glob import glob # 21个类别顺序必须和最终训练用的classes.txt完全一致 CLASSES [ button_mushroom, shiitake, oyster_mushroom, enoki, king_oyster, portobello, chanterelle, morel, matsutake, truffle, # 剩余类别按实际classes.txt补全顺序不能变 ] def voc_to_yolo(xml_path: str, out_dir: str) - None: tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(./size/width).text) img_h int(root.find(./size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: print(fskip unknown class: {name} in {xml_path}) continue cls_id CLASSES.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) x_center ((x1 x2) / 2) / img_w y_center ((y1 y2) / 2) / img_h width (x2 - x1) / img_w height (y2 - y1) / img_h x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) width min(max(width, 0.0), 1.0) height min(max(height, 0.0), 1.0) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) txt_name os.path.splitext(os.path.basename(xml_path))[0] .txt with open(os.path.join(out_dir, txt_name), w, encodingutf-8) as f: f.write(\n.join(lines)) if __name__ __main__: os.makedirs(yolo_labels_converted, exist_okTrue) for xml_file in glob(Annotations/*.xml): voc_to_yolo(xml_file, yolo_labels_converted)这段逻辑的核心是把bndbox里的绝对像素坐标换算成相对图像宽高的比例坐标中心点等于左上角和右下角坐标的平均值再除以宽度或高度框宽高等于差值除以宽度或高度。最后对坐标做饱和剪裁把落在0到1之外的数值拉回来防止个别标注框略微越界导致训练报错。CLASSES的列表顺序就是最终类别索引一旦开始训练就不能再动运行脚本时工作目录必须在数据集根目录脚本里的相对路径才找得到Annotations。3.3 转换结果抽查与坐标边界检查转换完别急着删原始XML先做一次机器检查。下面命令找出所有坐标越界的行cat yolo_labels_converted/*.txt | awk { for(i2;i5;i) if($i0 || $i1) print FILENAME, $0 }awk从第二列开始检查因为第一列是类别ID不参与归一化。如果输出为空说明这批坐标都在合理范围内。再看行数是否对得上比如wc -l Annotations/000001.xml里object的个数和对应txt行数相等。下面这个表是我在同类数据集上常见的三种异常和对应处理方式异常特征可能原因处理方式某行坐标大于1或为负原始XML框越界或size字段解析错误打开对应XML人工核对txt行数比XML中object少类别名不在CLASSES映射中补全类别映射后重新转图片找不到对应txt文件名不一致或XML解析失败循环对比两边文件名坐标偏差超过千分之几可以忽略但如果出现框宽高为负值说明标注的xmin大于xmax这种样本要在转换后直接剔除靠剪裁解决不了问题。4. 用YOLO格式蘑菇数据集训练前必查的三件事标签预览、类别分布和划分4.1 用OpenCV把YOLO标签画回原图确认框没有漂移转换脚本跑通不等于标签可用。最简单可靠的验证方式是把txt画回原图肉眼看框的位置和蘑菇实际位置是否吻合。下面这段代码读取一张图和对应的YOLO标签在原图上绘制绿色矩形框和类别名import cv2 import os def draw_yolo_label(img_path: str, label_path: str, class_names: list, out_path: str): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r, encodingutf-8) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(fbad line in {label_path}: {line.strip()}) continue cls_id int(parts[0]) x_c, y_c, bw, bh map(float, parts[1:]) x1 int((x_c - bw / 2) * w) y1 int((y_c - bh / 2) * h) x2 int((x_c bw / 2) * w) y2 int((y_c bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(out_path, img) class_names [button_mushroom, shiitake, oyster_mushroom, enoki] draw_yolo_label(JPEGImages/000001.jpg, yolo_labels_converted/000001.txt, class_names, check_000001.jpg)画框时把归一化坐标乘回图像宽高注意要用int()而不是round()避免边缘像素溢出。检查重点不是框画得准不准而是框和蘑菇实体之间有没有系统性偏移比如所有框整体偏左上那就说明转换脚本里坐标换算有误或原始标注本身就有偏差。随机抽20到30张图看一遍覆盖率足够高可以放行。4.2 统计21类别样本分布与数量不均衡处理检测数据集最怕类别数量悬殊。统计每类出现次数用这一条命令cat yolo_labels_converted/*.txt | awk {cnt[$1]} END {for(c in cnt) print c, cnt[c]} | sort -n输出第一列是类别ID第二列是该类别在所有标注中出现的总次数。注意这里统计的是标注框个数不是图片张数。一张图里可能同时出现多个蘑菇所以总框数会大于8430。样本量情况对训练的影响常见处理策略某类超过2000框模型偏向该类其他类被压制适当下采样或多类别损失权重某类只有几十框该类几乎学不出来复制该类别样本做重复采样图片级不均衡但框数均衡背景干扰差异大检查遮挡与光照分布考虑增强如果21个类别里有个别类别只有几十个框我一般先把这些图片单独复制一份混回数据集让模型每轮多看到几次而不是直接改损失函数。改损失函数会影响所有类别调试周期长先做数据层面的均衡更可控。4.3 按7:2:1划分train/val/test并生成data.yamlVOC的ImageSets/Main里可能已有划分但我习惯自己重新划分保证随机性和可复现性。划分脚本如下import os import random from glob import glob random.seed(42) images sorted(glob(JPEGImages/*.jpg)) random.shuffle(images) n len(images) n_train int(n * 0.7) n_val int(n * 0.2) splits { train: images[:n_train], val: images[n_train:n_train n_val], test: images[n_train n_val:], } for split, img_paths in splits.items(): os.makedirs(fyolo_{split}, exist_okTrue) with open(fyolo_{split}.txt, w, encodingutf-8) as f: f.write() for img_path in img_paths: base os.path.splitext(os.path.basename(img_path))[0] label_path fyolo_labels_converted/{base}.txt if not os.path.exists(label_path): print(fmissing label: {label_path}) continue os.system(fcp {img_path} yolo_{split}/) os.system(fcp {label_path} yolo_{split}/) with open(fyolo_{split}.txt, a, encodingutf-8) as f: f.write(os.path.abspath(img_path) \n)随机种子固定为42是为了别人复现时划分结果一致。注意这段脚本是复制文件到独立目录而不是移动原始数据集保持不动出问题随时能重来。划分结束后写data.yamlpath: /path/to/mushroom_dataset train: yolo_train.txt val: yolo_val.txt test: yolo_test.txt nc: 21 names: 0: button_mushroom 1: shiitake 2: oyster_mushroom 3: enoki 4: king_oyster # 其余类别按下标顺序补全names的顺序必须和转换脚本里的CLASSES完全一致一个字母都不能差。data.yaml里的test项可有可无训练时会自动忽略test集有它的话训练完可以直接在测试集上评估不用临时改配置。5. 拿蘑菇数据集跑yolov8训练参数设置与报错排解5.1 训练命令与必调参数数据准备完毕训练命令如下yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs120 \ imgsz640 \ batch16 \ patience20 \ device0 \ workers8参数含义建议model预训练权重与模型规模样本不足或算力一般选yolov8sepochs最大训练轮数120起步配合early stoppingimgsz输入图像尺寸640是精度与显存平衡点小目标多试试960batch每批图片数显存不足时减到8不要直接降imgszpatience连续多少轮val指标不提升就早停15到30之间epochs设大没关系patience会帮你早停。如果训练曲线震荡明显关注yolo损失函数里box与cls两项yolov8默认对类别损失加了权重类别不均匀时会在前几十轮出现波动属正常现象。5.2 训练中高频报错与处理训练阶段最常见的报错是AssertionError: class index 20 out of range意思是txt里出现了类别ID为21的标注越界了。排查命令awk $120 {print FILENAME, $0} yolo_labels_converted/*.txt找到越界文件后回看XML的name字段多半是某个类别名不在CLASSES里导致索引错乱。第二种是空标签文件训练日志会提示某个图片没有对应标签用find yolo_labels_converted -name *.txt -size 0找出空文件连同对应图片一起移出数据集。第三种是预训练权重下载卡住网络受限环境下yolov8会自动尝试从远端拉取权重处理方式是先把yolov8s.pt手动下载好放到项目根目录再执行训练脚本检测到本地文件后不会再尝试联网。提示看到标签报错先定位是哪个文件别急着调整epochs或网络结构数据问题排查成本最低。5.3 用混淆矩阵与置信度阈值指导调优训练结束后查看runs/detect/train目录下的confusion_matrix.png重点关注21个类别里哪些容易被互相混淆。比如鸡腿菇和杏鲍菇外观接近混淆矩阵里相应位置颜色深是正常的说明这类分辨确实难后续可以单独采数据或裁剪更多局部细节。数值指标看results.csv里面每一行是一个epoch的mAP50和mAP50-95用sort -t, -k10 -nr results.csv | head能快速拿到最优epoch。推理阶段的置信度阈值不是固定不变。对蘑菇这类目标密集、边缘样本多的任务我习惯先跑一次yolo detect predict modelbest.pt sourcetest_images conf0.25看召回再提高到0.5看精度。如果某些长尾类别在conf0.5下几乎不出框单独给它们设0.2宁可多几个误检再人工过滤也不要让阈值把真目标全部吞掉。本文还有配套的精品资源点击获取
返回列表