简介:车牌识别是计算机视觉领域极具落地价值的应用场景,其核心链路包括目标检测与光学字符识别两大环节。做车牌检测识别项目,最关键的往往不是模型结构,而是一份同时包含检测框与字符标注的高质量中国车牌数据集——检测框负责定位车牌区域,字符标注则支撑后续的OCR识别。然而,许多开发者误将通用目标检测数据集直接用于车牌项目,导致检测与识别效果严重脱节。本文从数据采集、标注规范、类别平衡、数据增强等基础工程问题切入,系统梳理了利用YOLOv8训练车牌检测器,并串联CRNN/PaddleOCR实现字符识别的完整技术路线。同时结合双层车牌、新能源车牌、夜间反光等真实场景中的落地难点,给出可复用的参数配置与踩坑经验,为正在从事车牌识别、OCR或相关数据集建设的开发者提供直接参考。 做车牌检测识别项目,最头疼的不是模型,而是数据集。我前前后后换过三版方案,踩了无数坑,最后发现一份靠谱的中国车牌数据集,必须同时包含检测框和字符识别内容,缺一样都白搭。这些年有太多人拿着通用目标检测数据集硬练,检测框倒是出来了,结果字符识别结果一塌糊涂,原因就是数据里根本没有识别层面的标注。这篇文章就围绕“中国车牌数据集包含检测和识别数据”这个主题,把我在数据采集、标注、YOLOv8训练和识别串联中的完整思路、参数选择和踩坑经历整理出来,给正在做车牌识别、OCR或者想把自己数据集跑通的读者一个可直接抄作业的参考。
1. 中国车牌检测识别数据集,到底需要哪些东西
1.1 检测数据和识别数据,两件事不能混为一谈
很多刚开始接触车牌项目的朋友会把“检测”和“识别”搅在一起,觉得模型能框出车牌就等于能认出车牌,这完全是两码事。检测任务关心的是“车牌在图像的哪个位置”,输出是一个边界框,比如左上角坐标、右下角坐标。识别任务关心的是“这个车牌上的字符到底是什么”,输出是一串字符串,比如“京A12345”。
中国车牌识别项目里,检测和识别通常是串联的:先用目标检测模型从整张图中定位车牌区域,再把车牌区域裁剪出来,交给OCR或字符识别模型去解析字符。这就决定了数据集必须同时支持两个环节。如果你只有检测框,没有字符标注,识别模型就没有训练依据;如果你只有字符标注,但检测框本身标注得很随意,检测模型就只能学到个大概,最终端到端效果一定翻车。
这也是为什么标题里特意强调“包含检测和识别数据”——这不是一句废话,而是整个项目的命根子。我见过不少人从某个开源仓库下载了“车牌数据集”,打开一看只有几百张图片和几个VOC标注文件,检测框有,字符标签却只有中文描述,根本没法喂给深度学习模型。这种数据,拿来练检测勉强行,练识别就抓瞎。
1.2 中国车牌的多样性,远比想象中复杂
中国车牌跟欧美车牌不一样,字符体系有省份简称、字母、数字,还要区分车牌颜色、单双层、新旧能源。你要是只标“blue plate”和“green plate”,识别精度会非常感人。
具体来说,一份合格的中国车牌数据集需要覆盖这些维度:
| 维度 | 具体内容 | 对识别的影响 |
|---|---|---|
| 车牌底色 | 蓝色(燃油车)、绿色(新能源)、黄色(大型车)、白色(警用)、黑色(涉外)、黄绿双拼(新能源大型车) | 检测颜色特征和后续分类 |
| 字符数量 | 传统蓝牌7位(汉字+字母+5位数字/字母)、新能源8位(汉字+字母+6位)、双层黄牌(上排汉字+省份简称,下排编号) | 识别网络输出长度需要适配 |
| 省份汉字 | 全国各省简称,不同的“京”“沪”“粤”“苏”等 | 汉字分类类别数量固定,但样本分布极不均衡 |
| 字体与排列 | 各省车牌字体有细微差异,有些车牌的字符间距不同 | 字符级特征容易混淆,如“0”和“O” |
| 环境因素 | 光照过曝、夜间反光、雨雾、泥污、倾斜、模糊、遮挡 | 需要数据增强和图像预处理 |
如果你只拿单一场景的数据集训练,比如只有白天高速公路卡口的蓝牌,换到地下车库或者雨天城市道路,效果断崖式下跌。所以数据集的“覆盖面”比“绝对数量”更重要。
1.3 数据集格式与标签结构怎么设计
数据集的存储格式直接影响后续训练脚本的编写难度。目前主流的目标检测格式有Pascal VOC XML、YOLO txt、COCO JSON,识别部分常见的有单独的字符序列文本,或者每个字符一个框的细分标注。
我个人的做法是:检测部分用YOLO格式(每行:类别id、中心x、中心y、宽、高,归一化);识别部分单独建一个txt文件,每行对应一张图片,格式是“图片名 车牌字符串”。这样训练YOLOv8做检测时直接读txt,训练OCR识别时直接读字符串列表,两边互不干扰,也不用每次转换格式。
如果你要做字符级定位识别,也就是检测车牌后还要把每个字符单独框出来,那就需要更细的标注:每个字符的边界框、字符内容、字符顺序。这种数据适合训练像LPRNet这类一步到位的端到端模型,但标注成本非常高。我的建议是:先决定你要走“检测+两阶段识别”还是“端到端”路线,再设计标注格式,别一上来就想全都要。
2. 数据采集与标注:一份能用的数据集是怎么来的
2.1 公开数据集与自行采集的取舍
国内比较有名的公开车牌数据集是CCPD(Chinese City Parking Dataset),包含超过25万张图片,覆盖了多种场景和天气条件,适合检测任务。但CCPD也有几个问题:一是图片分辨率偏高,很多车牌在画面中占比很小,直接训练检测模型容易漏检;二是字符标注存在噪声,部分图片是模糊或者严重倾斜的,当成识别数据会拉低精度;三是版权和许可协议有时不够明确,商用前要仔细确认。
我自己做项目时,会在公开数据集基础上补充自行采集的样本。采集车牌数据有合规问题,直接在路上拍别人的车牌涉及隐私,所以我的做法是在获得授权的小区、园区内部停车场采集,或者用模拟场景生成。网上也有一些模拟车牌生成器,比如用Python生成不同背景、字体、颜色的车牌图片,用于做识别网络的预训练,效果还不错。
2.2 标注规范与实操细节
标注工具我推荐三个:labelImg(常规矩形框,适合YOLO/VOC)、roLabelImg(旋转框,适合倾斜车牌)、labelme(多边形标注,适合字符级框)。如果你的车牌多是水平停车位抓拍,用labelImg就够了;如果是高速卡口或者道路监控,车辆角度变化大,车牌可能是斜的,用roLabelImg旋转框能减少背景噪声。
标注时最容易犯的错有三个:
- 只标字符区域不标整个车牌底板。有些车牌的边框、铆钉、螺丝会被误认为字符区域,识别时干扰极大。建议检测框范围包括整个车牌底板,但不包括车身上的品牌LOGO。
- 字符标注顺序不固定。中国车牌第一位是省份简称,第二位是字母,剩下的是字母和数字混合。一定要保持从左到右、从上到下的顺序,不然OCR输出就是乱码。
- 遮挡车牌要不要标。如果车牌被遮挡了一半,目标是检测整个车牌还是检测可见部分?我建议只要肉眼能判断有车牌,就标整个车牌的理论范围,由模型学会在遮挡下推理;但如果遮挡严重到看不出是一个车牌,就不要标,否则反而教坏模型。
此外,最好在标注结束后做一轮交叉校验。我自己试过,两个人标同一批图,框的边界可能差5个像素,字符可能有人把“I”和“1”搞混。要写一个小脚本对比两份标注文件,把不一致的地方挑出来人工确认,这一步能避免后续训练时模型学得“左右摇摆”。
2.3 数据集的规模、划分与增强
很多朋友问:车牌数据集到底要多少张才能练出效果?我的经验是,检测任务至少5000张到10000张起步,识别任务则要看字符类别数。中国车牌字符类别大概是31个省份简称、24个字母(除去I和O)、10个数字,总共约65个类别,每个类别最好有几百个样本。
我习惯把数据集按8:1:1划分为训练集、验证集和测试集。验证集用来调参和早停,测试集必须是完全没有参与训练的,尤其是那些不同地点、不同光照条件下的图片,确保泛化能力。划分的时候要注意,同一个车牌的连续帧不要同时出现在训练集和验证集里,否则会高估模型效果。
数据增强这块,YOLOv8训练时会默认启用Mosaic、HSV变换、随机翻转等增强,但对车牌场景还不够。我额外增加了三类增强:
- 模拟运动模糊:用OpenCV的GaussianBlur加随机方向的运动模糊,模拟高速行驶时的拖影。
- 模拟雾天和夜间反光:用对比度调整、亮度增强、加入高斯噪声,让模型见过更恶劣的光照。
- 随机仿射变换:旋转角度加±15度,轻度透视变换,模拟不同角度拍摄的车牌。
增强不是越多越好,要控制强度。有一次我把旋转角度调大,结果检测模型把侧面的非车牌反光区域误检成车牌,就是增强过头了。
3. 用YOLOv8训练车牌检测器,再串起字符识别
3.1 数据集格式转换与配置文件
我之前用的是Pascal VOC格式的标注,但想用YOLOv8跑,就需要先转成YOLO格式。转换脚本逻辑很简单:读取XML里的bndbox坐标,转换为归一化的center_x、center_y、width、height,写入同名txt。注意如果用了旋转框,就要把四点坐标转成旋转框参数,不能直接用普通目标检测脚本来处理。
转换完成后,在项目目录下建一个data.yaml,内容大概是这样:
train: datasets/train/images val: datasets/val/images test: datasets/test/images nc: 1 names: ['license_plate']一个类别就够了。这里有个容易踩的坑:如果你还要做车牌颜色分类,比如蓝牌、绿牌、黄牌,可以把类别扩展成多个,比如['blue_plate', 'green_plate', 'yellow_plate'],这样可以同时完成检测和颜色识别,但前提是你的标注里把颜色也标进去。
3.2 模型选型与训练参数实测建议
YOLOv8发布后,我试了n、s、m、l四个规格。在车牌检测这个任务上,n和s的速度很快,但小目标漏检率偏高,特别是画面中车牌宽小于40像素的。m和l的精度明显提升,但对算力的要求也更高。
我最终的推荐是:
- 边缘设备(Jetson Nano、RK3588)上用yolov8n,输入分辨率640,能跑到实时。
- 服务器训练,部署时用TensorRT加速,用yolov8s或者yolov8m,mAP50能在0.95以上。
- 如果是极端小目标场景,比如远距离卡口,用yolov8l并在高分辨率(960或1280)下训练,但推理速度会慢,需要trade-off。
训练命令很简单:
yolo train data=data.yaml model=yolov8m.pt epochs=150 imgsz=640 batch=16 device=0有几个参数值得细调。imgsz如果设成640,对大多数场景够用,但车牌目标小,我建议在显存允许情况下提升到960。epochs我基本设150,配合早停,防止过拟合。batch按显存自适应,一般先用默认值,出现OOM再调小。优化器直接用默认的AdamW,学习率往低设置,从头训的时候0.001左右,用自己的预训练权重接着练则降到0.0001。
训练过程中观察loss曲线和验证集mAP。我自己遇到过一个情况:mAP50很高,mAP50-95却很低,这说明模型对大目标的光敏感不错,但对小目标、遮挡目标的鲁棒性不足。解决办法就是回数据层面补样本,而不是死磕模型参数。
3.3 检测与识别的串联方案对比
有了检测模型之后,识别部分有两条路可选。
第一条路是两阶段方案:YOLOv8检测车牌区域,裁剪出图像,再送给OCR识别。OCR既可以用PaddleOCR内置的车牌识别模型,也可以用自己训练的CRNN或SVTR。PaddleOCR的好处是开箱即用,中文支持好,但对特殊车牌(双层、新能源)的适应性差一些,需要微调。自己训练CRNN则更可控,输入是车牌裁剪图,输出是字符序列。
第二条路是端到端方案:用LPRNet这种模型直接输入车牌图像,输出识别结果。LPRNet参数量小、速度极快,适合嵌入式部署。但它不能单独做检测,必须配合检测器使用,而且对车牌倾斜和模糊的容忍度比CRNN低。
我自己的项目里用的是“YOLOv8检测 + CRNN识别”。检测模型框出车牌后,我还会根据框的宽度和高度比判断是单层还是双层。如果宽高比大于3,按单层处理;如果小于2,大概率是双层或大型车牌,就先切成上下两行,再分别识别,最后拼接结果。这个简单启发式处理,比直接丢给识别模型更稳。
3.4 识别网络的训练与性能评估
识别网络输入建议统一缩放到固定尺寸,比如CRNN用高度32、宽度128或168的灰度图。训练时候采用CTC损失,字符类别加上一个空位。中国车牌字符数量不固定,传统蓝牌7位,新能源8位,双层车牌可能只有上下两行,用CTC可以避免固定的输出长度,是一个稳妥的选择。
评估识别效果不能只看整体准确率,还要拆分看:
- 汉字识别准确率:各省简称难记,样本分布不均,最容易出错。
- 字母与数字混淆率:比如“0”和“O”、“1”和“I”,车牌中一般不会同时出现O和I,但模型不该把它们输出为没见过的字符。
- 符号长度错误:少一位字符、多一位字符,在门禁系统中十分致命。
我测试时常用这样一张表来对比不同模型的效果:
| 方案 | 单张推理耗时 | 识别准确率 | 备注 |
|---|---|---|---|
| YOLOv8s + PaddleOCR | 35ms | 96.2% | 对新能源支持一般 |
| YOLOv8m + 自训CRNN | 28ms | 97.8% | 对模糊车牌略差 |
| YOLOv8s + LPRNet | 10ms | 93.5% | 速度快,准确率稍低 |
如果识别准确率卡在97%上不去,不要急着换大模型,优先观察错误样本的共性。我遇到最多的错误样本是颜色反光导致字符断裂,比如蓝牌在强光下蓝色区域和白色字符对比度下降,识别出来“A”变“4”。这种问题靠识别模型硬扛很难,我在前处理环节加了自适应对比度增强,效果立竿见影。
4. 实际项目中的坑,我替你踩过了
4.1 标注不一致导致模型学歪
第一次做车牌项目的时候,我一个人深夜标了两千张图,结果训练完发现验证集上mAP一直上不去。检查之后才发现,前期标注的框和后期标注的框标准不统一:前期喜欢把车牌的金属边框也框进去,后期只框字符区域。模型学到的特征一会儿是“字符区域”,一会儿是“整个车牌面板”,自然学不好。
这事给了我一个教训:标注规范必须提前写清楚,而且最好是图文版。比如“车牌检测框的左右边界以字符区域向外扩展不超过5个像素,包括蓝色底板,但不包括螺丝钉”。正式标注前先标20张,大家互相看一遍,达成共识再批量标注。
4.2 省份简称类别不平衡
中国车牌省份简称出现频率跟地区经济、道路密度相关,有的省份样本特别多,有的偏远省份样本很少。如果按原始分布训练,识别网络会对高频省份过拟合,对低频省份几乎不识别。
解决办法有三招:第一,训练时按类别重采样,保证每个省份简称在每轮epoch中出现的次数差不多。第二,用数据增强中的mixup,把一张“苏A12345”和一张“青A67890”混合,让模型被迫关注字符本身。第三,用合成车牌数据补充低频样本。我写过一个简单的车牌渲染器,随机选择省份简称、字母数字组合,用不同字体渲染到真实背景上,效果虽然不是完全真实,但识别模型能学到特征。
4.3 反光、夜间、运动模糊的识别率低
这是落地时最崩溃的问题。白天阳光直射下,蓝色车牌有时候会白花花一片;夜间车灯直射,甚至出现光晕;车辆高速行驶时,车牌不再是清晰锐利的,运动模糊会把字符抹成一条线。
纯靠数据增强能解决一部分,但还不彻底。我在识别前增加了一个轻量的图像预处理模块,先用Laplacian算子计算图像清晰度,如果低于某个阈值,就判定为模糊,先做去模糊处理。对于反光,用自适应阈值分割把字符区域和背景分离。运动模糊则尝试用OpenCV的逆滤波,但要注意逆滤波会放大噪点,所以还要配合中值滤波。
这里有个取舍:预处理步骤越多,单帧耗时越长。所以我会做一个简单的图像质量分类器,只有低质量的车牌图像才走预处理分支,清晰图像直接识别,避免拖慢平均速度。
4.4 双层车牌和新能源车牌的常见bug
双层黄牌主要出现在大货车和挂车上,上层是省份简称+字母,下层是数字编号,而且字符较小。如果不做处理,识别模型容易把上下两行字符混在一起,输出一长串无意义内容。
我的处理是检测框出来后先看宽高比。单层车牌宽高比普遍在2.5到4之间,双层车牌大概在1.2到2之间。对于双层,我把裁剪图水平切成上下两半,分别缩放到合适尺寸后送进识别模型,最后把两行字符串拼接。还有一个细节:双层车牌的上层有时候没有汉字,只有省份简称,这个在拼接时要保留顺序。
新能源车牌的问题主要是字符位数和“D”和“F”开头。传统蓝牌是7位,新能源是8位,如果识别网络固定输出长度,就会在小数点后多出或漏掉一位。用CTC可以解决,但要注意训练数据里新能源车牌的比例,不能全是蓝牌。我建议数据集中新能源车牌至少占20%。
4.5 部署阶段的模型提速
训练归训练,部署又是另一层地狱。我一开始直接用PyTorch模型推理,640分辨率下大概30ms一帧,看着够用,但在多路视频流并行时CPU和GPU占用率飙升。后来我换成ONNX + TensorRT,fp16精度下推理时间压到了10ms以下。
如果你的部署环境没有TensorRT,也可以做其他加速:图片预处理阶段,先把车牌目标附近的区域放大再识别,减少整个画面缩小的信息损失;或者用两个模型级联,第一个轻量模型快速找出可能的车牌区域,第二个高精度模型只对候选区域做精细检测和识别。
还要注意线程和显存管理,车牌项目经常要跑长时间不间断,内存泄漏和显存碎片积累会越来越卡。我习惯每个进程定期重启任务,或者用内存池管理图片缓存,避免出现OOM。
5. 一些值得记录的实操心得
车牌检测识别项目做到后面,基本上就是拼数据质量和细节处理。模型框架翻来覆去就那几套,真正决定上限的是你对数据有多了解。比如“粤”和“苏”在模糊状态下特别像,光靠训练集里的对比度可能学不到位,要在预处理里增加锐化;比如夜间绿色新能源牌在灰图上几乎看不清,就得专门做色彩空间转换,或者用YOLO直接检测原图颜色再做通道增强。
我还有个习惯:每轮训练结束后,把识别错误的样本连同原图、检测框、预测结果输出到错误集文件夹,然后定期人工翻看。别看这个操作土,它比任何指标都直观。很多问题在指标上只体现为0.5%,但一翻错误集就发现某个特定场景完全没覆盖到。有一次就是发现夜间地库的车牌大量被检测成黄牌,问题出在监控屏的白平衡偏黄,数据增强里根本没模拟过这种偏色。
最后再分享一个小技巧:在做车牌数据集的时候,可以把采集数据按照“省份”、“场景”、“光照条件”、“车牌颜色”打上标签,形成一张数据分布表。后续无论是训练还是测试,都能快速知道当前模型在哪个子集中表现差,补数据的时候也有的放矢。用一句话总结就是:数据集里藏着的不是一张张图片,而是整个落地场景的浓缩。做项目时较真一点,后面上线就能少熬几个夜。
本文还有配套的精品资源,点击获取