资讯动态

冬虫夏草检测数据集:从Pascal VOC标注到YOLO格式转换实战

发布时间:2026/9/14 18:10:42 来源:尧图企业网站定制
简介一份面向目标检测任务的数据集资源核心内容为冬虫夏草生长检测。包含613张实地拍摄的草地冬虫夏草图片以及相同数量的XML标注文件标注工具为LabelImg矩形框记录左上与右下角坐标标注类别为单一冬虫夏草。除图片与标注外还附带类别JSON文件与可视化Python脚本脚本可随机读取一张图片并自动绘制边界框保存到当前目录无需修改即可运行方便快速核对标注效果。整套资源共1229个文件以JPG图片和XML标注为主另含少量JSON、PNG与PY脚本压缩包整体约80.12MB。已有226人学习下载。该数据集可直接用于训练YOLOv5等目标检测模型XML标注格式具通用性便于转换为COCO或YOLO格式配合作者在博客分享的YOLOv5改进实战可帮助开发者系统完成从数据准备、模型训练到效果优化的完整流程同时为自定义数据集的标注与验证提供范例适合农业智能化与野生菌类监测等应用场景。1. 野外单类检测数据集的结构与使用思路做目标检测的人通常不缺模型缺的是干净可用的数据。这份冬虫夏草生长检测数据集一共 613 张图片和 613 个 XML 标签文件标注对象只有一类别草地上刚生长出来的冬虫夏草标注工具是 LabelImg输出形式是 XML 里的矩形框记录的是左上角和右下角坐标。613 张对单类检测来说不算大但对野外采集场景来说已经足够支撑 YOLOv5、YOLOv8 这类模型做迁移学习关键是怎么把这批 XML 用起来。数据集还附带了一个可视化脚本和一个类别 JSON 文件前者随机取图就能画框预览后者用于标注类别到索引的映射。本文从 XML 解析、可视化验证、JSON 类别配置、YOLO 训练格式转换到增强训练时容易翻车的点完整过一遍这个资源的使用链路。2. 数据集目录结构与 XML 矩形框标注格式解析2.1 拿到解压包后先看什么解压后的核心内容不是图片而是每张图同名的 XML 文件。图片名形如image (78).jpg对应的标注文件就是image (78).xml这种命名方式在 LabelImg 导出时非常常见注意文件名里的括号在 Shell 脚本中需要转义或用引号包裹批量操作时很容易在这里踩坑。├── images/ │ ├── image (78).jpg │ ├── image (71).jpg │ └── ... ├── labels/ │ ├── image (78).xml │ ├── image (71).xml │ └── ... ├── classes.json └── visualize.py如果解压后没有classes.json打开任意一个 XML 文件看objectname节点的值单类别数据集通常就是这个类别名本身。2.2 XML 标注格式的中心坐标换算LabelImg 输出的 XML 采用 Pascal VOC 格式核心是object节点下的bndbox里面存的是xmin, ymin, xmax, ymax四个像素坐标分别对应目标框的左上角和右下角。注意这个坐标系原点在图片左上角x 轴向右y 轴向下和 OpenCV、PIL 的坐标系统一致不需要翻转。import xml.etree.ElementTree as ET def parse_voc_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) boxes [] for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) boxes.append({ label: name, xmin: xmin, ymin: ymin, xmax: xmax, ymax: ymax, w: xmax - xmin, h: ymax - ymin }) return width, height, boxes这段代码是后续所有预处理的基础。width和height必须与图片真实尺寸一致否则在归一化坐标时整体偏移。这里boxes收集了每个实例的宽高信息后面统计目标尺寸分布、检查标注是否越界都会复用。2.3 LabelImg 导出时容易被忽略的两个字段除了bndboxXML 里还有folder、filename、path三个路径相关字段。LabelImg 在导出时filename写的是图片文件名path写的是标注时图片的绝对路径。如果是换电脑或者数据集整体移动过目录path就是错的但它不影响训练因为 YOLO 训练流程是拿图片文件路径去解析不是读 XML 里的 path。这时候容易犯的错是用glob去匹配 XML 名然后再去拼接图片路径导致括号和空格处理出错。import os import glob xml_dir labels img_dir images xml_files glob.glob(os.path.join(xml_dir, *.xml)) for xml_path in xml_files: basename os.path.splitext(os.path.basename(xml_path))[0] img_path os.path.join(img_dir, basename .jpg) width, height, boxes parse_voc_xml(xml_path) # 后续转 YOLO 格式时img_path 是实际存在的那张图basename直接取自 XML 文件名这样无论filename和path字段怎么脏都不会影响匹配结果。3. 数据可视化脚本的原理与边界框绘制实现3.1 随机抽图验证标注质量这份资源自带的visualize.py设计思路很直接从数据集里随机挑一张图片解析同名 XML用 OpenCV 绘制矩形框并保存到当前目录。为什么要做这一步因为 XML 标注是纯数字人无法直觉判断位置是否正确。画框预览的性价比极高框的位置偏了、尺寸是 0、坐标超出了图片边界一张图就能暴露问题。import os import cv2 import random import xml.etree.ElementTree as ET image_dir images xml_dir labels all_images [f for f in os.listdir(image_dir) if f.lower().endswith(.jpg)] chosen random.choice(all_images) stem os.path.splitext(chosen)[0] img cv2.imread(os.path.join(image_dir, chosen)) H, W img.shape[:2] tree ET.parse(os.path.join(xml_dir, stem .xml)) root tree.getroot() for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin int(float(bbox.find(xmin).text)) ymin int(float(bbox.find(ymin).text)) xmax int(float(bbox.find(xmax).text)) ymax int(float(bbox.find(ymax).text)) cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.putText(img, name, (xmin, ymin - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) out_path visualized_ chosen cv2.imwrite(out_path, img) print(fsaved: {out_path})运行这个脚本前把image_dir和xml_dir指向解压后的实际目录即可。cv2.rectangle 的第三、四个参数是左上角和右下角与 XML 里 bndbox 的语义一一对应所以不需要换算。颜色如果是 (0, 255, 0) 绿色在绿色草地里对比度可能略低可以改成红色 (0, 0, 255) 或者蓝色看实际图再调。3.2 坐标越界检查可视化脚本之外的批量验证可视化脚本每次只能看一张随机图要系统性地检查 613 张图是否全部规范建议补一个批量越界扫描。所谓越界指的是 xmax 大于图片宽度或者 ymax 大于图片高度这种情况通常发生在标注后期手动微调框时不小心拉出了边界训练时如果不处理YOLO 在计算损失时会直接报错。bad 0 for xml_path in xml_files: width, height, boxes parse_voc_xml(xml_path) img_path xml_path.replace(labels, images).replace(.xml, .jpg) if not os.path.exists(img_path): print(fmissing image: {img_path}) bad 1 continue img cv2.imread(img_path) H, W img.shape[:2] for box in boxes: if box[xmin] 0 or box[ymin] 0 or box[xmax] W or box[ymax] H: print(f{xml_path}: box out of bounds) bad 1 print(ftotal issues: {bad})这段代码里真正的检查是对W和H与 XML 的size字段做对比如果发现漏检一般不是 XML 写错了而是图片被压缩过但没有重新标注。所有坐标必须落在[0, W]和[0, H]区间内不满足就说明标签不可信。4. 类别 JSON 文件与 YOLO 训练格式转换4.1 JSON 类别文件到底在配置什么这里的数据集类别为冬虫夏草 1 类也就是classes.json里只有一个类别名。理解 JSON 文件的作用要站在 YOLO 训练流程的角度YOLOv5/YOLOv8 训练时数据集的类别 ID 从 0 开始编号标签文件里的每一行是class_id x_center y_center width height第一个数字就是类别索引。JSON 文件在这里充当类别名与索引的唯一映射。{ categories: [ {id: 0, name: cordyceps} ] }如果拿到的classes.json结构是简单的数组[cordyceps]本质也一样训练时把它转成 YOLO 要求的 yaml 即可。注意类别名的拼写要全局统一如果你决定用 cordyceps那 XML 里name节点的值也被映射成0两边的严格对应关系不能错。4.2 XML 转 YOLO TXT 的完整脚本YOLOv5/YOLOv8 默认不会直接读 Pascal VOC 格式的 XML需要先转成每张图一个.txt标签文件放在与图片同名的labels目录下这是使用这个数据集时最关键的一步没有之一。import os import glob def voc_to_yolo(xml_path, out_dir, class_names): width, height, boxes parse_voc_xml(xml_path) stem os.path.splitext(os.path.basename(xml_path))[0] out_txt os.path.join(out_dir, stem .txt) lines [] for box in boxes: if box[label] not in class_names: raise ValueError(funknown class: {box[label]}) cls_id class_names.index(box[label]) x_center (box[xmin] box[xmax]) / 2.0 / width y_center (box[ymin] box[ymax]) / 2.0 / height w box[w] / width h box[h] / height lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt, w) as f: f.write(\n.join(lines) \n) class_names [cordyceps] os.makedirs(yolo_labels, exist_okTrue) for xml_path in glob.glob(labels/*.xml): voc_to_yolo(xml_path, yolo_labels, class_names)转换的核心是坐标从像素值变为相对图片宽高的比例值而且必须把左上右下的表示转为边界框中心点坐标加宽高。为什么要用中心点而不是直接用 xmin 和 ymin因为 YOLO 的回归目标是对 anchor 的偏移量中心点加宽高的编码方式与模型输出头对齐迁移学习时直接套用 MS COCO 的 anchor 尺寸还是要靠这个格式。归一化时统一除以宽度和高度x 轴除以宽y 轴除以高不能混用否则框会上下左右漂移。4.3 数据集目录组织与训练配置转出来的 YOLO 格式还需要按训练框架的目录规范摆放一般会合成这样的结构目录/文件作用images/train/训练图片labels/train/训练标签images/val/验证图片labels/val/验证标签data.yaml类别名与路径配置划分比例建议按 8:2 或者 9:1613 张图片规模下验证集 120 张左右比较合适。对应的data.yaml如下train: images/train val: images/val nc: 1 names: [cordyceps]这里nc: 1对应单类别names的索引顺序必须与转换脚本里的class_names一致否则训练时类别名和标签数字对不上。没有按这个结构整理的数据集train 路径找不到图时 YOLOv5 会在 DataLoader 阶段报错而不是训练中途才报检查起来相对容易。5. 单类别数据集增强训练时的隐藏风险与验证技巧5.1 标注框重叠问题mosaic 增强的天然克星单类别数据集做 mosaic 增强时有个非常隐蔽的坑。Mosaic 会把四张图拼在一起如果两张图里的冬虫夏草刚好拼到相邻区域增强后生成的新标签框可能重叠或相交模型学到的边界就变成多个目标可以共享同一个框反向拉低精确率。常见做法是在训练时调低 mosaic 概率# hyp.scratch-low.yaml 或训练命令中 mosaic: 1.0 # 默认 1.0单类目标重叠频繁时可降到 0.5 mixup: 0.0 # 单类别小数据集建议关掉 mixup冬虫夏草在草地上密集分布时这种重叠更明显。训练完跑验证集 mAP 时如果发现 recall 高但 precision 持续走低第一嫌疑就是增强阶段产生的框重叠而不是模型容量不够。5.2 用边界框统计判断 anchor 是否合理613 张图片这个规模anchor 大小直接决定小目标能否被召回。统计所有标注框的面积分布如果框普遍很小就要缩小默认 anchor 或开启 autoanchorpython train.py --data data.yaml --cfg yolov5s.yaml --batch-size 16 --epochs 200 --hyp hyp.scratch-low.yaml训练前自动计算的数据集 anchor 是基于你这份 613 张图的真实框分布重新聚类的比 COCO 预训练 anchor 更有针对性。如果关闭 autoanchor锚盒多大模型的感受野就偏向多大小目标容易漏检。统计工具可以简单复用parse_voc_xml返回的w和h画一个散点图或直接打印分位数。冬虫夏草目标通常只占全图的 1% 到 3%看到这样的分布不用惊讶这是这类野外小目标数据集的常态。5.3 可视化输出图的快速验收方法跑完训练后最终验证还是得看效果。用detect.py对验证集出图时可以加--save-txt把检测结果也导出重点检查两类假阳性一是背景中的草茎被误检成冬虫夏草这是样本背景多样性还不够二是两根相邻目标被框成一个这是 NMS 阈值设置偏低导致相邻高 IoU 框没有正确合并。通过对比可视化脚本生成的标注图和 detect 输出的预测图能直观看出问题在标签侧还是模型侧。这个步骤不要跳过它是最低成本的错误定位手段。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价