田间焚烧灰烬检测:基于YOLO/VOC小样本数据集的训练与部署实践

发布时间:2026/10/9 23:37:19
田间焚烧灰烬检测:基于YOLO/VOC小样本数据集的训练与部署实践
简介田间焚烧灰烬目标检测数据集专为农业火灾监控与秸秆禁烧场景设计面向目标检测算法研究者及基层安防项目开发者包含221张高清晰度真实田间照片针对火、烟雾、灰烬三类目标绘制矩形标注框可用于YOLO、Faster R-CNN等模型训练与效果验证。压缩包共665个文件主要包含221张jpg原图、221个xml标注文件VOC格式与223个txt标签文件YOLO格式整体大小16.33MB目录按JPEGImages、Annotations、labels三层组织结构直观导入训练脚本即可使用。目前已有154人浏览学习。标注质量稳定三类别框数分别为灰烬275、火143、烟雾170共588个实例图片未增强保留田间光照、烟雾遮挡等真实变化有助于提升模型在野外的泛化能力作为公开标注数据是一份实用的农业视觉基准资源。1. 田间焚烧灰烬检测小样本数据集如何撑起实际巡检田间农作物焚烧灰烬的目标检测是秸秆禁烧监控、农业面源污染巡查和火灾隐患评估里绕不开的一环。灰烬区域不像火焰那样有鲜艳颜色也不像烟雾那样有明显运动轨迹它在可见光图像里呈现为大面积暗色斑块边界模糊、纹理稀疏和土壤、阴影高度相似通用检测模型在这类场景下经常把灰烬漏掉或者把翻耕土地错认成灰烬。这份「田间农作物焚烧灰烬数据集yolovoc格式221张3标签.zip」解决的就是这个问题用小规模、双格式、带明确类别划分的灰烬样本让从业者能快速验证检测思路摸清模型在自己的监控视角下能跑到什么程度。221张图在深度学习里算典型的小样本但小样本不等于不能落地。这类数据集的价值在于标注质量和类别划分清晰双格式交付省去了迁移训练的转换成本适合两类人一是想做秸秆焚烧识别但手里没有标注样本的算法工程师二是需要快速做可行性验证、给上级或客户出结论的项目负责人。下文我会从这个数据集的格式拆解讲起一路写到训练命令、标签转换脚本和实际巡检部署时会踩的坑照着做就能在本地跑通一条完整的灰烬检测流水线。2. YOLO与VOC双格式先读懂目录结构与坐标体系差异2.1 两种标注格式的本质区别拿到压缩包解压之后第一件事不是急着训练而是搞清楚数据组织方式。常见做法是根目录下同时存在yolo/和voc/两个子目录分别对应两种标注体系也可能只放一份图片、两份标注文件。YOLO格式是归一化坐标每行一条目标记录类别索引、中心点x、中心点y、宽、高所有数值都除以图片宽高归一化到 0 到 1VOC格式则是原生像素坐标每张图对应一个 XML 文件里面用xmin/ymin/xmax/ymax四个绝对值描述目标的边界框。这两种格式在数学上是同一件事但实际使用时有三个关键差异第一YOLO 的归一化坐标依赖图片原始宽高图片被缩放后标注无需重算VOC 的像素坐标在训练时要在数据集加载代码里做一次除以宽高的换算很多训练脚本对这一步的处理很隐晦。第二YOLO 的类别索引严格按训练配置里的names顺序排列0对应第一个类别VOC 则靠 XML 里的name标签对应类别名索引顺序完全由解析脚本决定两者一旦错位训练出的模型会出现所有框都偏移一个类别的诡异现象。第三两种格式对空标注文件的处理逻辑不同。YOLO 格式里没有标注的图片就不生成 txt 文件训练脚本遇到缺 tensor 也能容忍VOC 格式则通常要求每张图都有对应 XML空标注也要写object空列表。理解这些差异之后排查导入阶段的问题就有方向了。提示解压后先看目录里有没有classes.txt或names.txt这是确认标签顺序的唯一权威来源。不要凭文件名猜测类别顺序。2.2 快速检查数据完整性的命令我一般拿到数据集会先用一段轻量脚本检查文件数量、标注格式和坐标范围把问题在训练前暴露出来。下面这段代码用 python 实现不依赖任何深度学习框架。import os from pathlib import Path from PIL import Image root Path(田间农作物焚烧灰烬数据集) yolo_dir root / yolo voc_dir root / voc # 1. 统计各目录文件数 img_files list((root / images).glob(*.jpg)) yolo_txts list((yolo_dir / labels).glob(*.txt)) voc_xmls list((voc_dir / Annotations).glob(*.xml)) print(f图片数: {len(img_files)}, YOLO标签数: {len(yolo_txts)}, VOC标签数: {len(voc_xmls)}) # 2. 检查YOLO标注坐标是否越界 bad_count 0 for txt in yolo_txts: for line in txt.read_text().strip().splitlines(): vals line.split() if len(vals) ! 5: print(f非法行: {txt.name}: {line}) bad_count 1 continue cls int(vals[0]) x, y, w, h map(float, vals[1:]) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): print(f坐标越界: {txt.name}: {line}) bad_count 1 print(f发现 {bad_count} 处异常标注)这段脚本的逻辑分两步先核对图片与两种标注的文件数量是否匹配再逐行解析 YOLO 标注检查类别索引是否在合法范围、坐标是否超出 [0,1] 区间。参数上要注意glob只匹配.jpg后缀如果数据集用了.png或.jpeg需要同步改0 x 1的严格判断会过滤掉个别标注工具导出的 0 或 1 边界值这些值在训练时常导致 resize 后框体偏移建议宁可误报也别放过。文件数量核对的预期是图片数等于 YOLO 标签数且 VOC 的 XML 数与图片数一致两份数量对上之后才进入下一步。221 张图这个规模标签数如果不一致往往是标注工具漏标或者转换脚本过滤过小目标导致的这种差异会直接影响训练时的正样本数量必须提前定位。2.3 标签类别与先验场景的对应关系数据集自带 3 个标签这在焚烧识别的任务里是常见配置。行业里做田间灰烬检测类别划分一般逃不开三种思路按燃烧状态分明火、烟、灰烬、按燃烧对象分秸秆灰、落叶灰、杂草灰、按监控价值分需处理灰烬、可自然降解灰烬、干扰物。这份数据集的 3 标签极大概率是前两种思路之一具体类别名以压缩包里的classes.txt为准。拿到classes.txt后建议立刻和图片内容对照一眼确认类别在视觉上的区分度。有的数据集把「灰烬」和「阴影」分成两类有的把不同燃烧程度的灰烬合并成一类类别粒度的粗细直接影响模型训练难度——类别越细221 张图的单类样本数越少越容易过拟合。如果发现类别语义和你实际业务场景差别大比如你的监控只要区分「有无焚烧痕迹」那可以把三个类别合并成二分类重新跑这种改造在 YOLO 格式里只需要改标签文件的第一列成本极低。3. 用YOLO格式直接启动训练数据配置与最小命令3.1 目录重排与数据配置文件YOLO 系列训练脚本对数据目录结构有严格要求常见要求是 images 和 labels 目录同级且 train/val 子目录按比例划分。这个数据集的原始目录可能没有按训练要求排好我一般会在项目目录下重建dataset/结构把图片和标注按 8:2 划分到 train 和 val。221 张图的分割建议训练集保留 176 张验证集 45 张如果类别不均衡优先保证每个类别在验证集里至少出现 3 次。划分完目录后写一个data.yaml配置文件作为训练入口三行核心配置就能让脚本跑起来。path: ./dataset # 数据集根目录 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 names: 0: burn_ash 1: fire_point 2: smokepath字段用相对路径并基于训练启动时的工作目录解析避免换机器后绝对路径失效。names的索引必须和标签文件的第一列严格对应这是整个训练流程里最不能出错的地方。如果数据集的真实标签名和示例不一致直接改成classes.txt里的原样名称顺序也不要调整。3.2 从预训练权重开始的最小训练命令221 张图从零训练不现实必须加载 COCO 预训练权重做迁移学习。YOLO 目标检测框架的训练脚本统一提供了pretrainedTrue选项一行命令即可启动。yolo detect train \ datadataset/data.yaml \ modelyolov8s.pt \ epochs80 \ imgsz640 \ batch8 \ workers4 \ pretrainedTrue \ projectrun_ash \ nameash_det_exp1参数选择上有几个考量。yolov8s.pt是 s 版本参数量小适合小数据集如果用 l 或 x 版本221 张图的样本量撑不住复杂模型的拟合很容易在验证集上震荡epochs80是折中值小数据集一般 50 到 100 轮足够收敛超过 100 轮大概率开始过拟合imgsz640保持默认田间监控图一般分辨率较高640 的输入既能保留灰烬细节又不至于让显存爆掉batch8按 8G 显存左右的经验值设置显存不够直接降到 4。训练过程中的观察点有两个。一是每个 epoch 输出的box_loss和cls_loss灰烬类别容易和背景混淆cls_loss下降缓慢是正常现象不要因为前 10 轮没明显变化就中断。二是验证集的mAP50灰烬这种大斑块目标通常 mAP50 比 mAP50-95 高 10 个百分点以上这是预期内的不必焦虑。3.3 验证集评估指标的重点读法训练结束后脚本会输出results.csv里面记录了每轮的精确率、召回率和 mAP。田间灰烬检测任务里召回率比精确率重要。原因很实际监控巡检场景下漏报一次焚烧灰烬可能造成后续处理滞后而误报一个深色土壤块最多让巡查人员多跑一趟。recall低于 0.7 时优先考虑增加训练轮数或做数据增强而不是调 confidence 阈值precision低则可以在推理阶段提高阈值过滤误检。另一个值得关注的是每个类别的单独指标YOLO 训练脚本默认输出所有类别的汇总值。灰烬类别如果 AP 明显低于其他类别说明这个类别的样本在 221 张图里占比不足或形态差异过大此时考虑对灰烬类别的样本做特定增强这部分在最后一章展开。4. VOC格式转YOLOXML解析脚本与索引对齐的完整写法4.1 为什么需要自己写转换脚本数据集的 VOC 目录交付的是标注 XML但很多训练框架的加载接口对 VOC 格式支持不如 YOLO 原生格式顺手。自己动手写转换脚本有额外好处可以在转换过程中过滤掉非法框、统一类别索引顺带检查一遍标注质量。尤其当你要把这份数据集和自采数据合并训练时统一成 YOLO 格式是减少后续麻烦的唯一稳妥路径。我见过不少人直接用在线转换工具或可视化标注软件自带的导出功能结果是类别顺序被打乱、坐标中心点算错、边界框被错误裁剪。格式转换这件事必须可控可复核脚本里跑出来的中间统计结果能反查问题这是工具做不到的。4.2 一个健壮的XML转YOLO脚本下面这个脚本覆盖了解析、归一化、过滤、写入四个环节并加入了针对常见标注脏数据的保护逻辑。import xml.etree.ElementTree as ET from pathlib import Path from PIL import Image voc_dir Path(voc/Annotations) img_dir Path(voc/JPEGImages) out_dir Path(yolo/labels_converted) out_dir.mkdir(parentsTrue, exist_okTrue) # 类别顺序必须和训练时data.yaml的names一致 class_names [burn_ash, fire_point, smoke] class_index {name: i for i, name in enumerate(class_names)} def convert_xml(xml_path, img_path): tree ET.parse(xml_path) root tree.getroot() with Image.open(img_path) as img: img_w, img_h img.size lines [] for obj in root.findall(object): name obj.findtext(name) box obj.find(bndbox) if box is None: print(f跳过无框目标: {xml_path.name}) continue xmin float(box.findtext(xmin)) ymin float(box.findtext(ymin)) xmax float(box.findtext(xmax)) ymax float(box.findtext(ymax)) # 过滤非法框与过小目标 if xmax xmin or ymax ymin: print(f坐标倒置: {xml_path.name}) continue w, h xmax - xmin, ymax - ymin if w 5 or h 5: print(f目标过小已过滤: {xml_path.name}) continue # 防止越界后坐标比仍超过1 x_center max(0, min((xmin xmax) / 2 / img_w, 1)) y_center max(0, min((ymin ymax) / 2 / img_h, 1)) norm_w max(0, min(w / img_w, 1)) norm_h max(0, min(h / img_h, 1)) lines.append(f{class_index[name]} {x_center:.6f} {y_center:.6f} {norm_w:.6f} {norm_h:.6f}) return lines for xml_path in voc_dir.glob(*.xml): img_path img_dir / (xml_path.stem .jpg) if not img_path.exists(): print(f缺图: {xml_path.name}) continue lines convert_xml(xml_path, img_path) (out_dir / (xml_path.stem .txt)).write_text(\n.join(lines))脚本的核心逻辑分四段。第一段读取 XML 并解析出图片真实宽高注意这里必须用PIL读图获取实际尺寸不能信任 XML 里的size字段因为标注工具有时写入的尺寸和图片实际尺寸不一致。第二段遍历object节点依次提取类别名和四角坐标。第三段的过滤逻辑很关键w 5和h 5的像素级小目标被过滤因为这类目标归一化后不足图片宽度的 1%训练时几乎无法学到有效特征留着反而增加收敛难度。第四段做归一化并裁剪到 [0,1] 区间防止个别标注框超出图片边界后产生大于 1 的异常值。class_names列表的书写顺序就是这个脚本的生命线。转换前先打开数据集里的classes.txt或随便打开一个 XML 看类别名的原始拼写比如类别名在 XML 里是fire而脚本里写成fire_pointclass_index[name]会直接抛KeyError虽然报错比训练时类别错位更直观但也要避免跑到一半才发现。转换完成后抽查 5 到 10 个 txt 文件把坐标反算回像素坐标画框对比原图这一步能拦住 90% 的格式转换错误。4.3 转换后与原YOLO目录的合并策略如果数据集本身已经带了一套 YOLO 标签通常这里面会有一个细节标注软件导出或人工整理的 YOLO 标签和 VOC 转换出来的标签存在轻微差异比如某个目标被手动微调过。我在实际项目中遇到过一次两边坐标对同一个目标偏了 3 到 5 个像素。合并时不要盲目覆盖。常见做法是先对比同一图片的 txt 文件把差异超过阈值的图片挑出来人工确认。比较脚本可以写得很简单解析两组坐标的左上角点计算欧氏距离超过图片宽度的 2% 就标记。这一步虽然繁琐但能避免把已经检查过的标注质量倒退到不可信状态。最终以质量更好的一份为准另一份移入备份目录而不是直接删除留个后悔药。5. 数据检查避坑重复框、空标签与类别对齐的排查记录5.1 训练时loss正常但所有类别都没预测出来现象训练跑完 80 轮验证集 mAP 一直为 0推理时图片上画不出任何框但 loss 曲线下降趋势正常。检查数据配置和标注文件都看不出明显问题。原因这是一次典型的类别索引错位事故。数据集的classes.txt里类别顺序是ash, smoke, fire而我的data.yaml里写成了fire, ash, smoke。YOLO 训练时把标签第一列的0解析成fire但标注文件里的0原本对应ash。模型的分类头和标签语义对不上loss 虽然在降拟合的是错误的映射关系推理输出自然全是错的。解决把data.yaml里的names顺序改成和数据集的classes.txt完全一致删除runs目录重新训练。此后我养成一个习惯任何数据集进来先把classes.txt打印出来贴到训练笔记里核对无误再开工。这一步在双格式数据集里特别容易踩中因为 VOC 的 XML 里有类别名字面量转换脚本写错会直接报错但 YOLO 的 txt 只有数字索引错得毫无提示。5.2 灰度图导致训练早期崩溃现象训练在第一个 epoch 报错提示图片通道数不一致或者Image.open出来的图是L模式。查看数据后发现有少量图片是单通道灰度图。原因数据集制作过程中部分图片来源可能是黑白监控截图或经过压缩的灰度帧这些图在三通道模型输入时会触发预处理异常。人工看缩略图时很难注意到因为灰度图和彩色图在浏览器的缩略图下几乎一模一样。解决统一做 RGB 三通道转换。在数据处理阶段加一步检查扫描所有图片的mode发现L模式就用img.convert(RGB)保存为 RGB 副本覆盖原文件或另存统一目录。转换后再核对一遍图片尺寸是否全部一致因为 YOLO 训练脚本会统一 resize尺寸不一致一般不影响训练但个别超宽或超高图会改变长宽比语义影响灰烬这种形状特征不明显的目标。5.3 221张图里有重复样本现象训练过程中验证集指标异常高训练集 loss 几乎降到 0但把模型拿到真实监控视频里测试漏检率超过预期。排查时发现数据集里有两张图片名字不同、内容完全相同。原因小数据集制作时常见流程是从一段视频里抽帧抽帧脚本如果没做去重相邻两帧画面几乎一样就会被同时标进数据集。训练集和验证集分别落入相似帧时模型见到的验证样本和训练样本高度相似指标虚高真实场景连续画面是全新的模型立刻露馅。解决先做全局去重。用fdupes这类工具或写一个基于文件哈希的去重脚本找出内容相同的图片结合标注文件判断保留哪一份。去重后再看训练集和验证集的图片是否来自同一视频片段如果是按时间间隔抽帧而不是随机划分保证验证集的时间分布和训练集分开。这个坑在小数据集上尤其致命221 张图里只要有 10 张重复图验证集的有效性就大幅缩水。5.4 标注框里包含大量背景现象模型 mAP 不差但预测的框总是比实际灰烬区域大一圈边界不贴合无法用于后续面积估算。原因查看原始标注发现部分图片里灰烬区域和周围的深色土壤在视觉上连成一片标注人员框选时直接把两片区域框在一起。灰烬检测在业务上常要估算过火面积框不贴合就会让面积计算误差达到 30% 以上。解决这种问题在 221 张的小数据集里没有快速修复路径只能人工逐张复核。我一般采取折中策略先保留原始标注训练一版观察模型输出框的形状分布如果偏移方向一致后续自采数据时统一标注规范——只框灰烬本体不框外围过渡带。样本里标注风格的一致性对检测质量的影响在小数据集上甚至比数量更大。5.5 VOC转换时误伤了旋转目标现象XML 转 YOLO 后原 VOC 里用robndbox表示的旋转标注框全部消失训练后模型对斜向灰烬区域的检测丢失。原因VOC 标准格式里的bndbox是正矩形而部分标注工具为了贴合田间垄沟方向用了带角度的旋转框robndbox。转换脚本只遍历findall(object)下的bndbox没处理可选的robndbox旋转目标被静默丢弃。解决检查 XML 里是否存在robndbox节点存在就把它视作外接正矩形取旋转框的四角坐标的min/max作为正框。虽然这样框会略大至少目标不丢。如果业务场景需要精确贴合只能找支持旋转框的检测模型YOLO 系列的常规版本不支持旋转框这也是字段层面的边界。6. 把灰烬检测落地到实际巡检提升mAP的几个小技巧到了真正跑现场监控的阶段训练集里的 221 张图远不够覆盖真实光照和视角变化。我常用的做法是先用测试时增强TTA压出当前模型的真实上限。YOLO 训练框架默认不带 TTA 推理但常见推理入口的augmentTrue参数可以开启多尺度与翻转融合灰烬检测对翻转不敏感用 TTA 能稳定提升 1 到 3 个点的 mAP。TTA 确认模型潜力达标后再决定是否值得投入精力扩充数据。扩充数据不必急着采集新图。田间灰烬在不同光照下颜色从灰白到深黑变化先用 HSV 空间的饱和度抖动和亮度抖动模拟晨昏差异比盲目增加样本更有效。我在自采数据时发现同样的灰烬区域在正午顶光和傍晚低角度光下拍摄模型输出置信度能相差 0.2 以上而饱和度抖动能把这部分方差提前压进训练集。另一个有效技巧是随机裁剪拼接把 221 张图里的小灰烬区域裁剪后拼到无灰烬的背景图里既增加样本数又修正类别不均衡。部署阶段最实用的设置是 confidence 阈值分开调。前端巡检画面里灰烬类别的误检多数来自深色土壤、树影和收割后的麦茬地这类误检的置信度通常集中在 0.3 到 0.5 之间而真正灰烬的置信度往往超过 0.7。我一般把置信度阈值设在 0.25 保召回再对低于 0.5 的框加一个「必须是暗色且纹理均匀」的后处理规则用 OpenCV 算框内区域的灰度标准差低于阈值的判为疑似目标推送复核。这套组合拳既保住了漏报率又没让误报淹没值班人员。边界情况里最需要注意的是雨后场景。灰烬被雨水冲刷后会呈现完全不同的纹理特征颜色变浅、边界更模糊和干灰烬几乎不是同一个类内分布。如果业务覆盖雨季建议单独采集雨后的灰烬样本或者在推理链路里加入天气判定降雨时段临时提高置信度阈值并标记为低可信结果。最后说一个我自己的教训小数据集训练时不要迷信 mAP 数字。我第一次跑这个 221 张双格式数据集验证集 mAP50 到了 0.86兴冲冲拿去测试现场视频连续漏掉三处灰烬原因就是验证集划分过于随机和训练集画面相似度过高。后来我把划分逻辑改成按视频片段切分指标掉到 0.74但真实场景表现反而更稳。数据集标注质量再怎么谨慎都不为过因为这决定了下游每一步的上限希望这篇笔记能帮你在灰烬检测这条路上少走一段弯路。本文还有配套的精品资源点击获取