简介:目标检测是计算机视觉中的核心任务,而车牌识别作为典型的落地场景,融合了目标检测与OCR技术。YOLOv5凭借快速的推理速度和良好的精度平衡,成为构建车牌识别系统的热门选择。文章阐述了两阶段识别的原理:先通过YOLOv5定位车牌区域,再结合字符识别模型完成文本输出。这一技术广泛应用于智能交通、停车场管理和安防监控等领域。围绕工程实践,详细介绍了环境配置、数据集标注格式、模型训练参数调整以及推理部署中的常见问题与优化技巧,帮助开发者在实际项目中快速构建高效可靠的车牌识别系统。 做车牌识别项目,第一步选型就劝退了不少人。作为计算机深度学习领域很经典的一个落地场景,车牌识别通常分成两段:先检测出车牌在画面里的位置,再识别车牌上的字符。我一开始选择了YOLOv5做检测,配合后面的OCR模块完成整体识别。这篇文章就把我实际跑通过的一条完整链路分享出来:从环境安装、数据集标注、模型训练,到字符识别和部署,踩过的坑都会写清楚。
如果你现在正要接触YOLOv5车牌识别,或者已经卡在某个环节,可以照着这篇文章一步步走。整个过程不需要多高深的数学基础,能把Python和基本命令行玩明白就行。
1. 项目整体设计与方案选型
1.1 车牌识别本质上是“检测 + 识别”的两段式任务
很多人以为车牌识别是一个模型一步到位,实际不是。摄像头拍到的画面里有车、有行人、有背景,如果直接用一个模型去识别车牌字符,模型会非常懵。更合理的思路是:先用目标检测模型定位车牌区域,再把裁剪出来的车牌小块交给识别模块。这个思路在工程里叫“两阶段识别”,虽然看起来多了个步骤,但每个阶段的模型都更简单、更容易训练,精度反而更高。
我这边的流程是这样:摄像头或者图片输入到YOLOv5模型,模型输出若干个矩形框,每个框带着置信度和类别;我根据置信度筛选出车牌框,用仿射变换把倾斜的车牌矫正成水平方向;再把矫正后的图片送给一个车牌字符识别模型,最终得到类似“苏A12345”的字符串。两段模型独立训练,也可以独立替换,灵活性很高。
1.2 为什么选择YOLOv5而不是其他目标检测算法
做目标检测可选的方案很多,两阶段的Faster R-CNN、单阶段的SSD、YOLOv3、YOLOv5甚至YOLOv8。我选YOLOv5主要是三个原因:第一,YOLOv5是目前社区生态最成熟的YOLO分支之一,网上关于它训练自己数据集的中文教程非常多,遇到问题很容易搜到答案。第二,YOLOv5在模型大小、推理速度和精度之间取得了很好的平衡,尤其YOLOv5s这个版本,只有大约14 MB的权重文件,在普通显卡上推理一张图只要几十毫秒,部署到边缘设备也很有希望。第三,YOLOv5的代码封装得很友好,训练、验证、导出都能够通过命令行参数完成,不需要自己写太多底层逻辑,非常适合快速验证项目。
做个简单对比:Faster R-CNN精度不错,但推理速度相对慢,实时性场景不好用;SSD速度还行,但检测小目标能力偏弱,而车牌在画面里往往占比小,容易漏检;YOLOv5的multi-scale和anchor机制对中小目标更友好。当然,如果你现在动手做,也可以直接用YOLOv8,接口风格差异不大。我这篇文章还是围绕YOLOv5,因为用的人多,参数和权重更好找。
1.3 整体技术架构和模块拆解
整个项目我分成四个模块:数据准备、检测模型、字符识别模型、部署服务。数据准备负责采集车牌图片并标注;检测模型负责从原图找到车牌框;字符识别模型负责把车牌图变成文本;部署服务则是把两个模型串起来,对外提供接口。模块之间用统一的数据格式对接,检测模型输出的是坐标框,识别模型输入的是裁剪图,输出的是字符串。
这种架构的好处是每个模块都能单独测试。比如检测不准,我先检查是漏检还是误检;识别不准,我只需要固定裁剪图,单独调识别模型。如果采用端到端方案,出了问题反而很难定位。工程上我强烈建议先用两阶段跑通,确保整个链路能工作,再考虑后续优化。
2. 环境准备与依赖安装
2.1 硬件和系统说明
在开始装环境之前,先看看你的机器条件。我这次用的是一台Ubuntu 20.04系统的电脑,显卡是NVIDIA GeForce RTX 3060,12 GB显存。如果你用的是Windows,安装步骤类似,但sudo命令要换成管理员权限的命令行,路径写法上也有一点差异。没有NVIDIA显卡也没关系,YOLOv5支持CPU训练,只是速度会慢好几倍,训练100轮可能要好几天,建议先用小数据集验证流程,再考虑是否换机器。
需要提前安装的东西有Python、Anaconda(或Miniconda)、NVIDIA驱动和CUDA。建议不要自己单独配CUDA,直接通过conda安装PyTorch时会自动带上对应版本的CUDA runtime,省心很多。如果要训练大规模数据,显存最好8 GB以上,否则batch size一开始就会卡脖子。
2.2 创建虚拟环境并安装PyTorch
我习惯用conda创建独立环境,避免和系统环境打架。命令行里依次执行:
conda create -n yolo python=3.9 conda activate yolo pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu117PyTorch版本不需要追求最新,稳定就行。装完可以用一段简单代码验证GPU能不能用:
import torch print(torch.__version__) print(torch.cuda.is_available())如果输出True,说明GPU环境正常。这一步卡住的人很多,常见原因要么是显卡驱动版本太老,要么是PyTorch版本和CUDA不匹配。注意查看驱动支持的CUDA版本,用nvidia-smi能看到右上角的CUDA Version,PyTorch的cu版本不要高于它。
2.3 下载YOLOv5源码并安装依赖
YOLOv5的源码在GitHub上,可以直接用git克隆下来:
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt安装的依赖包括openpyxl、matplotlib、opencv-python、pillow等。这里有个小坑:requirements.txt里可能会安装一部分包的默认版本,如果你机器上已经有其他深度学习项目,建议先创建独立环境再安装,避免版本冲突。装完之后可以跑一下官方自带的检测脚本,拿一张测试图片验证环境是否OK:
python detect.py --source data/images/bus.jpg --weights yolov5s.pt如果运行正常,在runs/detect/exp目录下能看到带检测框的结果图。这一步能顺利通过,说明基本环境没问题。
3. 车牌数据集的准备与标注
3.1 数据集来源与整理建议
车牌识别模型的性能上限,很大程度由数据决定。公开数据集方面,比较常用的是CCPD(中国城市车牌数据集),里面有几十万张带车牌标注的图片,适合做检测训练。但CCPD的标注文件不是YOLO格式,需要转换一下。如果你有具体的应用场景,比如停车场出口、高速公路抓拍,最好自己采集一批对应场景的图片,公开数据集很难覆盖所有角度和光照条件。
无论用哪种数据,第一步都要清洗。把模糊到完全看不清车牌的图片删掉,把车牌被严重遮挡的图片去掉,重复图片也要去重。我一般先人工快速浏览一遍,再用脚本计算感知哈希,删除相似度过高的图。数据质量比数据量重要,喂给模型一万张干净图片,效果可能比三万张混杂图片还好。
3.2 标注工具和YOLO格式说明
标注工具我用的是LabelImg,简单实用。打开一张图片,画矩形框把车牌包住,类别设为plate,保存后会自动生成一个同名的txt文件。YOLO格式的标注文件每行表示一个目标,格式为:
class x_center y_center width heightclass是整数类别序号,从0开始;x_center、y_center、width、height都是相对于图片宽高的比例,范围在0到1之间。比如一张宽1920高1080的图片,车牌中心点坐标是(960, 540),框宽高是(200, 60),对应的txt内容就是:
0 0.5 0.5 0.10417 0.05556这里200除以1920约等于0.10417,60除以1080约等于0.05556。数值都不带单位,模型训练时会统一缩放到640×640分辨率,所以标注时使用的坐标必须基于原图,不要先裁剪再标注。
3.3 数据增强策略与样本均衡
YOLOv5内置了很多数据增强策略,包括马赛克(Mosaic)、随机翻转、缩放、色彩空间调整等。训练时不需要额外写增强代码,默认就会开启。不过车牌这类目标有特殊性:车牌字符本身颜色对比度高,但容易受到光照和角度影响。我建议在训练时增加一点亮度调整和模糊增强,模拟夜间和运动模糊场景。
另外要留意样本均衡。如果训练数据里绝大多数是蓝色牌照,只有少量黄色和绿色牌照,模型对后者的检测效果会明显偏差。我的做法是在准备数据时把少见类别单独复制并做一点轻度增强,让它们占比至少到5%到10%。车牌检测只有一类的话,问题不大;如果后续还要区分类型,那就得注意类别均衡。
4. 模型训练的关键配置与实操
4.1 修改数据配置文件和模型结构文件
训练YOLOv5之前需要准备好两个yaml文件。第一个是数据配置文件,一般放在yolov5/data目录下,内容是数据路径和类别列表。我的carplate.yaml如下:
train: /home/user/datasets/carplate/images/train val: /home/user/datasets/carplate/images/val nc: 1 names: ['plate']paths必须是绝对路径,或者相对yaml文件位置的路径,否则训练时读不到图片。第二个是模型配置文件,例如models/yolov5s.yaml,主要修改nc为1,和数据集保持一致:
nc: 1 depth_multiple: 0.33 width_multiple: 0.50如果你只是检测车牌,不需要改动网络结构,只改nc即可。注意不要直接把官方预训练权重里的类别数覆盖掉,训练时会通过yaml里nc决定最后一层输出的维度。
4.2 训练命令和超参数选择
训练命令我经常这样写:
cd yolov5 python train.py --img 640 --batch 16 --epochs 150 --data data/carplate.yaml --weights yolov5s.pt --name carplate--img表示训练时输入的图片尺寸,默认640。如果车牌在画面中占比很小,可以适当提升到800或者960,但显存和训练时间也会随之增加。--batch要结合显卡显存调整,显存吃紧就先从8开始。--epochs看数据集量级,1万张以下建议100到150轮;数据量大可以适当减少。
超参数方面,我一般保持默认,学习率初始0.01,最后用余弦退火降到很低。真正需要调整的是anchor尺寸。YOLOv5会自动学习anchor,但如果你的车牌宽高比非常极端,例如长条形的框占了大多数,训练时加上--noautoanchor选项,然后再微调anchor参数可能更稳定。不过对绝大多数情况,默认auto anchor已经够用。
4.3 训练过程监控与结果评估
训练过程中会输出每个epoch的loss、精度等指标,同时会在runs/train/carplate目录下保存训练曲线图。我需要重点关注的是val精度和召回率。如果loss下降缓慢或者不下降,先别急着改模型,检查一下数据标签有没有问题。一个常见错误是标注框坐标归一化时写错成像素坐标,导致loss巨大。
训练结束后,验证一下效果。YOLOv5自带验证脚本:
python val.py --data data/carplate.yaml --weights runs/train/carplate/weights/best.pt --img 640重点关注mAP@0.5和mAP@0.5:0.95。车牌检测属于单类目标,mAP@0.5到0.95一般能到0.9以上才算合格。如果检测框经常把整辆车框进去,说明回归方向有问题,可能是标注框不准确。看到误检和漏检,我建议先用detect.py跑几张测试图,打印出检测框坐标和置信度,再对照原图分析。
5. 车牌后处理与字符识别
5.1 从检测结果到车牌裁剪
检测模型输出的信息是归一化的坐标框,需要还原成原图坐标,然后裁剪出车牌区域。代码逻辑不复杂:
import cv2 import torch # 假设det为模型输出,包含xyxy格式的相对坐标 x1, y1, x2, y2 = det[0][:4].tolist() h, w = img.shape[:2] x1, y1, x2, y2 = int(x1 * w), int(y1 * h), int(x2 * w), int(y2 * h) plate_img = img[y1:y2, x1:x2]这里有一个细节:如果车牌是倾斜的,直接裁剪出来的矩形框会包含很多背景和多余区域,影响后续识别。我通常会在得到框之后,用OpenCV找到车牌区域的四个角点,然后做透视校正。如果只是简单项目,可以先用图像边缘检测加轮廓查找来获得车牌的最小外接矩形,再通过仿射变换转正。这一步对识别率的提升非常明显。
5.2 字符识别方案对比:OCR框架 vs 自建分类器
车牌字符识别可以分成两种路线。第一种是直接用现成OCR框架,比如PaddleOCR、Tesseract。优点是上手快,但中文车牌的汉字识别效果不一定好,需要额外微调。第二种是自己训练一个轻量级识别模型,输入是车牌图片,输出是字符串,通常用卷积网络加循环网络,比如LPRNet。这个方案可控性和识别率更高,但需要准备字符级别的标注数据。
我这次选择的是LPRNet的思路:先把车牌图缩放到统一尺寸,比如宽94高24,然后输入到一个轻量CNN网络,网络输出32个字符位置的分类概率,最后用CTC解码得到字符串。LPRNet结构不复杂,只有几层卷积,在CPU上也能跑到毫秒级。如果你不想从零训练,可以先下载一个开源预训练模型,再针对自己的车牌格式做微调。
5.3 字符分割与常见识别错误处理
传统字符识别方案是先把第二个字符后的汉字和字母数字逐个分割出来,再用CNN分类。分割的关键是找字符之间的连通域,但车牌铆钉、边框和污渍会干扰。现在更多使用基于CTC的序列识别,不需要显式分割字符,鲁棒性好很多。我用了LPRNet之后,确实省掉了分割步骤。
不过车牌识别依然有绕不开的坑:数字0和字母O极易混淆,1和I也是。建议根据车牌规则兜底,比如第二位字符在民用车牌中是字母,不会出现数字,如果模型输出“0”,可以纠正为“O”。汉字部分如“京”和“津”、“沪”和“泸”在模糊情况下也容易错,如果项目允许,可以加入基于车牌发牌规则的校验,把不合法省份字符排除掉。
6. 推理部署与常见问题排查
6.1 导出模型并搭建推理接口
训练好的检测模型可以导出成TorchScript或ONNX格式,方便在更多环境部署。我一般先导出ONNX,再根据目标设备选择运行时。导出命令:
python export.py --weights runs/train/carplate/weights/best.pt --include onnx --img 640导出的onnx文件可以用onnxruntime库加载,推理速度比直接跑PyTorch快不少。搭一个简易接口时,我会把检测和识别封装成一个类,对外只暴露一个process_image方法,输入图片路径或numpy数组,输出车牌字符串和坐标。这样就算不写Web服务,也能方便地集成到其他代码里。
6.2 性能优化小技巧
车牌识别通常用在实时监控或门禁场景,性能很重要。几个我自己实测有效的方法:第一,检测和识别模型都转为FP16精度,速度提升接近一倍;第二,把输入图片尺寸从1080p压缩到适合的最小尺寸,例如1280或640,检测精度几乎不受影响;第三,对视频流做帧间隔采样,例如每3帧检测一次,用跟踪算法如DeepSORT补足中间帧,可以大幅降低计算压力。
如果设备是边缘盒子,比如瑞芯微RK3568这类平台,YOLOv5模型可以先用ONNX转换成RKNN格式,再进行量化。量化后模型文件变小,但精度会有一定损失,需要重新验证。总之,性能优化要结合硬件来调,没有一套配置通吃所有设备。
6.3 常见问题速查表
我整理了训练和部署过程中最容易踩的坑,格式做成表格方便速查。
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 训练loss居高不下 | 标注格式错误,类别数不匹配 | 检查txt标注是否归一化,nc是否正确 |
| 显存不足,无法训练 | batch size过大,输入尺寸过大 | 减小batch,或设置--img 512 |
| 检测框框住整辆车 | 标注框偏大,模型学到上下文 | 重新检查标注,收紧车牌框边界 |
| 检测不到小尺寸车牌 | 图片被resize后车牌过小 | 提高输入分辨率,或使用更大scale的模型 |
| 车牌子颜色干扰识别 | 光照过强,字符对比度低 | 增加图像增强,或先做灰度均衡 |
| 字符识别把0识别为O | 分类混淆 | 根据车牌规则强制校正 |
| 部署环境没有GPU | ONNX / CPU推理慢 | 使用INT8量化,或挑选更小模型结构 |
有了这张表,很多问题能省去排查时间。遇到新问题,记得先看训练日志和输入输出,别盲调参数。
7. 项目扩展方向与个人实践体会
7.1 拓展多类型车牌和车辆属性识别
如果你之后不满足于单纯的车牌检测,可以继续扩展。比如同时检测车辆类型(轿车、卡车、SUV)、车身颜色、车牌颜色,或者识别新能源车牌和大型车黄牌。检测模型只需要增加类别数,数据标注也要跟上。这个扩展思路对停车场管理和路侧停车收费场景非常实用。我在测试时发现,同时输出多个属性,并不会增加太多推理耗时,因为都是在同一个检测模型上增加类别头,关键是数据要准备充分。
7.2 低光照与恶劣天气下的增强
车牌识别在夜间或者雨雾天气最容易翻车。除了提升训练数据多样性,我还会在推理链路中加入一个轻量的图像增强模块,比如自适应直方图均衡或者零深度降噪模型。注意不要过度处理,否则车牌本身的对比度也会被改变。一个简单的经验是:判断车牌区域的平均亮度,如果低于某个阈值,再做增强,否则保持原图。
7.3 从YOLOv5到更轻量模型的迁移思路
当项目要落地到嵌入式设备时,YOLOv5s可能还是有点大。此时可以尝试剪枝、蒸馏或者换用YOLOv5n、YOLOv8n这类更小模型。我在一个边缘盒子上测试过,把YOLOv5s换成YOLOv5n之后,检测速度提升了40%左右,mAP只下降了2个点,对车牌这种单一目标影响不大。但要注意小模型对远距离小目标更敏感,部署前要拿真实场景的图片多做测试,不能只看公有数据集指标。
从我个人经验来看,YOLOv5车牌识别这个项目最适合作为深度学习的入门实战项目,因为它每个环节都能拆开学习:目标检测、图像分类、序列识别、模型部署全覆盖。如果只跟着教程写完代码,进步有限;真正打开一张图片,从标注到自己的模型识别出第一个车牌,那感觉和看别人的截图完全不一样。过程中遇到问题不要慌,检查数据、检查标注、检查环境和版本,九成问题都是这三类。
本文还有配套的精品资源,点击获取