民启特种作业 · 安阳新乡特种作业考证咨询

首页 安阳报考专题 新乡报考专题 报名流程 考试批次 低压电工作业 熔化焊接与热切割 高处安装维护拆除 叉车司机 塔吊司机 新闻资讯 证书查询核验 证书复审 企业团报 在线预约 关于我们 联系我们
电话咨询 18236992212
首页新闻资讯文章详情

资讯详情

考试通知、政策法规、备考经验、行业动态,为安阳、新乡特种作业考证人员提供信息参考。

首页新闻资讯打场晒粮检测数据集实战:VOC转YOLO格式与YOLO模型训练指南

打场晒粮检测数据集实战:VOC转YOLO格式与YOLO模型训练指南

2026/10/12 11:46:36 民启特种作业 安阳 · 新乡考证资讯
打场晒粮检测数据集实战:VOC转YOLO格式与YOLO模型训练指南 简介在智慧交通与城市治理中对道路打场晒粮行为的智能监控需求日益增长而高质量标注数据是训练可靠检测模型的基础。为此智慧交通城市道路打场晒粮检测数据集配套说明文档面向计算机视觉与目标检测方向的开发者、研究人员及智慧交通项目实践者提供完整的数据集使用指引。文档详细列出了1065张jpg图片、1065个VOC格式xml标注文件与1065个YOLO格式txt标注文件共1个类别shailiang晒粮标注框总计1245个同时说明了labelImg工具与矩形框标注规则。读者依据文档可清晰掌握数据目录结构和标注格式直接用于YOLO、Faster R-CNN等主流检测模型的训练与评估。资源包为单个docx文件大小约2.32MB内容紧凑目前已获63人学习适合需要快速获取道路场景监测数据集并进行算法验证的初学者和进阶研究者能有效节省数据准备与格式转换时间。1. 1065张打场晒粮数据能解决什么问题智慧交通里的一个细分刚需七八月份的傍晚你开车经过城市环线辅路经常能看到非机动车道上摊满了刚收割的麦子旁边用几块石头、两个锥桶就算圈地。路政巡逻车需要自动识别这种打场晒粮行为而不是靠人工下车劝阻——这就是这个数据集存在的意义。标题里的1065张、1类别、VOCYOLO双格式听起来规模不大但恰恰是智慧交通细分场景里最常见的数据形态单类目标、实拍路面、标注边界清晰拿来做算法验证和模型训练完全够用。适合的人群是接智慧城市、路政巡检、交警违法抓拍项目的算法工程师以及想快速评估“打场晒粮检测到底能不能落地”的从业者。2. 打场晒粮检测的核心场景与数据集设计逻辑2.1 场景特征为什么城市道路上晒粮是“移动的障碍物”打场晒粮和常规交通目标检测最大的区别在于这个目标不是“物体”而是一片“区域”。麦粒、玉米粒摊开后颜色与沥青路面接近边界没有硬轮廓光线一变就和路面融为一体。再加上车辆经过会碾压、扬尘粮食区域在视频里是不断变形的。这就是为什么用通用检测模型容易翻车——COCO预训练模型见过人、车、猫狗但没见过“地上摊一片粮食”。另一个难点在于尺度跨度大。近距离俯拍时晒粮区域可能比一辆公交车还大远距离抓拍时它又可能只是几十个像素的浅色斑块。同一个类别尺度相差几十倍对检测头的感受野要求很高。所以数据集在设计时必须覆盖不同拍摄距离、不同光照、不同路宽下的样本否则训练出来的模型换个路段就废。2.2 1类别标注的边界哪些算、哪些不算这个数据集定义为一个类别标注对象是“打场晒粮区域”也就是粮食摊铺的实际范围。这个范围一般以粮食连续覆盖区域为准如果粮食中间被车辆压出几条路、露出沥青只要整体还是连片的就仍然归为一个框。晒粮边缘的石头、木棍、塑料布、锥桶不属于目标本身不需要单独标注因为它们只是辅助标记。那有人会问为什么不标注行人、车辆、路障因为检测任务的目标非常明确——发现晒粮行为。标得越杂类别不平衡越严重模型反而学不到核心特征。单类别的价值在于把“晒粮区域”和“正常路面”区分开这是智慧交通场景里最容易标注一致性出问题的地方。标注人员如果拿不准一块淡色区域到底是粮还是反光就需要看纹理和颗粒感而不是只看颜色。2.3 VOCYOLO双格式的目录结构与标签对应VOC格式适合做语义检查、数据可视化、用旧版检测框架跑训练YOLO格式是当前主流检测框架直接吃的输入。数据集同时给两种格式省去了自己转换的时间但目录结构必须看明白不然训练脚本里路径写错一步后面全是白费。常见的双格式目录结构是这样的dataset/ ├── VOCdevkit/ │ ├── VOC2007/ │ │ ├── JPEGImages/ # 1065张jpg图片 │ │ ├── Annotations/ # 1065个xml标签 │ │ └── ImageSets/ │ │ └── Main/ # train.txt / val.txt 等划分 └── yolo_format/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamlYOLO的标签文件是txt每一行对应一个目标格式是class_id x_center y_center width height。所有坐标都是相对于图片宽度和高度的归一化值范围0到1。VOC的xml里存的是绝对值比如左上角xmin、ymin和右下角xmax、ymax训练前必须先搞清楚两种格式的换算关系。如果数据集里只有VOC标签就需要自己转如果两种都有最好抽一个xml和对应的txt对一下验证转换的人有没有把坐标写对。3. 数据集到手后的第一件事校验、可视化与划分3.1 用Python统计图片数、标注框数与异常值很多人拿到数据集直接就开始训练这是最浪费时间的做法。我用一个脚本先做统计看看图片和标签数量是否一致、框的坐标有没有越界、类别id是否正确。对VOC格式我一般遍历所有xml把每张图的文件名、目标数和坐标范围打出来凡是xmin小于0、xmax大于图片宽度、或者宽度高度为0的都要标记出来。import os import xml.etree.ElementTree as ET xml_dir VOCdevkit/VOC2007/Annotations img_dir VOCdevkit/VOC2007/JPEGImages for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 读取图片实际尺寸用于后续判断坐标是否越界 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) obj_count 0 for obj in root.iter(object): bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) obj_count 1 # 越界或非法尺寸直接打印这种数据要么删要么修 if xmin 0 or ymin 0 or xmax img_w or ymax img_h or xmax xmin or ymax ymin: print(f{xml_file}: invalid box ({xmin}, {ymin}, {xmax}, {ymax})) # 顺便统计单张图片目标数看看有没有异常稀疏或密集的样本 print(f{xml_file}: {obj_count} objects)这段脚本的核心逻辑是“读尺寸、查越界、看数量”。坐标越界的框在YOLO训练时会被截断或直接忽略导致模型学到错误边界。单张目标数过少的图片要确认是不是漏标目标数特别多的图片则可能对Loss产生过大的贡献需要在训练时通过权重或者采样来平衡。跑完这个脚本你就知道数据集能不能直接用了。3.2 可视化标注框确认标注质量统计只能发现数字层面的异常真正的质量问题得用眼睛看。我一般会把VOC的xml和对应图片叠加画框生成一批可视化图随机抽50张左右人工扫一遍。重点看三件事框是不是紧紧贴着晒粮区域边缘框有没有把粮食和旁边的路面混在一起以及明显是晒粮的图片是不是被漏标了。import cv2 import xml.etree.ElementTree as ET import os xml_dir VOCdevkit/VOC2007/Annotations img_dir VOCdevkit/VOC2007/JPEGImages out_dir check_vis os.makedirs(out_dir, exist_okTrue) for xml_name in os.listdir(xml_dir)[:50]: # 抽查前50个 xml_path os.path.join(xml_dir, xml_name) img_name xml_name.replace(.xml, .jpg) img_path os.path.join(img_dir, img_name) img cv2.imread(img_path) if img is None: print(fmissing image: {img_name}) continue tree ET.parse(xml_path) root tree.getroot() for obj in root.iter(object): bndbox obj.find(bndbox) xmin int(float(bndbox.find(xmin).text)) ymin int(float(bndbox.find(ymin).text)) xmax int(float(bndbox.find(xmax).text)) ymax int(float(bndbox.find(ymax).text)) # 画红色矩形厚度2方便看是否贴合 cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 0, 255), 2) save_path os.path.join(out_dir, img_name) cv2.imwrite(save_path, img) print(f可视化图片已保存到 {out_dir})运行后打开输出目录你会看到一个很直观的问题有些框把半条马路都包进去了有些框只框住了粮食的核心区域但漏了边缘薄薄的一层。这两种情况都会影响训练效果——前者让模型觉得晒粮区域就是“大面积任意区域”后者让模型学不到完整边界。可视化不是为了赏心悦目是为了判断标注人员的标准是否统一。3.3 训练/验证/测试集划分的关键比例模型训练前必须划分数据集这个动作看起来简单但很多人在这里犯低级错误。常见比例是8:1:1或7:2:1关键是划分时要保证同一个场景的连续帧不要同时出现在训练集和验证集里否则模型等于是“开卷考试”。我自己一般先用脚本把图片按拍摄场景分组再对组进行随机划分而不是对单张图片直接随机。import os import random import shutil img_dir VOCdevkit/VOC2007/JPEGImages xml_dir VOCdevkit/VOC2007/Annotations train_img yolo_format/images/train val_img yolo_format/images/val train_lab yolo_format/labels/train val_lab yolo_format/labels/val os.makedirs(train_img, exist_okTrue) os.makedirs(val_img, exist_okTrue) os.makedirs(train_lab, exist_okTrue) os.makedirs(val_lab, exist_okTrue) imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(imgs) val_count int(len(imgs) * 0.1) # 10%做验证集 for i, img_name in enumerate(imgs): xml_name img_name.replace(.jpg, .xml) src_img os.path.join(img_dir, img_name) src_xml os.path.join(xml_dir, xml_name) if i val_count: dst_img_dir val_img dst_lab_dir val_lab else: dst_img_dir train_img dst_lab_dir train_lab # 复制图片到目标目录 shutil.copy(src_img, dst_img_dir) # 如果存在对应xml转换后再复制 if os.path.exists(src_xml): # 这里省略VOC转YOLO细节下一章专门讲 pass这里的比例逻辑是先取10%作为验证集剩下90%全部进入训练集。测试集在数据量只有1065张的情况下可以不单独分直接用验证集做最终评估或者从训练集里再切一部分。如果数据集本身带了ImageSets/Main里的划分文件优先用它的划分因为发布者已经考虑过场景重复问题。如果你发现验证集里出现和训练集高度相似的图片就要怀疑划分是否合理。4. VOC转YOLO格式的最小脚本一次跑通并避开4个坑4.1 XML转TXT的最小脚本虽然数据集标题里写着VOCYOLO双格式但实际拿到手很可能只有VOC格式或者YOLO格式并不完整。我一般会自己写一个转换脚本十几行就能跑通顺便把标签体系捋清楚。转换的核心就是读取xml里的目标框换算成归一化坐标写进txt。import os import xml.etree.ElementTree as ET classes [sun_drying_grain] # 类别列表顺序决定类别id def xml_to_yolo(xml_path, txt_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() with open(txt_path, w) as f: for obj in root.iter(object): # 类别名必须出现在classes里否则跳过 cls obj.find(name).text if cls not in classes: continue cls_id classes.index(cls) bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 归一化中心点坐标和宽高都除以图片宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h # 限制在0~1之间防止越界 x_center max(0, min(x_center, 1)) y_center max(0, min(y_center, 1)) width max(0, min(width, 1)) height max(0, min(height, 1)) f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n) # 示例单张图片转换 xml_to_yolo(VOC2007/Annotations/001.xml, yolo_format/labels/train/001.txt, 1920, 1080)这段脚本没有处理嵌套目录和批量循环但核心换算逻辑是完整的。注意我在这里做了坐标截断到0~1区间防止有些xml里坐标略微超出图片边界导致训练时报错。如果你拿到的数据集里图片尺寸不统一每一张都要从xml里读真正的宽高不能用脚本里写死的1920×1080。4.2 坑1类别索引从0开始别写成1VOC的xml里存的是类别“名字”比如sun_drying_grain、person、car而YOLO格式存的是类别“索引”。很多人刚转格式时习惯性把第一个类别写1第二个写2结果训练时类别id和数据集YAML里的names列表对不上模型永远学不会正确分类。YOLO的类别id严格从0开始如果你的数据集只有1个类别那这个类别永远是0。另一个隐蔽问题在数据集YAML里如果names写成[sun_drying_grain]YOLO会把这张图片里所有标记为类别0的框当作唯一目标。如果你转换脚本里写了1而names只有一个那所有框都会被当成非法类别直接丢弃训练结束mAP为0你还根本找不到原因。排查方法很简单打开任意一个yolo标签txt看第一列数字是不是都在0到nc-1范围内。4.3 坑2归一化坐标要保留足够精度VOC里存的是整数像素坐标比如xmin632img_w1920换算后x_center可能是0.385416667。如果你用字符串拼接写个0.3854精度损失会在小目标上被放大——一个只有20像素宽的晒粮区域归一化宽度是0.01左右写4位小数就直接变成0.0104或者0.0099框位置偏了半个目标。我习惯统一用f{x_center:.6f}保留6位小数这样坐标误差在1920分辨率下小于0.002像素完全够用。还要注意整数除法在Python2里的坑现在用Python3已经不存在但如果你在别人的旧脚本里看到x_center (xmin xmax) / 2 / img_w一定要确认结果是浮点数否则除完全是0。4.4 坑3图片与标注文件名必须严格同名YOLO训练框架通过图片路径查找对应标签默认规则是图片在images/train/abc.jpg标签必须在labels/train/abc.txt。后缀名不同没关系但主文件名必须完全一样包括大小写。有些数据集发布者整理时图片叫IMG_20230718_123456.jpgxml却叫img_20230718_123456.xml转换后标签文件名和图片对不上训练时直接跳过这张图你还没提示。检查方法是用脚本对比两个目录下的文件名集合把不一致的打印出来。我吃过一次亏当时有20多张图片因为后缀jpeg和jpg不统一导致标签匹配不上整个验证集的mAP低了十几个点。养成习惯转换后跑一个文件名一致性检查比埋头训练省时间。4.5 坑4空图片与负样本的处理打场晒粮检测场景里图片里没有目标的情况很少但确实存在——比如巡检车拍到一段没有晒粮的干净路面。VOC格式对空图片一般没有对应xml而YOLO格式要求每张图片都有对应txt没有目标就留一个空文件。很多人在转换时只处理有xml的图片导致部分图片在images/里存在但labels/里没有对应txt训练时框架会报错或跳过。负样本要不要加入训练要看你的业务需求。如果你的部署环境是“日常巡检”大部分时间路面是干净的那负样本能显著降低误检率。做法是把没有目标的图片复制进训练集在labels目录下建立一个空的txt文件。注意不要把所有空图片都扔进验证集否则验证集的mAP会失真——因为验证集全是负样本模型只要什么都不检就能拿到高精度。5. 训练参数与常见问题排查mAP上不去的5个原因5.1 数据集YAML的写法与路径配置YOLO系列的训练框架无论是v5还是v8第一件事都是读一个数据集YAML文件。这个文件定义了训练集和验证集的路径、类别数量、类别名字。路径写错是最常见的翻车现场尤其是相对路径和绝对路径混用导致换一台机器训练就要改文件。path: /data/sun_drying_grain # 数据集根目录 train: images/train # 相对于path的训练图片目录 val: images/val # 相对于path的验证图片目录 nc: 1 # 类别数量必须和labels里出现的一致 names: 0: sun_drying_grain # 0号类别对应的名称这里的path字段如果你写相对路径建议直接填./然后保证执行训练命令时当前目录在数据集根目录上一级。或者干脆写绝对路径虽然换机器要改但至少不会玄学找不到文件。nc: 1如果写错成2而标签里只有类别0训练过程不会报错但验证时mAP会始终在0附近徘徊因为模型以为有一个从没见过的类别1。5.2 训练命令与关键超参数选择数据量只有1065张模型选择上不必一上来就上最大的YOLOv8x那样不仅容易过拟合显存也浪费。常见的做法是用YOLOv8s或者YOLOv5s输入分辨率设为640就够了。训练命令里几个参数是必调的我一般这样跑yolo detect train \ datadata.yaml \ modelyolov8s.pt \ imgsz640 \ batch16 \ epochs100 \ patience20 \ lr00.01 \ cacheTruebatch16在单卡16G显存下比较稳如果你只有8G显存降到8或者4。patience20表示如果验证集mAP连续20轮不提升就提前停止防止后期过拟合。lr0是初始学习率数据集小时不要用默认的0.01以外的激进值不然Loss前几轮就爆炸。cacheTrue把图片预加载到内存1065张图吃几个G内存但训练速度能快不少。5.3 典型踩坑小目标漏检、密集堆积导致框乱跳打场晒粮最常见的两个训练问题一是远距离小目标漏检二是近距离大片粮食被模型拆成多个小框。先说小目标imgsz640意味着把原始图缩到640原本只有20像素的晒粮区域直接缩成4像素基本不可能检测到。我一般会把imgsz提到960或1280但显存不够时只能保持640然后用TTA或切图推理。密集堆积的乱跳问题根源是NMS阈值。一个连续晒粮区域模型可能输出十几个高置信度的候选框NMS默认iou阈值0.5会把它们合并成一个框但合并后框的尺寸和位置不稳定连续帧之间忽大忽小。这里没有魔法参数建议把标签本身的框画出来看如果标注人员是把整个晒粮区域标成一个框模型学到的就是整体如果标注人员标了多个小框模型就会输出多个框。标注一致性比调参更重要。5.4 排查思路loss、mAP、PR曲线联合看训练完不是只看一个mAP数字就结束要联合Loss曲线和PR曲线判断模型真正的状态。下面是一张排查表是我实际用的时候整理的遇到问题先对号入座。现象可能原因处理方式训练Loss不降学习率过大或过小、标签坐标异常查看lr曲线调低lr0重跑检查txt归一化值是否越界验证mAP始终为0类别索引错、YAML的nc和names不匹配、标签路径不对打印一张标签txt确认第一列是0核对目录结构PR曲线P高R低模型过于保守框只有高置信度才输出降低conf_thres检查是否有漏标样本PR曲线R高P低误检严重模型把路面裂缝、阴影当目标增加难负样本降低模型对低置信度的容忍Loss下降但验证Loss反弹过拟合数据量小加数据增强、降低epoch、增大验证集比例这张表排查顺序有讲究先确认数据没问题再调学习率最后才动NMS和后处理。很多人一上来就调置信度阈值结果越调越偏。6. 从测试集到路段落地两个可操作的验证技巧6.1 连续视频帧测试漏检率单张图片的mAP是静态指标但打场晒粮在视频里是动态目标。巡检车低速行驶时一个晒粮区域会出现在连续几十帧里只要其中一帧检出就有时间去处理。我习惯用视频抽帧的方式测试从一段真实巡检视频里每隔3帧取一张对每张都做推理然后数“目标实际出现的帧数”和“被检出的帧数”算出漏检率。这个指标比mAP更能反映部署后的真实体验。import cv2 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) cap cv2.VideoCapture(road_patrol.mp4) total_frames 0 miss_frames 0 while True: ret, frame cap.read() if not ret: break total_frames 1 results model(frame, conf0.35, imgsz960) # 假设这段视频每一帧都有晒粮区域如果检测框为空就计为漏检 if len(results[0].boxes) 0: miss_frames 1 cap.release() print(f漏检率: {miss_frames / total_frames:.2%})注意conf0.35是经过调参后的置信度阈值实际部署时宁缺毋滥因为误报一次会让路政人员白跑一趟。视频测试的价值在于你能看到同一个目标在不同帧里的置信度波动如果连续几帧都稳定在0.4以上那阈值可以适当提高如果忽高忽低说明模型对光照变化敏感需要做图像增强或者多尺度推理。6.2 按时间段和天候过滤误报打场晒粮行为有很强的时间规律夏收和秋收季节集中在白天10点到17点天气晴朗。如果没有这些先验信息模型在夜晚路灯下的一片反光或者雨天路面湿润的浅色区域很容易产生误报。我一般会在检测结果后面加一道规则过滤不单纯依赖模型输出。过滤条件推荐设置理由时间范围只在5月~11月启用检测冬春没有晒粮行为系统可以直接关闭时间段每天8:00~18:00夜间几乎没有合法晒粮误报概率高天况雨天、雪天直接关闭湿滑路面颜色和粮食高度相似置信度视频连续5帧至少3帧检出才报警单帧偶发误报直接忽略这道后处理里我踩过最大的坑是“置信度加时间过滤后真实目标也被滤掉了”。后来换成连续帧计数效果才稳定。做法是维护一个滑动窗口统计最近5帧内模型输出框的IoU只有当同一个位置在至少3帧里都检出目标才触发一次报警。这样既保留动态目标的稳定性又过滤掉单帧的随机误报。这段逻辑写起来不复杂但放在模型后面整个系统的可用度能提升一个层级。回头看我第一次拿这个数据集训练时只看了单帧mAP 0.89满心欢喜去现场结果一天误报几十次。后来把静态指标和时序验证结合起来才真正把模型用起来。这套流程从数据集校验到后处理每一步都值得走一遍希望帮到你。本文还有配套的精品资源点击获取
特种作业考证资讯 责任编辑:民启特种作业
FUWU BAOZHANG

看完文章,报名服务了解一下

从咨询到拿证,全程有人对接

条件先核对年龄、学历、体检先对照,能报才报
材料免费预审材料拍来先审,不齐的提前补
批次主动提醒报名截止、考试时间提前通知
费用透明费用报名前逐项列明,确认后再办

看完文章还有疑问?

报考条件、材料清单、考试批次,直接电话或在线咨询,几分钟给你明确答复。