ARTICLE DETAIL

资讯详情

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

广告牌检测数据集VOC+YOLO双格式114张,YOLOv8训练全流程

广告牌检测数据集VOC+YOLO双格式114张,YOLOv8训练全流程 简介面向城市管理与目标检测算法学习场景街道乱放广告牌检测数据集提供114张真实街景图片同时给出VOC与YOLO两种格式的标注框类别统一为广告牌共有165个矩形标注适合用于违规广告牌识别模型的训练与验证。资源包共344个文件包含114张jpg原图、114个xml标注文件及116个txt文件其中114个为YOLO格式标注整体仅8.12MB目录结构清晰便于快速加载与二次处理。目前已有312人学习下载数据量适中、格式规范既可作为目标检测入门练习数据也适合做数据增强或迁移学习实验使用labelImg工具标注矩形框规则明确有助于理解VOC与YOLO两种常用标注格式的对应关系。需要提醒的是数据集只保证标注准确合理不对模型精度作承诺使用者可按需划分训练集和测试集。1. 街道乱放广告牌检测数据集114张VOCYOLO双格式单类目标检测怎么落地城市管理巡检里街道乱放广告牌检测是一个比较典型的单类目标检测任务看着简单真做起来却有两道坎一是正样本要现场采集违规广告牌形态五花八门二是标注格式不统一换训练框架就得重新转格式。我最近拆了一份街道乱放广告牌检测数据集VOCYOLO双格式直接打包解压出来是114张jpg、114个xml、114个txt标注类别只有guangguang一个累计165个矩形框全部用labelImg人工画框完成。对想快速验证YOLOv5/YOLOv8训练流程的从业者这份数据省掉了爬图、清洗、标注、转格式的重复劳动对做城管告警、门店违规识别项目的人来说可以作为冷启动阶段的预训练样本。下面从解压、校验到训练、避坑把整个流程过一遍。2. 拆开压缩包看标注细节VOC与YOLO两种格式的目录结构、xml字段与归一化换算2.1 双格式目录结构jpg/xml/txt三者怎么对应拿到压缩包先不要急着解压训练先把目录结构摸清楚。这份数据集的文件名前缀是firc_jd后面跟编号我拆开之后看到的实际对应关系是每张图片有一个同名xml和一个同名txt三者散落在同一层目录里并不像很多开源数据集那样强制分成JPEGImages、Annotations、labels三个子目录。这对训练来说其实更自由你自己按需归类就行。文件类型数量内容说明jpg114现场采集的街道场景原图xml114Pascal VOC格式标注labelImg默认产物txt114YOLO格式标注每行一个目标框标注类别1guanggao广告牌总框数165全部为目标框平均每张约1.4个框为什么我强调“双格式”这件事因为labelImg保存时默认写VOC的xml而YOLO系列训练读的是txt。很多数据集只给其中一种你拿到后要么装labelImg重新导出要么到处找转换脚本。这份直接把两种都给齐了理论上解压后可以直接喂给YOLO训练。另外注意摘要里特别提了一句“不包含分割路径的txt文件”意思是这份txt不是YOLO分割格式的多边形polygon坐标而是每行一条检测记录别当成分割标注喂给yolov8-seg否则会直接报维度错误。2.2 VOC的xml标签字段object、size、bndbox逐一拆解VOC格式的好处是结构直观一个目标对应一个object节点。我拿其中一张图的结构做个示意字段含义如下annotation folderJPEGImages/folder filenamefirc_jd_13.jpg/filename size width1280/width height720/height depth3/depth /size object nameguanggao/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin100/xmin ymin80/ymin xmax340/xmax ymax260/ymax /bndbox /object /annotation这里的size是原图宽高和通道数bndbox是矩形框的左上角xmin/ymin和右下角xmax/ymax。truncated和difficult在labelImg里默认是0表示目标没有被截断、不算难例训练时这两个字段YOLO不读但保留着不影响转换。需要注意一点xml里的filename和实际jpg文件名必须完全一致包括字母大小写否则后面做数据划分时很容易出现“图片走了、标注没跟上”的情况。2.3 YOLO txt的归一化坐标从xml到txt的换算与类别映射YOLO格式的txt每一行是class_id x_center y_center width height坐标全部归一化到0到1之间。归一化公式我在实际项目里一般这样写def xml_to_yolo(xml_path, img_width, img_height): import xml.etree.ElementTree as ET tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): class_id 0 # 本数据集只有guanggao索引固定为0 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_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return \n.join(lines)这段脚本的核心逻辑是先解析xml拿到bndbox四个坐标再除以图片宽高做归一化最后按class_id cx cy w h拼成一行。因为这份数据只有一个类别class_id固定写0如果你的训练data.yaml里把类别顺序改了这个数字也要跟着改。归一化后w和h是相对值理论上永远小于1如果跑出来某行w或h大于1说明xml里的bndbox越界了后面避坑章节会专门讲这个场景。3. 从7z到可训练数据集解压校验、框数核对与train/val划分脚本3.1 解压7zWindows与Linux两种方式及文件完整性检查压缩包是7z格式和zip不太一样Windows系统自带的资源管理器解压不了得装第三方工具。我一般在Windows上用7-Zip右键“解压到当前文件夹”就能看到jpg/xml/txt三组文件。Linux环境下用命令行# Ubuntu/Debian 先装 p7zip sudo apt install p7zip-full # 解压到目标目录 7z x 街道乱放广告牌检测数据集VOCYOLO格式114张1类别.7z -o./street_sign_dataset解压参数说明x表示保留完整目录结构解压-o后面跟输出目录注意-o和目录路径之间不要加空格加了会被识别成两个参数。解压完第一件事不是看图片而是数文件数量。我的习惯是跑一条ls | wc -l分别统计jpg、xml、txt的数量必须都是114。这种标注数据一旦中间缺一个txt训练时那张图就会被跳过而且你不会立刻发现等loss曲线异常才回头排查非常浪费时间。3.2 校验脚本xml与txt框数、文件名一对一核对解压正常不代表标注没毛病。我拆数据集的固定动作是先跑一个校验脚本确认三件事文件名一一对应、每个txt行数不为0、总框数等于165。import os from pathlib import Path img_dir Path(./street_sign_dataset) # 图片目录 xml_dir Path(./street_sign_dataset) # xml目录 txt_dir Path(./street_sign_dataset) # txt目录 imgs {p.stem for p in img_dir.glob(*.jpg)} xmls {p.stem for p in xml_dir.glob(*.xml)} txts {p.stem for p in txt_dir.glob(*.txt)} # 1. 三集合必须完全一致 print(jpg数量:, len(imgs), xml数量:, len(xmls), txt数量:, len(txts)) print(缺xml:, imgs - xmls, 缺txt:, imgs - txts) # 2. 每个txt不能是空文件且框数总和应为165 total_boxes 0 for t in sorted(txt_dir.glob(*.txt)): lines [line.strip() for line in t.read_text(encodingutf-8).splitlines() if line.strip()] total_boxes len(lines) if len(lines) 0: print(空标注:, t.name) print(总框数:, total_boxes) assert total_boxes 165, 框数不对检查标注文件这个脚本的逻辑不复杂但很实用先用Path.stem取文件名不带后缀的部分做差集判断再逐行读取txt统计框数。注意txt的编码要按UTF-8读如果解压工具在Windows上把文件名搞成乱码这里集合差集会直接暴露问题。实际跑这份数据时框数出来的结果就应该是165多一行少一行都要停下来查。3.3 按比例切分train/val并生成data.yaml校验通过后做数据划分。114张图不算多我一般按9:1切也就是train约103张、val约11张。如果是做正式项目这个比例还会再调整但对小样本来说val太多反而让训练集更稀疏。import random import shutil from pathlib import Path src Path(./street_sign_dataset) train_dir Path(./street_sign_dataset/images/train) val_dir Path(./street_sign_dataset/images/val) train_lbl Path(./street_sign_dataset/labels/train) val_lbl Path(./street_sign_dataset/labels/val) for d in [train_dir, val_dir, train_lbl, val_lbl]: d.mkdir(parentsTrue, exist_okTrue) jpg_files sorted(src.glob(*.jpg)) random.seed(42) random.shuffle(jpg_files) val_cnt int(len(jpg_files) * 0.1) val_files jpg_files[:val_cnt] train_files jpg_files[val_cnt:] def move_pair(file_list, img_dst, lbl_dst): for jpg in file_list: xml jpg.with_suffix(.xml) txt jpg.with_suffix(.txt) shutil.copy(jpg, img_dst / jpg.name) shutil.copy(xml, lbl_dst / xml.name) shutil.copy(txt, lbl_dst / txt.name) move_pair(train_files, train_dir, train_lbl) move_pair(val_files, val_dir, val_lbl) print(train:, len(train_files), val:, len(val_files))这里我用的copy而不是move是给自己留后悔药——万一划分不均或者想重新切原目录还在。随机种子固定为42保证每次跑出来的划分结果一致方便复现。划分之后YOLO训练的data.yaml就能这样写path: /absolute/path/to/street_sign_dataset train: images/train val: images/val names: 0: guanggao提示path字段尽量写绝对路径。相对路径在YOLOv8里有时会因为启动目录不同而找不到图片报dataset not found排查起来很绕。4. 用YOLOv8跑通单类小样本训练dataset.yaml、增强参数与训练日志解读4.1 模型选型与dataset.yaml为什么单类114张优先选n或s处理数据集用于YOLOv8训练时第一关是选模型尺寸。很多人上来就选yolov8m甚至yolov8x觉得大模型精度上限高。但对114张、单类别、165框的数据集大模型几乎必然过拟合。我的经验是n和s之间选一个先跑通流程看loss和mAP如果欠拟合再往上加。模型参数量对114张小样本的适用性yolov8n约3.2M首选训练快过拟合风险低yolov8s约9.4M可试augmentation拉满时也稳yolov8m约25.9M不推荐数据量撑不起yolov8l/x43.7M/68.2M这个数据量基本别碰这个选择逻辑不复杂目标检测模型容量越大需要的数据和训练轮次越多。广告牌检测不是猫狗分类那种纹理极度丰富的任务n模型的特征提取能力已经足够覆盖“柜台上立一块牌子”这种中粒度目标。训练入口文件data.yaml也很简单类别guanggao对应索引0和txt里的class_id保持一致即可。4.2 训练命令与增强参数epochs、close_mosaic、batch怎么配合数据量小训练参数就得往“稳”字上靠。我实际跑这份数据的命令是yolo detect train \ datastreet_sign_dataset/data.yaml \ modelyolov8n.pt \ epochs200 \ imgsz640 \ batch8 \ patience30 \ close_mosaic10 \ lr00.005 \ cacheram参数说明modelyolov8n.pt会自动下载COCO预训练权重不用手动找yolo预训练模型下载地址epochs200对小样本足够多了反而记住噪声patience30表示30轮验证指标不涨就早停close_mosaic10很关键——mosaic增强在训练最后10轮自动关闭这是YOLOv8官方推荐的策略避免最后阶段还在拼图造成优化震荡lr0从默认0.01降到0.005小样本下初始学习率过高容易让BN层崩溃cacheram把图片缓存进内存114张小图完全放得下训练速度提升明显。增强参数如果默认效果不好我会在命令里追加参数默认值小样本建议作用hsv_h0.0150.02色相抖动模拟不同光照hsv_s0.70.8饱和度抖动应对黄昏/阴天degrees0.010.0轻微旋转广告牌不总是水平的translate0.10.2平移让目标出现在不同位置fliplr0.50.3左右翻转注意文字类目标慎用scale0.50.5缩放模拟远近变化fliplr对广告牌检测要谨慎如果路边广告牌本身带文字翻转后文字是反的但检测任务只看矩形框位置不看文字方向所以影响不大。真正要留意的是degrees别开太大广告牌在真实场景里很少大角度倾斜转太多反而制造伪样本。4.3 训练日志与混淆矩阵loss、P/R、mAP50的读法与常见误读训练完成后YOLOv8会在runs/detect/train/下生成results.csv里面每一列对应一个指标。我看这份数据时重点盯三个train/box_loss是否持续下降且无尖峰、metrics/mAP50(B)在val上是否稳定、metrics/precision(B)和metrics/recall(B)是否出现严重失衡。小样本单类任务常见的现象是recall很高、precision偏低因为模型把相似的招牌背景都当成广告牌。还有一个容易被误读的地方混淆矩阵。YOLOv8画出的混淆矩阵纵轴是真实类别占比横轴是预测类别占比格子加起来不等于100%很正常它表达的是“召回率视角”的分布不是联合分布。看到矩阵合计不是1别急着怀疑代码 bug先看是不是没做归一化。这属于yolo混淆矩阵里最常被问的坑。5. 避坑手册114张小样本广告牌检测的五个常见坑5.1 解压后文件名乱码或文件缺失现象用某些国产解压软件打开7z解压出的jpg文件名变成乱码或者xml/txt三件套少了一件。原因压缩包内文件名按UTF-8编码老版本解压工具按ANSI解码中文文件名和前缀符号被转义破坏。解决Windows一律用7-Zip 21以上版本Linux用p7zip解压解压完先跑一遍上文的集合差集脚本发现缺文件就从原包重新解压不要手工改名凑数。5.2 txt里出现全0坐标或宽高越界现象训练时日志提示某行标注框无效或者val的mAP一开始就是0。原因xml里的bndbox如果出现xmax等于xmin归一化后宽为0如果标注框超出了图片边界归一化后数值可能大于1模型训练时算loss直接算不下去。解决在校验脚本里加一条判断凡是w或h小于等于0、大于1的行单独打印定位到具体txt后人工回看图用labelImg修正对应xml再重新生成txt。这种问题通常在数据生产阶段就存在属于标注质量筛查不是训练配置能救的。5.3 val的mAP很高实拍视频却漏检现象验证集mAP50到0.9以上但拿现场手机拍的视频一测远处广告牌完全不识别。原因数据集里的原图拍摄距离偏近目标框占整图比例较大验证集和训练集来自同一采集批次分布高度相似评估结果虚高。实拍场景里目标小、亮度杂、背景乱分布漂移直接击穿模型。解决训练时可以用imgsz768提升小目标分辨率推理时对视频帧做ROI裁剪只检测街道两侧的固定区域同时加大实际场景数据的补充比例把每天巡检拍的新图按周回流训练集。5.4 把guanggao改成中文类别名后训练报错现象觉得类别名用拼音不够专业把data.yaml里的guanggao改成“广告牌”一跑训练就报错要么KeyError要么标签读取为空。原因Windows下记事本另存为时默认编码是ANSIGBKYAML按UTF-8读取时中文变乱码即使强迫保存为UTF-8cmd终端和Python解释器的编码环境不一致也会出问题。解决类别名保持英文或拼音最稳妥这是我在多个项目里的血泪经验实在要显示中文只改可视化标签映射不动训练文件里的names字段。5.5 训练中BN崩溃或loss出现NaN现象训练到一半train/box_loss突然变成nan之后的epoch全部无效。原因小样本下初始学习率偏高加上最后阶段mosaic增强拼接了大量不自然样本BN层的running_mean被带偏。解决把lr0降到0.003到0.005之间close_mosaic开到15到20轮batch固定不要忽大忽小如果还崩用yolo detect train ... freeze10把前10层backbone冻结只训练neck和head让训练更稳。这个毛病在yolo训练里不算罕见看到nan先查学习率别急着换模型。6. 验证与业务落地从mAP到置信度阈值与告警逻辑的联动6.1 导出ONNX并在业务侧做一次快速验证训练完的best.pt不能直接丢给线上服务建议先导成ONNX再用onnxruntime快速验证一张图yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出后用一段Python接口验证推理结果核心参数是置信度阈值和NMS的IoU阈值import cv2 import onnxruntime as ort import numpy as np sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name img cv2.imread(street_scene.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized cv2.resize(img, (640, 640)) blob resized.astype(np.float32) / 255.0 blob np.transpose(blob, (2, 0, 1))[None] out sess.run(None, {input_name: blob})[0] # out形状为 [1, 5nc, 8400]取第一维做后处理参数说明ONNX导出的输出是[1, 6, 8400]形状第一维batch第二维是cx cy w h obj_conf class_conf第三维是anchor解耦后的候选框数量。实际项目里我不会把后处理写死而是用ultralytics库里的YOLO直接加载onnx做推理减少重复造轮子。6.2 置信度阈值与连续帧告警业务侧怎么用模型输出的是概率业务侧要把它转成动作。我做城管告警类需求时置信度阈值一般按场景分三档阈值适用场景行为0.25初次检测、巡检抽查标记待确认不直接告警0.45正式业务告警触发工单0.60夜间或雨天严控误报只认高置信度低阈值带来的误报我用连续帧确认来过滤而不是直接调高置信度class AlarmWindow: def __init__(self, min_frames3, max_gap5): self.min_frames min_frames self.max_gap max_gap self.countdown 0 def update(self, det_conf): hit det_conf 0.45 if hit: self.countdown 1 else: self.countdown max(0, self.countdown - 1) return self.countdown self.min_frames这个逻辑很简单同一路视频流里连续3帧都检测到广告牌才触发告警中间漏一帧可以通过countdown缓冲不会立刻清零。相比单帧硬判它能挡住大部分行人路过、车辆遮挡造成的闪烁误报。从那以后我每次拿到一个标注数据集都强制先走一遍解压校验、划分、小参数训练、导出验证的流程再谈业务指标这份114张的广告牌数据集也是这样跑通的。希望帮到你。本文还有配套的精品资源点击获取
返回列表