
简介这份资源面向计算机视觉初学者与安防场景研究者提供高空抛物检测的完整数据集与配套训练成果解决从零采集、标注到模型落地周期长的问题。包内共807个文件约377.94MB包含259张jpg图像、259个xml标注与261个txt标签分别对应VOC与YOLO两种格式另有6段mp4原始抛物视频、7张png可视化图、2个pt权重文件、1个yaml配置、1个csv训练记录及TensorBoard日志覆盖数据、配置与训练全过程。已有1194人学习下载说明该方向具备一定关注度。使用者可直接用作者提供的yolov8s模型best.pt或last.pt进行推理验证也可基于VOC/YOLO标注重新训练、调参与对比实验省去数据采集与标注成本适合课程设计、毕业课题或安防算法预研等场景快速起步。1. 高空抛物检测为什么值得单独做一份数据集高空抛物这件事真正做过检测项目的人都知道它跟普通目标检测完全不是一个难度层级。普通数据集里目标大、特征稳、背景干净而高空抛物场景里抛下来的物体可能只有几十个像素背景是整栋楼的外立面窗户、空调外机、晾衣架、管道全是干扰项再加上运动模糊和光照变化模型很容易把一只飞过的鸟或者飘落的树叶当成抛物。这份「高空抛物数据集VOCYOLO格式259张6段视频yolov8模型和日志项目说明.zip」的价值就在这儿它不是单纯给你一堆图而是把标注好的 VOC 和 YOLO 双格式图片、6 段真实场景视频、一份已经跑过的 yolov8 模型权重和训练日志外加项目说明打包在一起。适合谁适合正在做智慧社区、楼宇安防、边缘盒子部署的工程师也适合想拿一个真实小样本数据集练手 yolov8 全流程的人。259 张图不算多但配合视频和日志你能把「数据标注→格式转换→训练→推理→视频验证」这条链路完整走一遍而不是只停留在跑通官方 coco128 示例的水平。2. 拆开压缩包VOC 与 YOLO 双格式到底怎么对应2.1 VOC 的 XML 结构和 YOLO 的 txt 差在哪VOC 格式的核心是每张图对应一个 XML 文件里面用object标签记录每个目标的类别和边界框边界框用xmin、ymin、xmax、ymax四个像素坐标表示。YOLO 格式则是一张图对应一个 txt每行一个目标格式是类别索引 中心x 中心y 宽 高而且这四个值全部归一化到 0 到 1 之间。很多人第一次转换就翻车是因为忘了 YOLO 的坐标是相对于图片宽高的比例不是绝对像素。这份数据集同时给了两种格式意味着你可以直接用 VOC 做可视化检查用 YOLO 直接喂给 yolov8省掉自己写转换脚本的环节。但要注意两套格式的类别顺序必须一致否则训练出来的模型会把「抛物」和「非抛物」搞反。2.2 用脚本核对两套标注是否对齐拿到数据集第一件事不是急着训练而是核对 VOC 和 YOLO 是否一一对应。我一般会写一个短脚本遍历 images 目录检查每张图是否有同名 xml 和同名 txt并抽查几个框的坐标换算是否吻合。import os import xml.etree.ElementTree as ET img_dir images xml_dir annotations_xml txt_dir labels_yolo def voc_to_yolo(xml_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() boxes [] for obj in root.findall(object): cls_name obj.find(name).text bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) # 转成 YOLO 归一化中心点格式 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h boxes.append((cls_name, cx, cy, w, h)) return boxes for name in os.listdir(img_dir): stem os.path.splitext(name)[0] xml_path os.path.join(xml_dir, stem .xml) txt_path os.path.join(txt_dir, stem .txt) if not os.path.exists(xml_path) or not os.path.exists(txt_path): print(缺失配对:, stem) continue # 这里只做存在性检查坐标换算可进一步比对 print(stem, 配对正常)这段脚本的逻辑很直白先按文件名把图片、XML、txt 三者对齐缺任何一个就报出来。voc_to_yolo函数演示了坐标换算的核心公式中心点等于框中心除以图片宽高宽高等于框尺寸除以图片宽高。参数上唯一要留意的是img_w和img_h必须从对应图片真实读取不能写死因为这份数据集里图片分辨率不一定完全统一。如果你发现某张图的 txt 里坐标大于 1那基本可以确定是转换时没做归一化这种样本直接删掉或者重新标注别留着污染训练集。2.3 259 张图怎么划分训练集和验证集259 张图属于典型的小样本划分比例不能照搬大数据的 8:2。我的习惯是训练集 200 张、验证集 59 张比例大约 77:23验证集稍微多一点因为小样本下验证集太小会导致指标波动极大今天 mAP 0.8 明天 0.5 的玄学现象多半就是验证集太小造成的。划分时要注意同一段视频抽出来的帧不能同时出现在训练集和验证集里否则验证指标会虚高实际部署时原形毕露。这份数据集带了 6 段视频如果你打算从视频里再抽帧扩充务必按视频来源划分而不是按帧随机划分。# 按 8:2 左右划分先建目录 mkdir -p datasets/images/train datasets/images/val mkdir -p datasets/labels/train datasets/labels/val # 用 python 做可复现的随机划分 python - EOF import os, random, shutil random.seed(42) # 固定种子保证每次划分一致 imgs sorted(os.listdir(images)) random.shuffle(imgs) split int(len(imgs) * 0.77) for i, name in enumerate(imgs): stem os.path.splitext(name)[0] dst train if i split else val shutil.copy(fimages/{name}, fdatasets/images/{dst}/{name}) shutil.copy(flabels_yolo/{stem}.txt, fdatasets/labels/{dst}/{stem}.txt) print(划分完成, split, len(imgs) - split) EOFrandom.seed(42)是为了让划分结果可复现团队协作时每个人跑出来的训练集验证集一致否则指标没法对比。split取 77% 作为分界点你可以按实际图片数量微调。复制而不是移动是为了保留原始文件万一划分错了还能重来。这一步做完目录结构就符合 yolov8 默认要求了images 和 labels 平行下面各有 train 和 val。3. 用 yolov8 训练这份数据集配置、命令与日志解读3.1 data.yaml 怎么写才不出错yolov8 训练靠一个 yaml 文件告诉它数据在哪、有几类、类名是什么。这份数据集是高空抛物二分类场景常见做法是设成parabola和background两类或者只保留parabola一类。如果你只检测抛物目标就写 nc: 1names: [parabola]。路径建议用绝对路径相对路径在 ultralytics 不同版本里解析基准不一样容易报「No labels found」。# data.yaml path: /home/user/datasets train: images/train val: images/val nc: 1 names: 0: parabolapath是数据集根目录train和val是相对 path 的子路径。nc是类别数必须和 names 的长度一致否则训练启动时就会报索引越界。names 的键从 0 开始和 YOLO txt 里的类别索引对应。改完 yaml 后建议先用一行命令验证路径是否被正确解析别等训练跑起来才发现找不到图。3.2 训练命令与关键参数yolov8 的训练入口是yolo detect train核心参数就那么几个但每个都影响结果。小样本数据集最忌讳直接上大模型yolov8n 或 yolov8s 就够了参数量小、收敛快259 张图用 yolov8x 只会过拟合。yolo detect train \ modelyolov8n.pt \ datadata.yaml \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/train \ nameparabola_v8nmodelyolov8n.pt表示从官方预训练权重开始微调小样本下这比从零训练效果好得多。epochs150是上限配合patience30做早停验证指标 30 轮不提升就停避免无效训练。imgsz640是输入尺寸高空抛物目标小理论上可以调到 960 甚至 1280但显存和速度会明显下降先用 640 跑通再考虑加。batch16在 8G 显存上比较稳显存不够就降到 8 或 4。lr00.01是初始学习率小样本可以降到 0.005 减少震荡。训练日志里重点看三个东西box_loss 是否稳定下降、mAP50 是否在涨、有没有出现 loss 突然变 nan。如果 loss 变 nan多半是学习率太大或者标注里有非法坐标。3.3 从日志判断模型有没有真的学到东西这份资源里附带了训练日志这是很实用的部分因为很多人只看最后 mAP不看过程。日志里metrics/mAP50(B)是 IoU 0.5 时的平均精度高空抛物这种小目标mAP50 能到 0.7 以上就算可用。但更关键的是看metrics/precision(B)和metrics/recall(B)的平衡如果 precision 高 recall 低说明模型保守漏检多如果 recall 高 precision 低说明误报多把树叶鸟影都当抛物。安防场景通常更怕漏检所以宁可 precision 低一点也要保住 recall。日志里还有val/box_loss如果它一直不降反升就是过拟合的典型信号这时候要么加数据增强要么减 epochs。# 训练结束后用验证集单独跑一次评估 yolo detect val \ modelruns/train/parabola_v8n/weights/best.pt \ datadata.yaml \ imgsz640这条命令会输出完整的 precision、recall、mAP50、mAP50-95。mAP50-95 更严格小目标数据集上通常比 mAP50 低不少别拿它跟大目标数据集比。评估完可以再用yolo detect predict跑几张验证图肉眼看框准不准指标好看但框歪的情况并不少见。4. 6 段视频怎么用抽帧扩充与推理验证4.1 视频抽帧补数据的具体做法259 张图对 yolov8 来说偏少6 段视频就是天然的扩充来源。但抽帧不是无脑每秒抽一帧那样相邻帧几乎一样等于重复样本。我的做法是按目标运动状态抽抛物出现的片段密集抽静止背景片段少抽或不抽。用 opencv 按间隔抽帧间隔根据视频帧率定25fps 的视频每 10 帧抽一张大约每秒 2.5 张。import cv2 import os video_path video_01.mp4 out_dir frames/video_01 os.makedirs(out_dir, exist_okTrue) cap cv2.VideoCapture(video_path) fps cap.get(cv2.CAP_PROP_FPS) interval int(fps / 2.5) # 约每秒抽 2.5 帧 idx 0 saved 0 while True: ret, frame cap.read() if not ret: break if idx % interval 0: cv2.imwrite(os.path.join(out_dir, f{idx:06d}.jpg), frame) saved 1 idx 1 cap.release() print(总帧, idx, 抽取, saved, fps, fps)interval由 fps 换算保证不同帧率的视频抽帧密度一致。抽出来的帧要重新标注这一步没有捷径labelme 或者 roboflow 都行标完再转成 YOLO 格式。注意抽帧后要按视频来源划分训练验证同一段视频的帧不能跨集否则验证指标会虚高。扩充后如果图片到了 800 张以上可以把 epochs 适当加到 200同时把数据增强开强一点。4.2 用训练好的模型跑视频推理模型训练完最直接的验证就是拿原始 6 段视频跑推理看实际画面里框得准不准、漏不漏。yolov8 支持直接对视频推理输出带框的视频文件。yolo detect predict \ modelruns/train/parabola_v8n/weights/best.pt \ sourcevideo_01.mp4 \ conf0.3 \ iou0.5 \ saveTrue \ projectruns/predict \ namevideo_01_resultconf0.3是置信度阈值高空抛物目标小、特征弱阈值设太高会大量漏检0.3 是比较常用的起点实际按漏检和误报的容忍度调。iou0.5是 NMS 的 IoU 阈值控制重叠框合并。saveTrue会把结果视频存到 project/name 目录下。跑完重点看两类问题一是抛物目标有没有被框住二是背景里的窗户、空调外机有没有被误框。如果误框多先别急着调模型回头检查训练集里是不是有类似背景被误标成了正样本。4.3 视频推理结果与日志指标对不上的排查很多人会遇到日志里 mAP 挺好看视频里却一塌糊涂这种落差通常有三个原因。第一验证集和视频场景分布不一致验证集里目标大、背景简单视频里目标小、背景复杂。第二抽帧扩充时把同一段视频的帧混进了训练集和验证集导致验证指标虚高。第三推理时的 imgsz 和训练时不一致训练用 640 推理用 1280小目标尺度变化会让模型不适应。排查顺序是先统一 imgsz再检查数据划分最后才考虑模型本身。如果三项都排除了还是不行那就是数据量真的不够老老实实回去标更多帧。5. 避坑与常见问题小样本高空抛物训练的五个血泪教训5.1 现象训练 loss 正常但 mAP 一直是 0原因YOLO txt 里的类别索引和 data.yaml 的 names 对不上或者 txt 里坐标没归一化值大于 1。模型学不到有效目标loss 看着在降其实是在拟合噪声。解决用第 2 章的核对脚本抽查坐标范围确认所有值都在 0 到 1 之间类别索引从 0 开始且不超过 nc-1。5.2 现象验证集 mAP 很高实际视频漏检严重原因验证集和训练集来自同一段视频的相邻帧画面几乎一样模型等于在背答案。解决按视频来源划分数据集同一段视频的帧只进训练集或只进验证集绝不混用。划分完再跑一次评估指标会降但更真实。5.3 现象模型把窗户、空调外机误检成抛物原因训练集里这些背景元素和抛物目标在位置上高度重合模型学到了错误的上下文关联。解决补充负样本把没有抛物的纯背景帧也加进训练集标注为空 txt。负样本比例控制在正样本的 20% 到 30%太多会拉低 recall。5.4 现象训练到一半 loss 变成 nan原因学习率太大或者标注里有 xmax 小于 xmin 这种非法框。解决先把 lr0 降到 0.005 甚至 0.001 重跑如果还 nan就用脚本遍历所有 txt检查每行宽高是否大于 0非法行直接删掉或修正。5.5 现象推理速度慢边缘设备跑不动原因用了 yolov8m 以上的模型或者 imgsz 设得太大。解决高空抛物场景 yolov8n 通常够用imgsz 从 640 起步边缘设备上可以导出 onnx 或 tensorrt 加速。导出命令是yolo export modelbest.pt formatonnx imgsz640导出后再用对应推理引擎加载速度能提升明显。6. 把这份资源用透从 259 张图到可部署模型的进阶技巧小样本数据集最怕的就是「跑通即结束」其实这份资源真正的价值在于它给了你一条可复现的基线你可以在这条基线上做增量。第一个技巧是善用日志里的曲线做学习率调度。yolov8 默认带余弦退火但小样本下你可以手动试lr00.01和lr00.005两组对比日志里 mAP50 的上升斜率斜率更陡的那组更适合你的数据分布。第二个技巧是冻结 backbone 微调。259 张图直接全量微调容易过拟合可以先用freeze10冻结前 10 层只训练检测头跑 50 轮后再解冻全量跑 100 轮这种两阶段训练在小样本上往往比一次性训练稳。# 第一阶段冻结 backbone yolo detect train modelyolov8n.pt datadata.yaml epochs50 freeze10 lr00.01 namestage1 # 第二阶段解冻全量微调 yolo detect train modelruns/train/stage1/weights/best.pt datadata.yaml epochs100 lr00.005 namestage2freeze10表示冻结前 10 层具体层数可以用model.model打印结构确认。第二阶段从第一阶段的 best.pt 继续学习率减半避免破坏已经学好的特征。第三个技巧是验证时别只看 mAP把混淆矩阵导出来看。yolov8 验证后会生成confusion_matrix.png如果 background 那一列误检特别多说明负样本不够如果抛物那一行漏检多说明正样本多样性不足。第四个技巧是模型导出后做一次端到端视频回放把 6 段视频全部跑一遍统计每段的漏检帧数和误报帧数做成表格对比这比单一 mAP 更能反映部署效果。视频编号总帧数漏检帧误报帧可用性判断video_01750128可用video_02620355需补样本video_03900920需加负样本这张表是我每次做完小样本项目都会填的漏检和误报分开统计才能定位到底是数据问题还是模型问题。从那以后我每次拿到小样本数据集都强制先跑一遍基线、再填一张这样的视频回放表才决定要不要加数据、调参还是换模型。希望这份高空抛物数据集和这套流程能帮你少走几个我当年踩过的坑。本文还有配套的精品资源点击获取