首页 / 资讯中心 / 文章详情

高空抛物数据集VOC+YOLO格式259张:yolov8训练与视频抽帧实战

高空抛物数据集VOC+YOLO格式259张:yolov8训练与视频抽帧实战 ★ FEATURED ARTICLE
简介本资源面向计算机视觉入门与安防场景研究者提供高空抛物检测的完整数据集与配套训练成果。数据来源于6段简短抛物视频逐帧截取259张图像并用labelImg完成标注同时给出VOC与YOLO两种格式方便直接接入不同检测框架开展训练与验证。压缩包共807个文件约377.94MB包含259个xml标注、261个txt标签、269张jpg图像以及6段mp4原始视频、2个pt权重、1个yaml配置、1个csv训练记录和若干png与日志文件覆盖数据、配置、模型与训练过程。作者已提供yolov8s训练好的模型及训练日志不想自行训练可直接选用best.pt或last.pt进行推理测试。目前已有1194人学习下载适合希望快速复现高空抛物检测流程、对比标注格式或开展二次开发的研究者与工程人员。1. 高空抛物数据集怎么选259 张 VOCYOLO 双格式到底够不够用高空抛物检测这个方向真正卡住大多数人的从来不是模型结构而是数据。你搜「高空抛物数据集 VOCYOLO 格式 259 张」的时候心里大概率在盘算两件事这点量能不能训出可用的 yolov8 模型以及那 6 段视频和现成日志到底能省多少事。先说结论——259 张标注图做通用目标检测确实偏少但高空抛物是一个背景固定、目标类别单一、运动轨迹可预测的场景配合视频抽帧和合理的增强策略它足以跑通一条从数据到 yolov8 推理的完整链路用来验证方案可行性、搭 demo、做算法讲解 PPT 都够用。它不适合直接上生产但非常适合让你在半天内看到第一个能框住坠落物的模型。这篇就按「数据集长什么样 → 怎么转格式 → 怎么训 yolov8 → 怎么避坑 → 怎么榨干那 6 段视频」的顺序讲透新手能照着敲熟手能直接看到边界在哪。2. 拆开这个压缩包VOC 与 YOLO 双格式的目录结构和字段含义拿到一个数据集压缩包第一件事不是急着训练而是把目录结构和标注字段摸清楚。高空抛物这类数据最常见的坑就是「图能打开、标签对不上」等你训到一半发现 loss 不降回头查才发现坐标越界或者类别名写错。所以这一章先把包里的东西拆开看。2.1 VOC 格式的 Annotations 与 JPEGImages 怎么对应VOC 格式的核心是Annotations目录下的 XML 文件和JPEGImages目录下的图片一一对应文件名不含扩展名必须完全一致。高空抛物数据集的 XML 里你重点看四个字段filename、sizewidth/height/depth、object下的name和bndboxxmin/ymin/xmax/ymax。name就是类别名这类数据集通常只有一个类常见命名是falling、object或者中文拼音转换前一定要先统计一遍到底有几个不同的 name。import os import xml.etree.ElementTree as ET from collections import Counter anno_dir Annotations cls_counter Counter() bad_boxes [] for xml_file in os.listdir(anno_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(anno_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) for obj in root.findall(object): name obj.find(name).text cls_counter[name] 1 box obj.find(bndbox) xmin int(float(box.find(xmin).text)) ymin int(float(box.find(ymin).text)) xmax int(float(box.find(xmax).text)) ymax int(float(box.find(ymax).text)) # 检查坐标是否越界这是高空抛物小目标最常见的脏数据 if xmin 0 or ymin 0 or xmax w or ymax h or xmax xmin or ymax ymin: bad_boxes.append((xml_file, xmin, ymin, xmax, ymax, w, h)) print(类别分布:, cls_counter) print(异常框数量:, len(bad_boxes)) for b in bad_boxes[:10]: print(b)这段脚本干三件事统计类别分布、检查坐标越界、打印前十条异常。参数上没什么可调的直接改anno_dir路径即可。逻辑说明高空抛物目标往往只占画面几十个像素标注时手一抖就容易把 xmax 标到图像宽度之外YOLO 训练时这类框会被静默丢弃或产生 NaN loss所以必须在转换前清洗。跑完如果bad_boxes不为空要么修正坐标要么直接删掉这几张图别心存侥幸。2.2 YOLO 格式的 txt 标签归一化坐标与类别索引YOLO 格式每张图对应一个同名.txt每行是class_id x_center y_center width height全部是归一化到 0~1 的相对值。这里有两个高频翻车点一是有人直接把 VOC 的绝对像素坐标写进去训练时框全跑到左上角二是class_id从 0 开始还是从 1 开始搞混导致类别错位。高空抛物单类别的话class_id就是 0。字段VOC (XML)YOLO (txt)换算关系类别name 文本class_id 整数需建类别映射表中心 x无x_center(xminxmax)/2/w中心 y无y_center(yminymax)/2/h宽xmax-xminwidth(xmax-xmin)/w高ymax-yminheight(ymax-ymin)/h表格里 w、h 是图像原始宽高。记住这个换算下一章的转换脚本就是照它写的。如果你的数据集已经同时给了 VOC 和 YOLO 两套标签那恭喜省一步但仍然要抽查——我见过不少「双格式」数据集里 YOLO 那套是坏的只有 VOC 是准的。3. 从 VOC 转 YOLO一份能直接跑的转换脚本和四个必查项双格式数据集最省心的地方在于你可以拿 VOC 当「真源」自己转一遍 YOLO转完和包里自带的 YOLO 标签对比不一致的地方就是脏数据。这一章给你一份完整可复现的转换脚本再讲清楚训练前必须查的四项。3.1 转换脚本划分 train/val 并生成 data.yamlimport os import random import xml.etree.ElementTree as ET import shutil # 类别列表顺序即 class_id务必和后续训练一致 CLASSES [falling] random.seed(42) voc_img_dir JPEGImages voc_anno_dir Annotations out_root highfall_yolo val_ratio 0.2 for split in [images/train, images/val, labels/train, labels/val]: os.makedirs(os.path.join(out_root, split), exist_okTrue) def convert_box(size, box): w, h size xmin, ymin, xmax, ymax box x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 裁剪到 [0,1]防止浮点误差导致越界 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) bw min(max(bw, 0.0), 1.0) bh min(max(bh, 0.0), 1.0) return x_center, y_center, bw, bh xml_files [f for f in os.listdir(voc_anno_dir) if f.endswith(.xml)] random.shuffle(xml_files) val_count int(len(xml_files) * val_ratio) val_set set(xml_files[:val_count]) for xml_file in xml_files: stem os.path.splitext(xml_file)[0] tree ET.parse(os.path.join(voc_anno_dir, xml_file)) root tree.getroot() size_node root.find(size) w int(size_node.find(width).text) h int(size_node.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: continue cls_id CLASSES.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) xc, yc, bw, bh convert_box((w, h), (xmin, ymin, xmax, ymax)) lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) split val if xml_file in val_set else train # 图片扩展名按实际情况改常见 jpg src_img os.path.join(voc_img_dir, stem .jpg) if not os.path.exists(src_img): continue shutil.copy(src_img, os.path.join(out_root, images, split, stem .jpg)) with open(os.path.join(out_root, labels, split, stem .txt), w) as f: f.write(\n.join(lines)) # 生成 data.yaml yaml_content fpath: {os.path.abspath(out_root)} train: images/train val: images/val nc: {len(CLASSES)} names: {CLASSES} with open(os.path.join(out_root, data.yaml), w) as f: f.write(yaml_content) print(转换完成train/val 已划分)逻辑说明脚本先固定随机种子保证划分可复现再逐 XML 解析、换算、裁剪、写 txt最后生成data.yaml。参数上val_ratio建议 0.2259 张的话验证集约 52 张够看趋势但不够做严谨评估心里要有数。CLASSES必须和 XML 里的 name 完全一致大小写都不能差。跑完检查highfall_yolo下四个子目录的文件数是否对得上。3.2 训练前必查的四项空标签、越界框、类别错位、图文不匹配转换完别急着开训花五分钟查这四项能省你几小时 debug。第一空标签有些图里没有目标txt 是空的YOLO 会当负样本少量可以多了会拉低召回。第二越界框脚本里已经裁剪但如果你用的是包里自带的 YOLO 标签就得自己查。第三类别错位data.yaml的names顺序和 txt 里的class_id必须严格对应。第四图文不匹配图片和标签文件名对不上训练时直接报找不到标签。# 快速统计空标签和标签行数分布 find highfall_yolo/labels -name *.txt -empty | wc -l find highfall_yolo/labels/train -name *.txt -exec wc -l {} | sort -n | tail -5第一条命令数空标签数量第二条看标签行数最多的几个文件。高空抛物单类别正常每张图 1~3 行如果某个文件几十行多半是标注时把整片区域都框了得人工复核。提示259 张图训 yolov8n 时batch 设 16、epochs 设 100 起步别一上来就 300先看 loss 曲线有没有正常下降。4. 用 yolov8 训练高空抛物模型参数怎么设、日志怎么看数据准备好了接下来是训练。这一章按「环境 → 命令 → 参数 → 日志解读」走重点讲清楚每个参数为什么这么设以及日志里哪些信号说明模型在正常学、哪些说明要停下来改。4.1 环境配置与最小训练命令yolov8 用 ultralytics 包CPU 和 GPU 都能跑。高空抛物 259 张图CPU 训 100 epoch 大概几小时有张 GTX1660Ti 级别的卡就快很多。环境配置不复杂关键是版本别乱。# 建议 python 3.8~3.10新建虚拟环境 python -m venv yolo_env source yolo_env/bin/activate # windows 用 yolo_env\Scripts\activate pip install ultralytics -i https://pypi.tuna.tsinghua.edu.cn/simple # 验证安装 yolo checksyolo checks会打印环境信息重点看 torch 是否装好、CUDA 是否可用。如果只有 CPU训练时把device设成cpu别让它去找 GPU 报错。yolo detect train \ datahighfall_yolo/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns/highfall \ nameexp1逻辑说明modelyolov8n.pt用官方预训练权重做迁移学习259 张图从零训基本没戏必须靠预训练。imgsz640是默认输入尺寸高空抛物目标小可以试 960 但显存翻倍。batch16在 8G 显存上比较稳爆显存就降到 8。device0指第一块 GPUCPU 改成cpu。project和name决定日志和权重存哪。4.2 关键参数逐个说imgsz、batch、lr0、patience参数不是越多越好高空抛物这个场景真正影响结果的就那么几个。下面这张表是我反复试出来的经验值直接抄。参数建议值作用调整信号imgsz640小目标试 960输入分辨率目标框普遍小于 32px 就调大batch8~16批大小爆显存就减半lr00.01初始学习率loss 震荡就降到 0.001patience30早停轮数数据少时别设太小epochs100~200训练轮数看 mAP 是否还在涨mosaic1.0马赛克增强小目标多时保留别关lr0是最容易翻车的参数。高空抛物数据量小学习率大了 loss 直接飞表现为前几个 epoch loss 变成 nan 或者几百。遇到这种情况别慌把lr0降到 0.001 重跑。patience30意思是 30 轮验证指标不涨就停259 张图建议设 30~50太小会提前停。4.3 从 results.csv 和日志里读出模型到底学没学会训练时终端会刷一堆指标别只看最后一行。真正要盯的是runs/highfall/exp1/results.csv里面有每轮的 box_loss、cls_loss、mAP50、mAP50-95。正常曲线是 loss 稳步下降、mAP 稳步上升最后趋于平缓。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/highfall/exp1/results.csv) df.columns [c.strip() for c in df.columns] fig, axes plt.subplots(1, 2, figsize(12, 4)) axes[0].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[0].plot(df[epoch], df[train/cls_loss], labelcls_loss) axes[0].legend(); axes[0].set_title(loss) axes[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) axes[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) axes[1].legend(); axes[1].set_title(mAP) plt.savefig(train_curve.png, dpi150)这段脚本把 loss 和 mAP 画出来比盯终端直观得多。判断标准box_loss 降到 0.5 以下、mAP50 能到 0.7 以上说明模型基本学到了如果 mAP50 一直在 0.2 以下晃要么数据标注有问题要么学习率不对。高空抛物因为目标小、样本少mAP50 能到 0.6~0.8 就算这个数据集的合理上限别拿 COCO 的标准要求它。注意results.csv的列名在不同 ultralytics 版本里可能带空格读进来先 strip 一遍否则 KeyError 找半天。5. 高空抛物训练避坑5 个我真实踩过的坑这一章全是血泪经验每条按「现象 → 原因 → 解决」写你照着排查能少走很多弯路。坑一loss 一直是 nan。现象是训练第一个 epoch 就打印 nan。原因八成是标签里有越界坐标或宽高为 0 的框归一化后出现非法值。解决回到第 2 章的检查脚本把所有异常框清掉或者用第 3 章脚本重新转一遍转换时已经做了裁剪。坑二mAP 死活上不去一直 0.1 左右。现象是 loss 在降但 mAP 不动。原因通常是类别索引错位——data.yaml里names的顺序和 txt 里的class_id对不上模型学的是错的映射。解决打印几张图的 txt 和对应 XML人工核对 class_id 和 name 的对应关系。坑三验证集指标比训练集还高。现象是 val 的 mAP 高于 train。原因一般是验证集太小259 张里才 50 来张且和训练集分布太像或者划分时没打乱。解决确认划分用了随机种子必要时做交叉验证别被虚高的数字骗了。坑四模型只框大目标小目标全漏。现象是画面里明显的坠落物框不住。原因是imgsz640下小目标特征在下采样中丢了。解决把imgsz提到 960 或 1280同时确认 mosaic 增强没关mosaic 能增加小目标的出现频率。坑五推理时框的位置整体偏移。现象是框比实际目标偏一圈。原因多半是训练时的 letterbox 填充和推理时不一致或者你拿 VOC 绝对坐标直接当 YOLO 用了。解决统一用 ultralytics 的推理接口别自己写预处理确认标签是归一化后的相对坐标。6. 榨干那 6 段视频抽帧补数据与推理验证的完整闭环259 张图训出来的模型直接拿去跑视频大概率会失望——因为视频里的光照、角度、背景和训练图不完全一样。但这个数据集附带的 6 段视频恰恰是最有价值的资源用好了能把有效数据翻倍。这一章讲怎么用视频抽帧补数据以及怎么用模型跑推理做闭环验证。6.1 用 OpenCV 抽帧并去重把视频变成训练数据视频抽帧的关键不是抽得多而是抽得不重复。高空抛物视频里大量帧是静止背景全抽出来只会让数据集充满冗余负样本。我的做法是按间隔抽再用简单的帧差去重。import cv2 import os import numpy as np video_dir videos out_dir frames os.makedirs(out_dir, exist_okTrue) frame_interval 10 # 每 10 帧抽一张 diff_threshold 2.0 # 帧差均值阈值低于它认为画面没变化 for vid in os.listdir(video_dir): if not vid.endswith((.mp4, .avi)): continue cap cv2.VideoCapture(os.path.join(video_dir, vid)) idx 0 saved 0 prev_gray None while True: ret, frame cap.read() if not ret: break if idx % frame_interval 0: gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if prev_gray is not None: diff np.mean(cv2.absdiff(gray, prev_gray)) if diff diff_threshold: idx 1 continue prev_gray gray name f{os.path.splitext(vid)[0]}_{idx:05d}.jpg cv2.imwrite(os.path.join(out_dir, name), frame) saved 1 idx 1 cap.release() print(f{vid}: 抽帧 {saved} 张)逻辑说明frame_interval10控制抽帧密度视频 30fps 的话相当于每秒 3 张。diff_threshold是帧差均值低于它说明画面几乎没变跳过。这两个参数要按视频实际长度调视频短就减小 interval。抽完的帧需要人工标注才能进训练集别指望自动生成标签——高空抛物目标小自动标注误检率很高。6.2 用训练好的模型跑视频推理验证真实场景表现抽帧补完数据、重新训一轮后用模型直接跑视频看它在连续帧上的表现。这一步能暴露静态图片评估发现不了的问题比如目标运动模糊、帧间抖动。yolo detect predict \ modelruns/highfall/exp1/weights/best.pt \ sourcevideos/test.mp4 \ conf0.25 \ iou0.45 \ saveTrue \ projectruns/predictconf0.25是置信度阈值高空抛物宁可漏检也别误检太多可以先设 0.3 看效果。iou0.45控制 NMS 重叠阈值。跑完在runs/predict下看输出视频重点观察三件事目标出现的前几帧能不能框住、快速下落时框会不会丢、背景里有没有误检。如果误检多提高conf如果漏检多降低conf同时回头补小目标训练数据。6.3 一个判断数据集值不值得投入的土办法最后给你一个我常用的判断习惯拿到任何高空抛物数据集先花十分钟做「最小闭环」——转格式、训 20 epoch、跑一段视频。如果 20 epoch 后 mAP50 能到 0.4 以上说明数据质量过关值得继续投入标注和调参如果 20 epoch 还是 0.1 上下别急着加数据先回去查标注和类别映射八成是数据本身有问题。259 张图加 6 段视频这个体量做算法验证和 demo 完全够但真要上生产你得按同样的流程把抽帧标注的量级做到几千张并且覆盖不同时段、不同天气、不同楼层的画面。我自己的习惯是每补一批数据就重跑一次这个最小闭环用同一套参数对比 mAP涨了就留不涨就查原因别凭感觉堆数据。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站