简介:本资源是面向计算机视觉算法工程师与深度学习初学者的广告牌目标检测专用数据集,聚焦商场、建筑及道路边等典型城市场景中的广告牌识别任务,可直接用于YOLO、Faster R-CNN等主流模型的训练与评估。压缩包共2000个文件,包含1997张640×640分辨率JPG图像及3个COCO格式JSON标注文件(含train/val划分及完整类别定义),总大小431.18MB,结构简洁、开箱即用。已有492人下载学习,适用于模型微调、数据增强实验及小样本泛化能力验证。所有图像均来自真实城市环境采集,覆盖多角度、多光照、多尺度广告牌实例,标注严格遵循COCO规范,支持直接导入Detectron2、MMDetection等框架,附带标准目录结构与标签映射说明,大幅降低数据预处理门槛。 做户外广告监测项目那阵子,甲方一开口就要识别商场外墙、道路两侧和建筑附属的所有广告牌,说是要给投放效果做量化评估。我第一反应是拿现成目标检测模型跑一跑,翻遍公开数据集才发现,通用物体检测里根本没有广告牌这个类目,COCO 的 billboard 类别少得可怜,粗糙到基本没法用。就在这时候看到了这个广告牌识别数据集:8157 张图、7137 张训练集、640x640 分辨率、COCO JSON 标注,几乎是把广告牌识别从零到一到上线部署的完整闭环都覆盖了。这篇我把自己跑这个数据集的完整过程写出来,从数据体检、标注格式剖析到训练调参和落地部署,尽量把每一步背后的"为什么"讲透。
1. 广告牌识别数据集拆解:8157 张图的含金量在哪里
1.1 为什么广告牌值得单独做一个数据集
很多人觉得广告牌不就是"大一点的目标"嘛,用通用检测模型框出来就行。真正做下去才知道,广告牌识别是个非常典型的"看起来简单、做起来全是坑"的细分方向。
先说尺度问题。一块商场外墙的巨型广告牌可能占整张图 40% 以上的面积,而路边的小型灯箱广告牌可能只有几十个像素,这两者在同一个检测任务里同时出现,对模型的尺度适应性要求很高。再说外观多样性,广告牌有喷绘布、LED 屏、灯箱、玻璃贴膜、立体字等多种形式,白天强反光、夜晚自发光、雨天玻璃有水渍、雾天整体对比度下降,这些都会导致同一个广告牌在不同时间、不同天气下的视觉特征差异极大。更麻烦的是多语言环境,国内广告牌混排中文、英文、数字的情况非常常见,文字在画面上密集排列时会严重干扰检测器的特征提取。
通用数据集里的"广告牌"类目往往只是把大型户外广告板当作背景中的附属物体随便框一下,标注质量和覆盖面都达不到实际项目要求。一个专门为广告牌识别打造的数据集,需要覆盖不同场景、不同光线、不同尺度、不同拍摄角度,才能在真实项目里派上用场。这个数据集选择了"商场、建筑、道路边"三个最核心的场景,切中的正是户外广告投放最密集、商业价值最高的物理空间。
1.2 8157 张图的规模在目标检测里算什么水平
8157 张图,7137 张训练集。这个数字放在今天的大模型时代听起来不大,但在目标检测任务里,尤其是针对单一类目的垂直场景数据集,8000 张图已经是相当实用的规模。
用数据说话。COCO 数据集有 33 万张图,但那是涵盖 80 个类目的通用数据集,平均到单个类目也就几千张。VisDrone 这个非常出名的无人机视角目标检测数据集,训练集约 6471 张图。BDD100K 自动驾驶数据集训练集约 7 万张,但那是包含 10 个大类的复杂驾驶场景。对于一个单类目(或极少数类目)的广告牌检测任务来说,7000 多张训练图配合合理的数据增强,足以训练出一个 mAP 在 70-80 甚至更高的检测模型。
更重要的是,这个数据集的图片不是那种"纯数据竞赛"式的干净图片。从分布来看,它采集的是真实的商场、建筑和路边场景,包含大量自然拍摄条件下的光照变化、遮挡、透视畸变和复杂背景。这种"脏数据"恰恰是模型落地时最需要的。
1.3 640x640 分辨率为什么是关键选择
640x640 这个分辨率绝不是随便定的。熟悉目标检测的都知道,YOLOv5 之后官方默认的训练分辨率就是 640x640,Ultralytics YOLOv8 同样将 imgsz=640 作为默认值。选择 640 意味着拿到的数据集不需要进行大幅度的尺寸适配,可以直接塞进主流的训练管线。
从计算量的角度看,640x640 是精度和速度的平衡点。分辨率越高,小目标的检测精度越高,但训练和推理耗时呈平方级增长。以 YOLOv8m 为例,输入从 640 提升到 1280,FLOPs 会增加约 4 倍,推理速度下降一半以上。而广告牌虽然大小不一,但经过前面说的尺度分析,绝大多数广告牌在 640 分辨率下已经能够保留足够的纹理和边缘特征。对路边的远距离小广告牌,还可以通过 tiling 切图或 SAHI 推理来补足,这个后面细说。
另外提一点,640x640 意味着源图大概率是被等比缩放或居中裁剪过再补齐的。做数据预处理时要注意的是,如果原始图片是 1920x1080 的横构图直接 resize 到 640x640,会产生严重的宽高比畸变。我拿到这个数据集的第一时间就会随机抽几张图看看,确认是 letterbox 补边还是直接拉伸,这直接关系到训练时的预处理策略是否要调整,不然后期部署到视频流时会发现框的位置偏差得莫名其妙。
2. 拿到数据集之后,先给 COCO JSON 做一次"体检"
2.1 COCO 标注格式到底长什么样
COCO 数据集格式是目标检测领域最常见的标注格式之一,除了标注工具普遍支持外,最核心的原因是它的结构设计足够通用和规范。一个标准的 COCO JSON 文件包含五个顶级字段:info、licenses、images、annotations、categories。
info 字段是数据集的元信息,包括数据集描述、版本、创建时间等,属于"数据集的身份证"。licenses 字段记录图片的版权许可信息,商用前一定要检查。images 字段是图片清单,每条记录包含图片 id、文件名、宽、高和下载地址。annotations 字段是标注信息正文,最核心的是 image_id、category_id、bbox 和 segmentation。categories 字段定义了所有类目,结构上支持多个类别各自拥有独立的 id 和名称。
在这个广告牌数据集中,categories 的数量需要看具体 JSON 文件。如果只有广告牌一个类目,那就是单类目标检测;如果包含多个类别,则需要额外注意不同类别的样本均衡问题。实际项目中遇到过某些数据集把"广告牌"拆成"大广告牌"和"小广告牌",或者混入"招牌"和"标识",类目体系一旦设置得不稳定,后续所有训练评估都会跟着混乱,这是我强烈建议先确认的事情。
2.2 annotations 字段里的核心细节
annotations 里的核心概念是 bbox,也就是目标框。COCO 格式的 bbox 是一个四元组 [x, y, width, height],注意这里的 x、y 是目标框左上角的坐标,width 和 height 是框的宽和高,单位是像素。这个定义和 YOLO 格式里常用的中心点坐标 (cx, cy, w, h) 不同,转换的时候最容易出错的也在这里。
另一个关键字段是 area,表示目标框的面积。COCO 官方工具包计算 mAP 时会利用 area 把目标分为小目标(小于 32x32)、中目标(32x32 到 96x96)和大目标(大于 96x96),分别统计 AP-S、AP-M、AP-L。做广告牌识别时一定要关注这三项指标,如果 AP-S 远低于 AP-L,说明模型对远处小广告牌的检测能力不足,需要针对性处理。
segmentation 字段在检测任务里可以留空或使用 RLE 编码的多边形,但如果是同一个物体被另一个物体遮挡,COCO 格式的标注框会包含被遮挡的部分,而分割掩码能精确地只标注可见区域。如果之后想把检测任务升级为分割任务,这个字段的价值就体现出来了。
2.3 用 Python 做一次完整的标注体检
拿到数据集,我建议不要急着开训练,先写一个简单的 Python 脚本做数据体检。以下是我常用的检查逻辑,可以快速找出大多数标注问题:
import json from collections import Counter from pathlib import Path with open("annotations.json", "r", encoding="utf-8") as f: coco = json.load(f) # 1. 图片数量、标注数量、类目数量 img_ids = [img["id"] for img in coco["images"]] ann_count = len(coco["annotations"]) cat_count = len(coco["categories"]) print(f"图片数: {len(img_ids)}, 标注数: {ann_count}, 类目数: {cat_count}") # 2. 检查每张图的标注数分布 ann_per_img = Counter() for ann in coco["annotations"]: ann_per_img[ann["image_id"]] += 1 print(f"平均每张图标注数: {sum(ann_per_img.values()) / len(ann_per_img):.2f}") # 3. 检查 bbox 是否超出图像边界 out_of_bounds = 0 for ann in coco["annotations"]: img_info = next(img for img in coco["images"] if img["id"] == ann["image_id"]) x, y, w, h = ann["bbox"] if x < 0 or y < 0 or x + w > img_info["width"] or y + h > img_info["height"]: out_of_bounds += 1 print(f"越界标注数: {out_of_bounds}") # 4. 检查 bbox 宽高为 0 的坏标注 bad_boxes = sum(1 for ann in coco["annotations"] if ann["bbox"][2] <= 0 or ann["bbox"][3] <= 0) print(f"宽高非正的坏标注数: {bad_boxes}") # 5. 检查是否所有图片都有至少一个标注(完全没标注的图会影响训练) unlabeled_imgs = [img["id"] for img in coco["images"] if img["id"] not in ann_per_img] print(f"无标注图片数: {len(unlabeled_imgs)}")这段脚本干了几件事:核对基本数量关系;统计每张图的标注密度,如果大量图片只有一两个标注、少数图片有几十个标注,说明数据存在严重的正样本分布不均;检测越界和非法框,这是最常见的标注错误。
还要注意一种不太容易发现的问题:重复标注。不同标注框如果是完全相同的坐标,大概率是标注工具或数据处理时产生的重复,训练时等价于给同一个目标重复加权。另外,多边形的标注如果坐标点过密,会显著增大 JSON 文件体积并拖慢训练时数据加载速度,遇到这种情况需要做抽稀处理。
2.4 检查训练集和验证集的相似度
7137 张训练集意味着剩余约 1020 张图应该是验证集或测试集。这里最关键的问题是:训练集和验证集是否存在"近重复"图片。
如果同一场景、同一广告牌的不同拍摄帧被同时分到了训练集和验证集,训练时模型已经在隐含地"记住"了验证集的目标,得到的评估分数会虚高。落地时换到新场景,效果直接打折扣。我一般用图像感知哈希对全部图片做相似度对比,或者直接看文件名和图像 hash 是否有明显规律,这是成本最低的检查手段。
3. 广告牌识别数据集里的三类核心场景,分别怎么打
3.1 商场广告牌:画面最复杂,标注难度最高
商场场景的广告牌识别,最大的问题是画面元素密度极高。商场外墙往往同时存在品牌 logo、巨型 LED 屏幕、玻璃幕墙上的贴膜广告、以及门口的多层灯箱。即便是同一个商场,不同区域、不同楼层的广告牌在尺度和内容上天差地别。
在训练这种数据时,建议把注意力放在"区分广告牌和背景纹理"上。商场外墙的装饰线条、玻璃反光、甚至建筑本身的几何结构,都可能被模型误判为广告牌。一个实用的做法是,在训练时给商场类图片适当提高负样本比例,或者在数据增强中加入更强的色彩扰动,打破模型对特定商场的颜色记忆。如果数据集里没有单独的负样本(没有广告牌的纯商场图片),后期自己补拍一些盲测负样本也是很有价值的补充。
3.2 建筑附属广告牌:多尺度和透视畸变的重灾区
建筑广告牌通常张贴在楼体表面,从地面拍摄时不可避免会有严重的透视畸变。广告牌的实际形状是长方形,但在照片里可能呈现为梯形甚至任意四边形。COCO 标注用的 axis-aligned bbox(轴对齐矩形框)不可避免地会把背景部分包进来。
面对这类问题,除了把检测模型当作基础手段外,还可以考虑两个进阶方向:一是评估工具更全面地看待标注质量;二是在后处理时对检测框加上旋转校正或透视校正模块,把检测出的矩形区域透视变换为规整矩形,再做 OCR 或广告内容识别,投放效果分析会更准确。
3.3 道路边广告牌:小目标密集,遮挡多
道路场景的广告牌种类最杂:路灯杆上的小灯箱、公交站台广告牌、高架桥旁的立柱广告牌、路牌式广告等。这些广告牌往往距离拍摄者较近,但尺寸小、互相遮挡,还与行人、车辆存在交错。
对道路场景,我个人比较推荐在训练时将这类样本适当过采样,或者用 Mosaic 增强时把这些小广告牌分配给更靠近中心的位置,让模型有更多机会学习小目标的特征。如果有条件,还可以用 SAHI(Slicing Aided Hyper Inference)在推理阶段做滑窗切图,把 640x640 的图切成 320x320 的块,分别推理后再 NMS 合并,小目标召回率能明显提升。
以下是我整理的三个场景的核心差异对照表,方便做针对性策略时快速查阅:
| 对比项 | 商场广告牌 | 建筑附属广告牌 | 道路边广告牌 |
|---|---|---|---|
| 画面元素密度 | 极高,背景复杂 | 中等,背景相对规则 | 高,人和车频繁出现 |
| 典型尺度 | 偏大,多为中大型目标 | 跨度大,从大型楼体到小型招牌 | 偏小,小目标占比高 |
| 常见干扰 | 玻璃反光、LED 变色、装饰线条 | 透视畸变、楼体立面纹理 | 遮挡、运动模糊、夜间灯光 |
| 检测难点 | 广告牌与背景纹理难区分 | 框体包含无关背景 | 小目标特征不足、漏检率高 |
| 优先策略 | 负样本 + 色彩扰动增强 | 旋转增强 + 后处理校正 | 过采样小目标 + 滑窗推理 |
4. 把 COCO JSON 转成 YOLO 格式,一次跑通训练流程
4.1 COCO 转 YOLO 的核心公式和常见坑点
拿到 COCO JSON 格式的数据,要训练 YOLOv8 就必须先转成 YOLO 的 txt 标注格式。YOLO 格式的关键内容是:每个 txt 文件中每行一个目标框,格式为 "class_id cx cy w h",其中 cx、cy 是目标中心点的相对坐标,w、h 是目标宽高的相对值,所有数值都归一化到 0 到 1 之间。
从 COCO 到 YOLO 的转换公式如下:
x_coco, y_coco, w_coco, h_coco = ann["bbox"] img_w = img["width"] img_h = img["height"] cx = (x_coco + w_coco / 2) / img_w cy = (y_coco + h_coco / 2) / img_h w = w_coco / img_w h = h_coco / img_h这里最容易踩的坑有三个。第一,COCO 的 bbox 是左上角坐标加宽高,不是中心点坐标,很多人第一次转换时直接拿 x_coco 当作 cx 用,导致所有框整体发生偏移。第二,类别 id 需要重新映射:COCO 的 category id 往往从 1 开始,而且不一定连续;YOLO 要求类别 id 从 0 开始连续编号。第三,训练时要安装正确的数据集配置文件,路径随便写会直接报错。
4.2 完整转换脚本和数据集目录组织
我一般会写一个转换脚本,一次把整个 COCO JSON 拆分成 YOLO 格式的 txt,同时生成 YOLOv8 需要的 data.yaml。下面是一个可直接运行的单文件版本:
import json import os from pathlib import Path from collections import defaultdict def coco_to_yolo(coco_json_path, output_dir, subset): with open(coco_json_path, "r", encoding="utf-8") as f: coco = json.load(f) images = {img["id"]: img for img in coco["images"]} cat_id_map = {cat["id"]: idx for idx, cat in enumerate(coco["categories"])} img_dir = Path(output_dir) / "images" / subset label_dir = Path(output_dir) / "labels" / subset img_dir.mkdir(parents=True, exist_ok=True) label_dir.mkdir(parents=True, exist_ok=True) anns_by_img = defaultdict(list) for ann in coco["annotations"]: anns_by_img[ann["image_id"]].append(ann) for img_id, img in images.items(): src = Path(img["file_name"]) dst_img = img_dir / src.name # 如果图片已经按 train/val 分目录存放,这里做复制或软链 if src.exists(): dst_img.symlink_to(src.resolve()) # 也可以改成 copy2 label_path = label_dir / (src.stem + ".txt") with open(label_path, "w", encoding="utf-8") as f: for ann in anns_by_img[img_id]: cls_id = cat_id_map[ann["category_id"]] x, y, w, h = ann["bbox"] img_w = img["width"] img_h = img["height"] cx = (x + w / 2) / img_w cy = (y + h / 2) / img_h w_norm = w / img_w h_norm = h / img_h f.write(f"{cls_id} {cx:.6f} {cy:.6f} {w_norm:.6f} {h_norm:.6f}\n") print(f"[{subset}] 处理完成: {len(images)} 张图, {len(anns_by_img)} 张有标注") coco_to_yolo("annotations/train.json", "datasets/billboard", "train") coco_to_yolo("annotations/val.json", "datasets/billboard", "val")这个脚本的核心逻辑很直接,但有几个细节值得展开说。使用 symlink 可以避免复制图片浪费磁盘空间,但注意有些云训练环境不支持软链,那时需要改成 shutil.copy2。数据集目录按 YOLO 惯例组织为 datasets/billboard/{images,labels}/{train,val} 的结构,data.yaml 里可以直接写相对路径指向同级目录,省得在多个项目间迁移时还要改绝对路径。
4.3 训练参数怎么设,mAP 才扛得住
数据准备好了,接下来就是训练。用 Ultralytics YOLOv8 的话,建议先用官方预训练权重做迁移学习,而不是随机初始化从头训练。广告牌虽然是个垂直场景,但底层特征(边缘、纹理、颜色)和 COCO 预训练模型学到的东西是通用的,迁移学习能大幅缩短收敛时间并提升最终精度。
以 YOLOv8m 为例,一个比较稳的起步配置:
# billboard.yaml path: datasets/billboard train: images/train val: images/val nc: 1 names: ["billboard"]训练命令:
yolo detect train \ data=billboard.yaml \ model=yolov8m.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ optimizer=SGD \ augment=True \ patience=20 \ project=runs/billboard \ name=exp1这些参数里,batch 取决于显卡显存。以 12GB 显存为例,YOLOv8m 在 640x640 下 batch=16 基本能跑,但如果你开了 Mosaic 增强,建议降到 8,否则容易 OOM。lr0 用 0.01 是迁移学习里比较稳的保守值,如果你打算从头训练,可以调到 0.001。patience=20 表示连续 20 个 epoch 验证集指标没有提升就早停,这个数据量级下通常在 60-80 个 epoch 左右就能收敛。
训练过程中要盯几个关键指标。train/box_loss、train/cls_loss 和 train/dfl_loss 应该逐步下降;val/box_loss 等验证损失如果出现先降后升的 U 型曲线,说明过拟合了;metrics/mAP50 和 metrics/mAP50-95 是最终质量指标。对于广告牌这种目标轮廓比较规整的检测任务,mAP50-95 在 0.6 以上算是可用水平,0.7 以上就是很不错的模型了。如果 mAP50 高但 mAP50-95 低,说明模型虽然能框住目标,但框的定位精度不够精细,后续可以加大 DFL loss 的权重或者换用更关注边界框精度的变体模型。
4.4 第一次训练跑出来的常见问题
训练集只有 7137 张,模型可能出现的典型问题是过拟合。一个明显信号是 train loss 持续下降,但 val loss 在某个 epoch 后开始反弹,同时 mAP 曲线进入平台期甚至回落。这个时候不要急着加数据,先检查:
- 验证集图片是否真的和训练集图片来自不同场景。如果是同一批镜头拍摄的连续帧,评估结果会虚高。
- 是否有多余的增强导致"增强分布"和真实场景严重不符。比如广告牌是静态物体,如果加了重度随机透视和旋转,会让模型学到错误的几何先验。
- 类别是否只有广告牌一个 class。如果后面想加"LED 屏幕""灯箱"等子类别,需要在标注阶段就规划好,而不是训完再改。
我还遇到过一种比较隐蔽的情况:数据集里部分图片是直接从视频抽帧得到的,相邻帧的广告牌几乎一样,导致模型对"同一个广告牌"过拟合,换个广告牌就漏检。处理方法是按视频片段分组,确保同一个视频的帧只进训练集或只进验证集,不能两边都有。
5. 广告牌识别的难点,和通用目标检测真不一样
5.1 广告牌的高宽比极端到离谱
广告牌的形状分布极不均匀,有超宽幅的横版(电视塔上的环形广告屏),也有极高的竖版(商场外墙的巨幅海报)。通用目标检测的 prior box 设计通常覆盖从 0.5 到 2 的常见宽高比,但广告牌的宽高比可以轻松到 4:1 甚至 6:1。
在 YOLOv8 这类 anchor-free 架构里,极端宽高比会导致回归目标在中心点和边框距离的计算上产生较大误差,模型往往把高瘦或宽扁的广告牌框得过大或过小。缓解手段有两个方向:一是训练时对极端比例的样本做更多裁剪,让模型看到更多局部细节;二是推理后处理时加入形状先验,把明显不符合广告牌比例的检测框过滤掉。比如一个宽高比小于 0.1 或大于 10 的框,基本可以判定是误检。
5.2 夜间和反光是广告牌的"隐身衣"
广告牌在夜间的视觉特征和白天完全是两个东西。白天它是反射环境光的喷绘布或灯箱,晚上 LED 屏和霓虹灯管才是主角,整个物体的颜色分布、亮度和对比度都变了。如果训练集里白天图片占绝大多数,夜间检测效果会明显下滑。
数据层面可以做两件事:一是用黑夜模拟增强,在 HSV 空间把亮度通道乘一个 0.3-0.6 的系数,并稍微提高饱和度,模拟夜景;二是做曝光增强,模拟灯箱高亮和玻璃反光。训练时加入这些增强后,模型的鲁棒性会显著提升。
反光是另一个麻烦。阳光直射广告牌时,喷绘布表面会出现高光区域,高光部分的纹理几乎完全丢失,模型看起来就像一块纯色区域。玻璃幕墙上的广告牌更是重灾区,反光会把后面的建筑、天空甚至行人折射到广告牌上,让模型分不清哪个是广告内容。
对这种问题,单纯靠数据增强效果有限,更实用的方案是"检测+识别"串联架构:先用检测模型找到广告牌区域,再用 OCR 或图像检索模型识别广告内容。检测只需要找到"这里有个广告牌",识别环节才需要处理内容,这样即使反光导致内容识别不准,位置框一般还是准的,整体业务指标不会崩太多。
5.3 反复出现在画面里的同一块广告牌,不算重复数据
广告牌识别的另一个特点是"同一物体反复出现"。同一个商场的同一块广告牌,在不同时间、不同天气、不同拍摄角度下会出现几十次,但广告内容本身没变。这种数据对模型的"实例学习"有帮助,但也会让模型对特定广告内容产生记忆。
训练时如果发现某个广告牌在训练集里出现次数过多,验证时就会造成一种"伪正确":模型并不是学会了识别广告牌,而是学会了识别某个具体的广告画面。要检验模型的泛化能力,可以把同一广告牌的所有图片单独分一组,训练集、验证集按照广告牌实例分组而不是按图片分组,这样评估结果才反映真实的泛化水平。
6. 落地部署时,你还需要知道这些
6.1 从图像检测到视频检测,要处理哪些变化
广告牌识别在实际项目中往往不是对单张图片做检测,而是对视频流做实时分析。这时会出现静态图片训练时完全遇不到的问题:镜头抖动、画面切换、广告牌内容动态变化(LED 屏播放不同的广告内容)、以及不同摄像头之间的白平衡和色彩风格差异。
我一般在视频部署时会用跳帧检测加追踪关联的思路:不是每帧都跑一次检测网络,而是每隔几帧跑一次,中间帧用追踪算法(ByteTrack 或 BoT-SORT)平移预测广告牌位置。这样节省算力的同时,还能保证广告牌的 ID 是连续稳定的。如果一个广告牌在画面里持续出现,就能统计它的曝光时长、出现频率,这些才是投放决策真正需要的数据。
6.2 光照、天气和季节,直接影响模型寿命
广告牌数据集的图片拍摄时间和季节分布,直接影响模型的季节适应性。夏季树叶茂盛,可能会挡住路边广告牌的下半部分;冬季树叶落光,广告牌完整露出来,框的位置和大小差异很大。太阳高度角的变化导致阴影位置不同,早晨和傍晚的广告牌侧光、逆光效果截然不同。
如果项目要求全年稳定识别,建议给数据集补充不同季节的图片,或者在训练时加入模拟不同光照方向的增强。我之前一个户外广告项目就是没注意这个问题,夏天调试得很好,到了冬天发现同一块广告牌在早上 9 点到 11 点这个时间段大面积误检,排查半天才发现是低角度阳光把喷绘布照成了接近背景墙的颜色。
6.3 用热词里找到的周边工具做补充
搜索这个数据集相关内容时,经常能联想到一些同样优秀的周边资源——比如 YOLOv8 训练自己的数据集、mmrotate 处理旋转目标、SAHI 做小目标推理、SAHI 在高分辨率图像上的适配。广告牌识别里如果你对旋转框感兴趣,用 mmrotate 的旋转检测来替代 axis-aligned bbox,对楼体倾斜角大的广告牌会有明显收益,但要注意旋转框标注成本比矩形框高很多,而且很多下游业务系统不支持旋转框输出。
对于纯小目标的道路场景,SAHI 基本是标配。它的思路很简单:训练时图片自己会做 resize,推理时则切成 640x640 的小块,相当于等效放大了小目标,但代价是推理耗时成倍增长。1280 分辨率大图切一次 640 推理,耗时大概是原图的 4 倍,所以实际部署时要根据摄像头数量和 GPU 算力做权衡,不是所有场景都值得上 SAHI。
7. 训练完以后,怎么判断这个数据集到底好不好用
跑完一轮训练,看到 mAP50-95 超过 0.7,很多人就觉得"数据集不错,模型没问题了"。这个结论下得太早了。我建议把输出模型拿到真实业务场景里做一次盲测,用那些没有出现在训练集和验证集里的真实照片做定性评估。
盲测时要特别关注几个维度:小广告牌的召回率是否够高;和广告牌颜色相近的建筑背景会不会误检,比如深灰色玻璃幕墙上的广告牌就经常被漏掉;极端天气条件下的置信度是否稳定,雨天、雾天、雪天模型的表现如何。如果这些测试都通过了,才可以说这个数据集真正支撑起了"商场、建筑、路边广告牌识别"这个目标。
从项目复用的角度看,这个数据集最大的价值不只是"8157 张已标注图片"这个数量本身,而是它把广告牌识别从通用目标检测里彻底独立了出来,形成了一套可以持续扩展的数据基础。后续想支持 LED 屏幕内容识别,在这个数据基础上补标注就行;想支持竖版广告牌检测,在现有数据里做方向增强就行。数据集的边界比较清楚,扩展起来才有章法。
最后再分享一个小技巧。如果项目对广告牌的定位精度要求很高,比如要算每个广告牌的实际长宽,建议在检测后加一步语义分割或者四边形检测,而不是只依赖轴对齐矩形框。广告牌在照片里经常是透视变形的,矩形框会引入大量背景像素,导致后续像素级换算误差很大。用检测框直接框矩形框定位广告牌区域,和用四边形框或者分割掩码定位广告牌区域,换算出来的物理尺寸可能相差 20% 以上,这个误差在广告计费场景里是没法接受的。
整个流程跑下来,我的体会是:广告牌识别这个方向,数据集的规模固然重要,但更关键的是对场景的理解和标注细节的把控。8157 张图、7137 张训练集、640x640 分辨率、COCO JSON 格式,这些参数组合起来是一个可以直接落地的基础设施。你在这个基础上做的每一次数据体检、每一个增强策略、每一次验证集分组优化,都会在真实场景的准确率上得到回报。
本文还有配套的精品资源,点击获取