简介街景门牌号数据集是面向计算机视觉研究与自动驾驶应用的专业数据集为AI研究人员与开发者提供门牌号识别模型的训练与评测基础。数据主体为街景图像及对应标注覆盖训练、验证、测试三个阶段能够支撑卷积神经网络等深度学习模型的搭建、参数调优与泛化能力验证适用于智能导航、智慧城市等真实场景。资源包共7个文件总量约885.18MB以3个zip图像压缩包、2个csv数据列表与提交样例、2个json标注文件构成各文件分工明确便于直接接入自定义数据加载流程。目前已有519人学习下载。使用该数据集用户可快速获得完整的数据划分与标注体系节省数据采集与清洗成本专注于模型结构设计与精度优化是开展图像识别实验或算法竞赛的有效数据支撑。1. 街景门牌号数据集门牌识别为什么从“图像问题”变成了“数据问题”门牌号在整张街景图里往往只有几十个像素大还叠着倾斜、反光、树枝遮挡这类干扰通用目标检测器经常把它当成背景直接漏掉。街景门牌号数据集的核心价值就是把这类难找的小目标整理成“图像定位框数字串”的标准训练语料让模型专门学会在混乱的城市背景里把门牌找出来并读对。搭建地图信息采信、物流末端配送、建筑地址入库这类管线的团队手里多半已经攒了一堆街景图真正缺的是一份标注规范清晰、切分不泄漏、能直接喂给检测器和OCR模型的数据集。本文从数据怎么建的讲起一路把模型选型、训练参数和踩坑记录写到能照着复现的程度。2. 街景门牌号数据集长什么样从抽帧、裁剪到一份可切分的目录2.1 抽帧与去重同一家门牌不能在训练里出现几十次街景采集通常以固定帧率录像我一般按“每行驶固定距离取一帧”来抽而不是按时间抽。城市道路每3米取一帧郊区或快速路每10米取一帧这样能保证门牌出现的密度基本均匀也不会因为车辆等红灯而把同一面墙连拍几十张。固定距离抽帧依然会产生大量近重复图像。车辆在路口排队时同一个门牌会以几乎相同的视角连续出现这些帧如果全部进训练集模型会对这一个样本过度拟合验证集上指标很漂亮到了陌生街道却失准。我实际操作时的做法是把连续帧里同一个门牌出现的片段合并成一个事件每个事件最多保留两帧一帧做训练一帧留在原地做数据回放。回放帧不参与训练只用于人工抽检。抽完帧还要做一层跨帧匹配。相邻帧之间用门牌框的重叠度判断是否属于同一块门牌重叠度超过0.8就归为同一个事件。这一步看起来费事但对后续按街道分组切分训练集非常关键否则数据泄漏问题会在模型评估时直接放大。匹配脚本可以用现成的帧间特征比对也可以用检测器先粗跑一遍再按框的位置聚类前者更快。2.2 标注规范框要框住门牌本体串要按从左到右读门牌的标注包含两部分定位框和门牌号字符串。定位框的边界规则直接决定后续OCR矫正质量。我给标注定的标准是上边压着号码牌顶端下边压着号码牌底端左右对齐数字边缘既不包含门框也不包含墙面。框得比门牌大一点OCR裁剪时就会带入大量背景干扰数字的纵向比例被压缩识别准确率明显下降框得太紧又会把号码的边缘笔画切掉尤其是“1”的顶部和“7”的尾部这类细笔画最容易丢。字符串标注按人眼从左到右的阅读顺序记录。门牌号里的“-”“栋”“单元”这类附加信息尽早决定是否纳入标注字典。为了保证这个数据集的通用性我建议把附加信息拆到独立字段主标注只留纯数字串。比如“12栋-3号”在数据里拆成“主号码3附加属性12栋”OCR模型只需要学十个数字加上一个CTC空字符字典小、收敛快误识别概率也低。位数分布是另一个容易被忽略的标注要求。一栋门牌可能是“5”也可能是“1204”四位数样本如果只占全部样本的1%模型大概率会把长串读短。标注阶段就引入按位数配额抽样的策略让一位数到四位数的样本比例落在3:4:2:1附近训练出来的OCR在长串上才不会系统性丢尾部数字。2.3 目录结构与格式落地一份可切分的标注样本一份结构清晰的街景门牌号数据集常见布局如下doorplate_dataset/ ├── images/ │ ├── street_A_0001.jpg │ ├── street_A_0002.jpg │ └── ... ├── annotations/ │ ├── street_A_0001.json │ ├── street_A_0002.json │ └── ... ├── roads.csv # 场景ID与道路ID映射用于防泄漏切分 ├── dict.txt # OCR字符字典0-9按需加blank └── data.yaml # 检测器训练配置JSON标注里至少要有门牌框的坐标、旋转角度、整串数字文本还有一个场景ID字段。场景ID对应拍摄时的地理位置聚类编号切分时按它分组随机切分在这里不适用。下面这段Python脚本完成从原始标注到YOLO训练格式的转换并按道路分组切分出训练集和验证集import json import random from pathlib import Path random.seed(2024) def collect_samples(ann_dir): samples [] for json_path in Path(ann_dir).glob(*.json): ann json.loads(json_path.read_text(encodingutf-8)) # 每个JSON至少包含 image_path、boxes、scene_id 三个字段 for box in ann[boxes]: samples.append({ image: ann[image_path], scene_id: ann[scene_id], xyxy: box[xyxy], # 门牌框像素坐标 text: box[text], # 纯数字门牌串 }) return samples def split_by_scene(samples, val_ratio0.15): # 关键点按scene_id分组而非按单张图随机切分 scenes {s[scene_id] for s in samples} scene_list sorted(scenes) val_count max(1, int(len(scene_list) * val_ratio)) val_scenes set(random.sample(scene_list, val_count)) train [s for s in samples if s[scene_id] not in val_scenes] valid [s for s in samples if s[scene_id] in val_scenes] return train, valid def write_yolo_format(dst_path, samples): with open(dst_path, w, encodingutf-8) as f: for s in samples: x1, y1, x2, y2 s[xyxy] # YOLO格式cx, cy, w, h 全部归一化类别固定为0 img_w 1920 img_h 1080 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h f.write(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n) train_set, valid_set split_by_scene(collect_samples(annotations)) write_yolo_format(train.txt, train_set) write_yolo_format(val.txt, valid_set)这段脚本里要重点理解两个设计。一是split_by_scene按场景ID切分而不是把每个样本当独立个体随机分这是防止同一个门牌的多张照片被拆到训练集和验证集两边二是类别固定写死为0因为数据集里只有“门牌”这一个检测目标。图片尺寸归一化时我直接写死成1920×1080如果你的街景图分辨率不同一定要从JSON的元信息里读取实际宽高否则框的位置会整体偏移。归一化坐标常见问题是宽高搞反。YOLO的cx cy w h是相对整张图的比例不是像素值。很多新手把像素坐标直接填进去训练时loss能降但mAP上不去推理结果框全部漂到左上角。转换脚本里我给img_w和img_h变量赋了固定值实际项目里要改成从图片元数据或者JSON的image_size字段读取。data.yaml 文件也很简单它告诉检测器去哪里找训练文件、类别名是什么。我对这个数据集的典型配置是类别名只写doorplate一个类val.txt指向验证集路径。OCR部分的字典文件 dict.txt 则按行放置数字字符检测训练完全不用关心它等到第三阶段模型选型时才用。3. 门牌识别怎么选型检测OCR两阶段是默认起点参数这样定3.1 为什么默认两阶段而不是端到端门牌号识别不能直接套通用场景文字识别方案。通用OCR的输入通常是画面中心已经存在的大段文字而门牌在街景图里既小又偏直接整图识别会有两个问题一是小目标在特征提取阶段就被不断下采样最后连数字轮廓都模糊了二是街景里的店招、车牌、路牌全是文字OCR模型会把它们也读一遍输出里混入大量垃圾。常见做法是拆成两阶段第一阶段用目标检测器在整张街景图里定位门牌框第二阶段把裁出来的门牌小图单独送OCR模型识别。两阶段的另一个好处是错误来源清晰。检测框漏了是检测器的问题框在但数字读错是OCR的问题调试时可以分别看两类错误而不是对着一个端到端黑匣子无从下手。端到端方案也不是不行但需要更大的数据集和更长的训练周期对多数团队来说投入产出比不高。两阶段还有一个隐藏收益检测框可以为OCR提供几何校正依据。门牌在街景里经常有旋转检测器输出的是带角度的旋转框可以据此把门牌区域矫正到水平再裁剪OCR只需面对近正视角的输入难度大幅下降。我在实际项目里检测器输出水平框但训练数据里专门保留了旋转样本做增强效果也够用。3.2 目标检测训练命令与输入分辨率选择检测器我一般选YOLO系列的中小模型起步精度足够且训练和推理链路成熟。训练命令里的参数设置几乎决定了项目成败下面是一份可直接套用的命令yolo detect train \ datadoorplate.yaml \ modelyolov8s.pt \ imgsz1280 \ epochs80 \ batch16 \ lr00.01 \ mosaic1.0 \ close_mosaic10 \ projectdoorplate_yolo \ nameexp1imgsz1280是这份命令里最重要的一个参数。门牌在画面中属于小目标640分辨率下很多门牌的宽只有十几个像素特征图上的响应几乎消失抬高到1280后小目标的可分性明显改善。代价是显存占用变大如果你的卡只有12GB显存可以把batch降到8用梯度累积来等效保持batch16的效果。YOLO训练的开源实现里梯度累积可以通过accumulate2这类参数控制等价于每两步做一次参数更新。close_mosaic10的含义是最后10轮关闭mosaic增强。mosaic把四张图拼接成一张对小目标检测很有帮助但模拟出来的拼接边界和真实街景差异较大训练末期强行继续用会让模型收敛不稳。关闭后模型用纯真实分布微调几个epoch验证集指标通常会有小幅回弹。这个参数在门牌这类小目标任务上的效果比一般目标检测更明显原因就是门牌对边界纹理很敏感。训练结束后看runs/detect/exp1/下的confusion_matrix.png和PR_curve.png重点确认门牌类别在低置信度区间是否还有较多召回。门牌检测器推断时默认置信度阈值在0.25实际部署时我会压到0.1因为漏检的代价比误检高得多。OCR阶段会再次过滤误检前一阶段宁可多框出来也不能让门牌被漏掉。3.3 OCR阶段的字符集与解码约束门牌OCR和通用OCR的差别在字符集非常小通常只有0到9的十个数字最多再加一个表示“空”的CTC blank。字典小的时候模型的绝大部分能力应该花在区分“1/7”“3/8”“0/6”这类易混字形上而不是消耗在几千个常用汉字上。自定义训练OCR识别时把输出类别数直接设为字典长度不要沿用通用模型的几千类。门牌号的长度通常只有1到4位个别地方会出现5位。这给解码阶段带来了一个先验条件识别结果不应该出现超过5位的数字串。我给OCR推理加了一个长度校验逻辑解码时若连续输出超过5个有效字符后段直接丢弃重新对齐。这个操作能有效解决“门牌后面的树影被识别成数字”这类问题。OCR训练时的一个关键做法是按第二步标注的位数分布对裁剪图做分层采样。如果三位数样本只有两位数样本的十分之一就把三位数样本的采样权重拉高保证每个batch里各位数数量大致均衡。我在门牌OCR训练里通常把一位数到四位数的batch内占比控制在1:2:2:1而不是按自然分布走。自然分布下三位以上的样本被几千个一位数样本稀释长串识别能力起不来。4. 训练一版能用的门牌识别模型从增强策略到评估线4.1 数据划分的防泄漏细节上一章脚本里按scene_id切分实际标注里还存在更隐蔽的泄漏路径同一扇门牌在不同季节、不同光照条件下被采集过两次它们可能属于两个不同的scene_id但内容几乎一样。我在划分时会在scene_id基础上再做一层图像相似度去重用感知哈希算法对全体图像算指纹相似度超过0.85的两张图强制分到同一侧。这一步能显著压掉验证集虚高。防泄漏的验证方法也比较直接训练结束后随机抽20个验证集门牌去训练集里用向量检索找最相似的图如果超过一半能找到同门牌不同视角的近重复样本说明切分逻辑还有漏洞。我第一次跑这个检查时发现验证集里约三分之一样本都能在训练集里找到“同一门牌的兄弟帧”这就是切分没做严的典型症状。4.2 增强策略模拟街景的脏和歪检测器阶段的增强不宜过重。门牌是强结构目标过度的色彩抖动反而会让数字边缘变糊。我一般只开以下增强mosaic1.0、轻微透视变换、随机亮度±20%、随机旋转±15度。翻转增强不要全局开因为门牌号是文本左右翻转会把“12”变成“21”模型学到的是错误的语义映射。YOLO的默认配置里翻转是随机的训练门牌时必须显式关闭水平翻转。OCR阶段的数据增强思路完全不同它面对的是已经裁剪出来的门牌块重点模拟现实里的降质因素高斯模糊、运动模糊、JPEG压缩、模拟雨滴和反光斑。每张裁剪图以50%概率叠加至少一种降质以30%概率叠加两种。透视变换的幅度控制在10度以内太大的形变会让数字结构崩坏。裁剪块右侧要额外保留5%的空白边这样解码器在读取尾位数字时不会因为边界截断而丢失响应。4.3 评估指标与验收线检测器指标用mAP0.5和mAP0.5:0.95两个都看。门牌检测的验收线我设为mAP0.5不低于0.95mAP0.5:0.95不低于0.75。前者保证定位基本可用后者防止模型只在宽松交并比下勉强命中。如果只看mAP0.5模型对门牌框边缘的回归会很粗糙直接影响OCR裁剪质量。OCR的评估不要用单字符准确率要用整串准确率。单字符准确率在“1234”被读成“1233”时依然显示75%但整串准确率直接归零它才真正反映门牌号能不能被一次性用对。按位数分层的整串准确率更有参考价值一位数应达到0.98以上四位数不应低于0.85。如果四位数准确率显著低于一位数基本可以断定是训练数据里长串样本不足回数据层补样本比调模型更有效。用下面这段脚本跑评估import json import torch from PIL import Image def evaluate_ocr(model, val_json_path): anns json.load(open(val_json_path, encodingutf-8)) correct {1位: [0, 0], 2位: [0, 0], 3位: [0, 0], 4位: [0, 0]} for ann in anns: crop Image.open(ann[crop_path]).convert(RGB) pred model.predict(crop) # 返回识别文本 true ann[text] key f{len(true)}位 correct[key][1] 1 if pred true: correct[key][0] 1 for k, (hit, total) in correct.items(): if total 0: print(f{k}整串准确率: {hit / total:.3f} ({hit}/{total})) # 调用示例 evaluate_ocr(ocr_model, val_annotations.json)这段脚本把识别结果按位数分开统计一眼就能看到模型在哪种串长上掉点。我实际项目里第一次跑完就发现三位数准确率只有0.61远低于一位数的0.97后来查数据分布发现三位数样本只占6%。把它的采样权重调到30%重新训练三位数准确率拉回0.89这个诊断方法比看总准确率有效得多。5. 门牌数据集的五个高频坑现象、原因与对策5.1 验证集指标很高上街就翻车数据泄漏现象训练时mAP0.5冲到0.98验证集整串准确率0.96但把模型部署到新的街景采集数据上漏检率突然升到四成。原因训练集和验证集里混入了同一门牌的近重复帧切分时没有按场景分组。模型实际是在“记住”门牌样本而不是在“学会”识别门牌。我从一个失败的第一次模型的训练日志里发现验证集和训练集的loss曲线几乎重合且同步下降这就是记忆化而不是泛化的信号。解决严格按场景ID分组切分再加感知哈希去重保证任何一张验证集图片在训练集里都找不到同一门牌的相似图。训练完成后做一次相似图检索抽查这是最后一道闸门。5.2 多位数门牌号尾数被吞OCR截断现象一张明明写着“1204”的门牌识别结果是“120”尾部“4”不见了。专门去看单张裁剪图人眼一眼能读出来但模型输出就是短一截。原因CTC解码在序列尾部容易出现对齐衰减尤其当裁剪块右侧空白不足、数字靠近图片边缘时。另外训练数据里四位数样本占比过低模型对长串的生成先验不足。解决裁剪时在门牌框右侧多留5%到8%的空白给解码器留出序列末端响应空间训练时对三位数和四位数样本做上采样让长串在batch里的比例不低于40%。我用这个组合把四位数整串准确率从0.72提到了0.91。5.3 门牌被空调外机、树枝或车辆局部遮挡现象检测框在遮挡场景下频繁抖动置信度在0.2左右OCR输入里数字被树影劈成两截。原因标注阶段把被遮挡严重的样本直接丢掉了导致模型从未见过“残缺门牌”长什么样。街景采集恰恰大量遇到这种场景模型训练分布和实际分布不一致。解决不要删除遮挡样本把它们单独放一个难例集训练时以20%的采样比例混进真实分布里。遇到完全无法辨认的情况标注时可以只框可见部分但文本字段要标记为“部分遮挡”让模型学到“看到一半也要尝试输出”的模式。5.4 imgsz调到1280以上显存不够640以下小目标漏检现象把imgsz从1280改成1600后直接报CUDA out of memory改回640训练验证集mAP低了近5个百分点。原因门牌是典型小目标分辨率越低特征图上的响应区域就越小。显存限制和识别性能在低分辨率下形成矛盾直接降分辨率是最容易想到但收益最差的解法。解决保持训练分辨率1280batch从16降到8并用梯度累积两步等效回16。显存仍然紧张就把输入切成三块重叠的图分别推理再把框映射回原图坐标做合并。切片后会遇到门牌正好被切在缝上的情况重叠率设成20%能缓解大半。5.5 连锁品牌门头让模型学歪了现象某个城市同一连锁品牌的门牌样式高度一致模型对这类样本过拟合后在陌生街道上检测率骤降。原因采集车在城市里巡游时连锁门店会在短时间内被重复拍到十几次这些同一样式的门牌在训练集里占比虚高模型不知不觉把“门牌”和“特定配色门头”绑定了。解决按门牌外观聚类分组同一品牌或同一配色模板的样本在训练集里限流最多占总数的5%。我一般会跑一遍框架内的颜色直方图聚类或者直接用字符串前缀做粗分组把最大的几类压下去训练再观察mAP曲线变化。做完这一步后模型对陌生街区的泛化能力提升非常明显这也说明门牌识别本质上是个数据均衡问题。6. 让数据集越用越厚伪标签回流与困难样本闭环一版模型上线后最容易忽略的事就是同步搭建数据回流通道。街景车每天都会带回新的原始帧与其等人工标注不如先让上一版模型全量跑一遍把输出按置信度分成三档。置信度大于0.95且整串识别结果稳定的样本直接作为伪标签进候选库置信度在0.3到0.7之间且模型多次预测不一致的样本排进人工复核队列完全低置信度的样本直接丢掉不值得浪费人力。这个0.3到0.7的区间是人工标注价值最高的区域因为高置信度样本人工复核一遍也几乎没有改动低置信度样本多数是极端遮挡或模糊标注成本高且学习价值低。伪标签回流还有一个细节每轮新训前检查老模型和当前模型的错误样本做一次差异对比。如果某些门牌在老模型上读错、新模型上读对了说明数据增强和模型更新生效了如果老模型对而新模型错的样本持续增多多半是训练分布被伪标签带偏了需要回退部分数据重新平衡。我习惯在每次版本发布前跑一轮错误聚类把识别失败的样本按相似图像堆在一起看往往能找到可批量修复的模式比如某一片区域的旧式门牌底色偏黄导致整体误检针对这类样本做一次颜色归一化增强而非逐张调参效率高得多。从数据采集到模型部署整条链条里数据侧的工作量至少占七成。门牌识别模型的很多“玄学”问题——验证集虚高、长串识别不稳、泛化崩塌——最后都能在数据构造方式里找到根因。把数据集的分组切分、位数均衡和难例回流这三件事做扎实模型侧只需要按常规参数训练就能得到稳定可用的结果。这算是我在这类任务上养成的一个习惯每次动模型前先问自己有六成把握问题不在数据侧吗没有的话就先回头整理数据这个习惯帮我避开过好几次无用功。希望帮到你。本文还有配套的精品资源点击获取