ARTICLE DETAIL

资讯详情

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

翻越围栏检测数据集实战:VOC/YOLO标注格式与YOLOv8训练部署指南

翻越围栏检测数据集实战:VOC/YOLO标注格式与YOLOv8训练部署指南 简介面向行人翻越栏杆/围栏检测任务这份数据集提供了1680张真实场景图片包含climbing和person两类目标共2454个标注框。标注工具为labelImg采用矩形框标注并同时导出Pascal VOC格式XML与YOLO格式TXT可无缝接入主流目标检测框架的训练与验证流程。压缩包共2000个文件以VOC标注XML和YOLO标注TXT为主整体约100MB结构清晰便于批量加载。数据集中含部分增强图片适合需要增加样本多样性或检验模型泛化能力的使用者下载后可直接用于安防监控、园区周界翻越行为检测等场景的数据准备、模型微调与效果评估。目前已有598人浏览资源标注准确合理但不对模型精度作保证使用前请根据需求筛选样本。1. 翻越栏杆围栏检测一个看起来简单、落地才知深浅的目标检测数据集小区周界的监控大屏上一个人翻越围栏往往只有两三秒可用传统周界报警要么不报、要么狂报地铁站台、工地围挡、校园围墙、厂区井口这类场景都在等一个能快速落地的检测方案。标题里这套翻越栏杆围栏数据集1680张VOCYOLO格式含增强解决的正是这个需求把翻越围栏这个动作型问题拆成目标检测这个可以标框、可以训练、可以部署的问题。目标检测从业者可以直接拿VOC或YOLO格式的标注开始训练安防集成商可以用它快速验证模型选型。适合人群很明确——正在做智能安防、园区警戒、行为识别预处理或想用YOLO系列快速出原型的人。2. 先把数据集读透VOC与YOLO两套标注在字段上差在哪里2.1 从标注文件反推一张图的全部信息拿到zip解压之后先别急着开训花十分钟把标注文件读一遍。VOC和YOLO两套格式描述的是同一批目标但坐标体系完全不同VOC的xml里存的是像素坐标左上角右下角各一组YOLO的txt里存的是归一化后的中心点坐标和宽高。不读透这两套字段后面转换、校验、可视化都会踩坑。VOC的xml结构大概是下面这样每个object块代表一个目标bndbox里四个值都是整数像素坐标annotation folderJPEGImages/folder filenamefence_001.jpg/filename size width1920/width height1080/height depth3/depth /size object nameperson/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin421/xmin ymin332/ymin xmax668/xmax ymax901/ymax /bndbox /object /annotation这里最容易忽视的两个字段是truncated和difficult。truncated表示目标被图像边界截断difficult表示目标是否难以辨认。翻越围栏的数据里人跨到围栏顶端那一帧脚和头经常超出画面这两个字段如果标注得准训练时就能有选择地处理边界样本。YOLO格式的txt则简洁很多每行一个目标五个数字分别是class_id x_center y_center width height全部做了图像宽高归一化取值在0到1之间。同一张图的标注内容大致是0 0.2833 0.5708 0.1286 0.5269 1 0.7125 0.4630 0.0188 0.4259class_id对应的是你配置的类别顺序不是类别名。所以拿到数据集后第一件事是把YOLO标签里的class_id和VOC xml里的name对应上确认类别顺序是否统一。常见做法是用一个小脚本把两种格式都读取一遍打印出前几条记录做交叉核对。import xml.etree.ElementTree as ET from pathlib import Path voc_dir Path(Annotations) xml_file voc_dir / fence_001.xml tree ET.parse(xml_file) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) print(fimage size: {w}x{h}) for obj in root.findall(object): name obj.find(name).text box obj.find(bndbox) xmin int(box.find(xmin).text) ymin int(box.find(ymin).text) xmax int(box.find(xmax).text) ymax int(box.find(ymax).text) # 校验这个框是否超出图像边界 if xmax w or ymax h: print(f[warning] {xml_file.name}: {name} box exceeds image bounds) print(f{name}: ({xmin}, {ymin}, {xmax}, {ymax}))这段代码做了三件事读取图像尺寸、逐个解析目标框、做越界告警。越界框在VOC里是允许的但在转换YOLO后会被clip或直接导致归一化坐标大于1训练时可能被模型当成异常样本。整批数据跑一遍这样的脚本能提前发现标注质量问题的规模。2.2 1680张含增强数据的目录组织与划分逻辑含增强三个字决定了这批数据不是简单的一对一标注。常见的增强组合是水平翻转、小角度旋转、亮度对比度扰动、少量马赛克拼接。增强后的文件名通常会带后缀区分比如fence_001.jpg对应fence_001_flip.jpg、fence_001_bright.jpg标注文件也同步生成同名文件。拿到压缩包后建议按下面的目录结构整理这是YOLOv8训练时最省事的组织方式路径内容images/train/训练图片含原始图和增强图images/val/验证图片建议只用原始图labels/train/与训练图片同名的txt标注labels/val/与验证图片同名的txt标注Annotations/原始VOC xml文件备份用classes.txt类别列表一行一个类名这里有一个和含增强直接相关的坑增强图不能全部塞进训练集而不做划分。如果某个原始样本及其所有增强副本都进了训练集模型相当于反复见同一个目标的几种变体验证集上表现得很好看一到现场就露馅。我一般会把原始图按8:2划分增强图只进训练集验证集保持纯净。这样验证结果更接近真实部署效果。数据划分用一段简单脚本就能完成不需要手动拖文件import os import random from pathlib import Path random.seed(2025) img_dir Path(images) labels_dir Path(labels) train_ratio 0.8 all_images sorted(img_dir.glob(*.jpg)) original_images [p for p in all_images if _flip not in p.stem and _bright not in p.stem] aug_images [p for p in all_images if p not in original_images] random.shuffle(original_images) val_cnt int(len(original_images) * (1 - train_ratio)) val_images set(original_images[:val_cnt]) os.makedirs(images/train, exist_okTrue) os.makedirs(images/val, exist_okTrue) os.makedirs(labels/train, exist_okTrue) os.makedirs(labels/val, exist_okTrue) for img in all_images: label labels_dir / (img.stem .txt) if img in val_images: img.rename(fimages/val/{img.name}) label.rename(flabels/val/{label.name}) else: img.rename(fimages/train/{img.name}) label.rename(flabels/train/{label.name})这段脚本先用文件名后缀区分原始图和增强图再把原始图按比例抽到验证集。关键逻辑在val_images这个集合增强图永远不进验证集。随机种子固定后重复跑不会改变划分结果。2.3 把VOC转成YOLO转换脚本与四个边界坑虽然数据集声明了双格式你拿到的文件未必每一份都字段齐全或者你可能从其他来源收集了一批VOC标注想合并进来。备一个VOC转YOLO的脚本是必要的。import os import xml.etree.ElementTree as ET from pathlib import Path # 类别顺序必须和后续训练的 data.yaml 完全一致 CLASS_NAMES [person, fence, railing] def voc_to_yolo(xml_path, out_dir, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_NAMES: continue cls_id CLASS_NAMES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 边界裁剪避免归一化后出现大于1的值 xmin max(0, min(xmin, img_width - 1)) xmax max(0, min(xmax, img_width - 1)) ymin max(0, min(ymin, img_height - 1)) ymax max(0, min(ymax, img_height - 1)) if xmax xmin or ymax ymin: continue x_center ((xmin xmax) / 2) / img_width y_center ((ymin ymax) / 2) / img_height width (xmax - xmin) / img_width height (ymax - ymin) / img_height lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) out_path Path(out_dir) / (Path(xml_path).stem .txt) out_path.write_text(\n.join(lines) \n if lines else ) voc_dir Path(Annotations) img_dir Path(JPEGImages) out_dir Path(labels) out_dir.mkdir(exist_okTrue) for xml_file in sorted(voc_dir.glob(*.xml)): img_file img_dir / (xml_file.stem .jpg) if not img_file.exists(): print(f[skip] missing image: {xml_file.stem}) continue # 从图片文件读取真实宽高 from PIL import Image w, h Image.open(img_file).size voc_to_yolo(str(xml_file), str(out_dir), w, h)四个边界坑在这段代码里都做了防范第一类别ID顺序。CLASS_NAMES列表的顺序直接决定txt里第一列的数值训练时data.yaml的names必须和它一致错一位全盘皆错。第二越界裁剪。VOC允许框超出图像边界不clip的话归一化坐标会大于1YOLO训练时容易导致边界框回归不稳定。第三过滤退化框。xmax xmin这种框在增强后可能出现直接跳过比强行保留更安全。第四空标注文件。一张图没有任何检测目标时txt文件保留为空不要写一个不存在的类别进去。转换之后用随机选一张图叠加可视化一下框和人的位置对不对一眼就能看出来。有条件的话把目标检测常用标注工具如LabelImg、X-AnyLabeling打开一个txt文件对比看一遍确认格式没有偏差。3. 用YOLOv8训练自己的翻越数据集从检查数据到跑通一次训练3.1 环境和数据前置检查训练前检查GPU显存和数据规模。1680张图不算大但YOLOv8在640分辨率下跑100个epoch显存低于6GB会很难受。环境安装用官方方式即可# 推荐 Python 3.10 或 3.11 conda create -n yolo python3.11 -y conda activate yolo pip install -U ultralytics-U参数保证装到当前最新版。装完后先跑一个检测命令确认环境正常yolo predict modelyolov8n.pt sourceimages/fence_001.jpg这一步能同时验证三件事模型权重能否下载、训练/推理接口是否可用、样例图片能否正常读取。很多翻车案例都是环境没装完就急着训练报错先集中在依赖缺失反而把数据问题掩盖了。数据前置检查的重点是类别分布。跑一个统计脚本确认每个类别的目标数量和框的尺寸分布from pathlib import Path label_dir Path(labels/train) stats {} for txt in label_dir.glob(*.txt): lines txt.read_text().strip().splitlines() for line in lines: parts line.split() if len(parts) ! 5: print(f[warning] bad label line in {txt.name}: {line}) continue cls_id int(parts[0]) stats[cls_id] stats.get(cls_id, 0) 1 for cls_id, cnt in sorted(stats.items()): print(fclass {cls_id}: {cnt} boxes)如果翻越的人person类只有几百个框而围栏本身有几万个框训练时模型会严重偏向围栏检测。翻越围栏数据集的特殊性就在这里围栏是静态背景人翻越目标很小且动态。发现类别不平衡后可能需要调整损失权重或单独对person类做重采样。3.2 配置data.yaml与数据增强参数数据集准备好后写一个data.yaml指向路径和类别列表# data.yaml path: /home/user/datasets/fence_climb train: images/train val: images/val names: 0: person 1: fence 2: railingpath建议写绝对路径。YOLO系列的相对路径解析逻辑在不同版本里有变化写绝对路径可以少踩一个坑。names的顺序必须和label转换时CLASS_NAMES的顺序完全一致person是0说明在你的标签文件里person类出现时第一列写0。数据增强是含增强数据集的最佳搭档。训练命令里通过augment相关参数控制不用改数据集本身yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ batch16 \ imgsz640 \ hsv_h0.015 \ hsv_s0.5 \ hsv_v0.4 \ degrees5 \ translate0.1 \ fliplr0.5 \ mosaic1.0 \ patience20参数含义拆开看hsv_h/s/v控制色调饱和度亮度的扰动范围这三个数值是防止模型依赖光照变化degrees5表示只做5度以内的小角度旋转翻越围栏场景人不会是倒立的大幅旋转反而制造错误样本translate0.1让目标产生轻微平移提高定位鲁棒性fliplr0.5是水平翻转概率对围栏这种左右对称场景非常安全mosaic1.0启用马赛克拼接但翻越场景里人通常是大目标mosaic太重会把小目标裁掉后面避坑章节会说。如果你在命令行里漏写了这些增强参数YOLOv8会使用默认值默认值针对COCO数据集调校直接迁移到围栏场景会有偏差。建议训练时显式写出增强参数不要依赖默认。3.3 训练过程中的损失曲线与检查点训练开始后不要干等它跑完。日志里三个损失分量需要盯box_loss、cls_loss、dfl_loss。box_loss是框回归损失cls_loss是分类损失dfl_loss是分布焦点损失。翻越围栏数据里人跨过栏杆瞬间的姿态多变box_loss的收敛速度通常比cls_loss慢这是正常的。训练中间产物会保存在runs/detect/trainN目录下每轮结束会往weights/里写入last.pt和best.pt。best.pt是按验证集指标保存的最优权重不是最后一步的训练结果。数据集标注质量一般时best.pt通常出现在第60到80轮之间训练到最后几轮可能因为过拟合在验证集上指标回退。训练结束后别急着删last.pt有时候它在验证集上比best.pt低一点点但在真实视频里更稳。损失值出现NaN是训练中可能遇到的最严重问题尤其和数据集自身质量相关。如果训练在第5轮左右突然loss冲高并变成NaN优先检查数据里是否有纯色图片或全黑帧。翻越围栏的监控视频截图里夜间红外图可能大面积过暗这类样本会让BN层统计量崩溃。可以在训练前用脚本扫描图片像素标准差把标准差过小的图挑出来单独看。3.4 验证指标、混淆矩阵与首次推理训练结束后做一次独立验证yolo detect val \ modelruns/detect/trainN/weights/best.pt \ datadata.yaml验证输出里除了mAP50和mAP50-95混淆矩阵那张图更值得细看。矩阵的行是真实类别列是预测类别对角线越亮越好。翻越围栏场景里最容易混淆的是person和railing人跨过栏杆的瞬间躯干和栏杆在视觉上交叉重叠标注时很难界定边界。混淆矩阵会直观反映模型把人判成围栏还是把围栏判成人。验证通过后跑一次预测感受一下模型在真实图片上的行为yolo detect predict \ modelruns/detect/trainN/weights/best.pt \ sourcetest_clip.mp4 \ conf0.45 \ iou0.5 \ saveTrueconf是置信度阈值翻越场景建议从0.45起步调试。阈值太高会漏掉跨栏瞬间被遮挡的人太低则产生大量框框跳变。iou是NMS的IoU阈值0.5是居中值行人密集区域可以往0.45调。首次推理视频后把输出视频逐帧播放一遍重点看两件事人跨到栏杆上方时框是否跟着物体的最高点走人完全越过栏杆走到围栏另一侧时模型是否还误判为翻越中。这两个边界状态是模型后续调优的主要方向。4. 避坑记录翻越栏杆围栏数据集训练与落地的四个坑4.1 增强图片的标注框集体错位现象训练loss在某个数值附近停滞loss曲线不再平滑下降可视化训练图片时发现某些图上的框明显没盖住人。原因数据集里的增强图片在生成时做了图像翻转或旋转但标注文件没有同步做对应的坐标变换。比如原图水平翻转后框的坐标没有映射成w - xmin这种镜像坐标目标实际在左边框却还在右边。整批数据如果有一两成这样的错位样本模型会被反复往错误方向拉。解决先排查错位比例。写一个脚本逐张读取增强图片的标注把框叠加到图上输出到一个临时文件夹快速翻看。如果确认是文件本身的问题就用前面提到的转换逻辑对增强图单独重新计算坐标。这里最关键的是不要手动改个别文件写脚本统一处理不然800张增强图改完手会废。我一般会保留所有原始xml作为基准增强图坐标一律从原图坐标推算避免标注和图像不一致。4.2 训练中BN崩溃或损失变NaN现象训练正常运行5到20轮loss突然变成NaN或者所有指标在一轮内全灭。原因最常见的是增强参数设置过猛尤其是hsv_v的亮度扰动过大加上mosaic拼接时把多张亮度截然不同的图拼在一起BN层的running统计量被污染。翻越围栏监控画面的光照本来就不稳定夜间红外图整体亮度偏低再加上剧烈增强模型直接崩掉。其次可能是batch size太小而学习率过大梯度爆炸。解决把增强参数调回温和区间hsv_v不超过0.4mosaic尝试从1.0降到0.5。学习率方面YOLOv8默认的lr00.01在batch size为16时合适如果显存只够跑batch 8把lr0降到0.005。出现NaN后不需要清空重来从最近的last.pt用更小学习率继续训练即可。先跑20个epoch确认损失绝对值可控再加大训练量。4.3 mAP不低但部署现场疯狂误报现象验证集mAP50有0.87看起来不错一旦部署到真实的园区围栏边树叶晃动、车灯扫过、飞鸟经过全都在报警。原因数据集来自特定的几个摄像头视角现场环境光照、视角高度、摄像头型号不一样时域偏移非常严重。再一个原因是翻越围栏这个正样本里的人和负样本里的路人外观上没有本质差别模型学的是栏杆附近有一团人形物体而不是人正在做翻越动作。验证集mAP只反映同源数据上的表现不反映域偏移后的鲁棒性。解决收集现场没有翻越行为的历史监控截图加入训练数据作为背景负样本。这里注意负样本图片应该只有图像没有标注文件或者允许空标注。让模型看到栏杆旁有人经过但不要报警的样本。更彻底的办法是加一个检测后处理规则只有检测到人且该人的框与围栏检测框有重叠时才触发报警。把单模型问题改成双模型组合问题误报率能降一个数量级。4.4 行人密集且互相遮挡时框跳变现象两个人并肩靠近围栏检测框在两个目标之间来回跳动一个人被另一个人挡住半边身体时框的宽高比突然异常。原因NMS后处理在高密度目标场景下的固有缺陷。当两个目标重叠面积较大时NMS会把置信度较低的检测框当成重叠框抑制掉。翻越围栏数据里人的姿态是侧身跨栏两个侧身人叠加在图像上非常常见。置信度阈值也影响跳变阈值越低同一个目标可能产生多个重叠框NMS的抑制结果会更不稳定。解决把NMS的iou阈值从默认0.7往0.4到0.5方向调允许更多重叠框保留。配合跟踪器使用每个目标分配一个跟踪ID检测框跳变但跟踪ID不变报警稳定性会好很多。我在实际部署时一般用ByteTrack或DeepSORT接在检测模型后面帧率损失不到5%但报警连续性提升明显。如果数据集里密集样本本来就少先补充一批两人或多人在围栏前的标注图比调NMS参数更治本。4.5 借助YOLO系列对比选小模型还是大模型现象yolov8s训练效果一般换yolov8m后mAP涨了但推理帧率掉到不可用。原因翻越围栏场景往往要跑在IPC或边缘盒子上算力受限。YOLO系列从n、s到m、l参数翻倍增长但mAP提升在自建小数据集上并不总是线性增长。1680张数据集规模不大大模型很容易过拟合。解决优先用yolov8n或yolov8s把训练epoch加到150甚至200配合早停patience20。如果小模型精度到瓶颈再考虑用更大模型加更强的增强正则。模型宽度和深度不是唯一的提升路径输入分辨率从640升到960往往对围栏上的人这类小目标帮助更直接代价是推理时间增加。在边缘设备上分辨率收益通常比模型大小收益更高。5. 部署时再省一笔翻越检测的ROI裁剪与跳帧推理翻越围栏检测在部署端有个天然优势目标只会出现在围栏线附近。监控画面里大片天空、地面、道路都是无效区域整帧推理不仅浪费算力还容易把远处无关的行人当目标。部署时先把ROI区域框出来只对围栏附近的带状区域做检测。具体做法是先用一段现场视频跑一遍目标检测观察围栏在画面中的像素位置把围栏上下各扩充50%的区域设为ROI。推理时先对整帧做一次图像裁剪把裁剪后的区域送入模型。以1920x1080输入为例围栏区域通常只占画面宽度的30%到40%裁剪后推理耗时能降一半以上。跳帧推理和检测需要搭配使用。边缘设备上可以不每一帧都跑检测而是每跑一帧检测接下来两到三帧用跟踪器预测位置。检测通道控制在10FPS左右跟踪通道保持25FPS输出画面连贯性不受影响。翻越动作持续约两秒10FPS的检测频率不会漏掉关键帧。如果设备支持TensorRT把训练好的权重导出为engine格式推理速度还能再翻一倍。导出方式yolo export \ modelbest.pt \ formatengine \ device0 \ imgsz640导出时device0指定用哪张GPUimgsz要和训练时的输入分辨率一致。engine格式对输入分辨率是固定的训练用640导出也用640推理时再用ROI裁剪保证输入尺寸匹配。如果你部署的是CPU设备考虑用OpenVINO导出比直接跑PyTorch权重快不少。我习惯在交付前花一晚上把现场监控视频循环跑一遍第二天只统计误报和漏报mAP再高也不如真实夜间的表现更有说服力。翻越检测这类长尾场景真正稳定下来的方案往往是在数据、模型和后处理三层各做一点减法而不是追求单点指标的极致。希望这些经验帮到你。本文还有配套的精品资源点击获取
返回列表