简介:本资源是一个面向人工智能课程设计与毕业设计实践的深度学习车牌识别系统完整实现,聚焦智能交通、停车场管理及治安监控等实际场景,适合具备Python与PyTorch基础的本科生、研究生开展项目开发与边缘部署学习。压缩包共104个文件(7.55MB),涵盖37个核心Python脚本(含YOLO车牌检测、LPRNet+STNet字符识别主干代码)、41个配置类YAML文件(模型参数、训练超参、树莓派部署环境定义)、7份结构化Markdown文档(含Deeplearning.md理论解析与RaspberrySetting.md全流程部署指南)、5个Shell自动化脚本及2个Dockerfile,支撑从训练、测试到树莓派端轻量化部署的全链路实践。已有49人下载学习,资源提供清晰的模块化目录结构、.gitignore规范管理、README快速上手指引及.ipynb交互式教程,特别包含空间变换矫正、车牌图像预处理与端侧推理优化等关键细节,可直接复用于课程报告、毕设答辩与工程原型验证。
1. 从零到一:一个车牌识别项目的诞生与核心价值
最近在整理硬盘,翻出来一个老项目——“基于深度学习的车牌识别系统.zip”。解压开来,看着里面熟悉的代码、模型文件和一堆测试图片,很多回忆涌上心头。这大概是我几年前为了验证一个想法,从零开始搭建的一个车牌识别原型系统。虽然现在市面上成熟的车牌识别SDK和API已经非常多,但自己动手走一遍从数据准备、模型训练到部署推理的全流程,那种收获感是完全不同的。今天,我就把这个项目的核心思路、关键实现细节以及踩过的那些坑,系统地梳理一遍,希望能给对计算机视觉、深度学习应用感兴趣,特别是想自己动手做一个完整项目的朋友一些参考。
车牌识别,或者说车辆牌照自动识别,是一个典型的计算机视觉任务,属于目标检测和光学字符识别的结合体。它的应用场景非常广泛,从我们每天进出小区的道闸、高速公路的ETC收费、停车场的无人值守,到交通违章抓拍、车辆轨迹追踪等安防领域,背后都有它的身影。这个项目的核心目标,就是利用深度学习技术,让计算机能够像人眼一样,从一张包含车辆的图片中,准确地定位出车牌的位置,并识别出上面的字符。整个过程可以拆解为两个核心子任务:车牌检测和车牌字符识别。听起来简单,但要让模型在各种光照、角度、污损、模糊的情况下都表现稳定,里面有不少门道。
2. 技术选型与整体架构设计:为什么是YOLO+CRNN?
在动手写代码之前,最关键的一步是确定技术路线。这直接决定了后续数据怎么处理、模型怎么训练、系统怎么搭建。当时我主要对比了几种主流方案。
2.1 车牌检测模块的抉择:从传统方法到深度学习
早期的车牌检测多基于颜色、纹理、边缘等手工特征,比如利用车牌蓝底白字(国内常见)的HSV颜色空间特征,或者利用车牌区域具有密集垂直边缘的特点进行定位。这些方法在光照均匀、背景简单的场景下效果尚可,但一旦遇到光线变化、车身颜色干扰、或者车牌倾斜,就很容易失效,鲁棒性很差。
因此,我毫不犹豫地选择了基于深度学习的目标检测模型。当时主流的候选者有Faster R-CNN、SSD和YOLO系列。Faster R-CNN是两阶段检测器的代表,精度高但速度相对慢;SSD和YOLO是单阶段检测器,速度优势明显。考虑到车牌识别往往有实时性要求(比如视频流分析),我最终选择了YOLOv5。理由很充分:首先,YOLOv5在速度和精度上取得了很好的平衡,其PyTorch实现生态完善,训练和部署都非常方便;其次,它的模型结构清晰,从轻量级的YOLOv5s到大型的YOLOv5x有多种尺寸可选,可以根据实际硬件条件灵活选择;最后,社区活跃,遇到问题容易找到解决方案。对于车牌这种尺寸相对固定、长宽比特征明显(国内车牌多为440mm*140mm,比例约3.14:1)的目标,YOLO这类检测器经过针对性训练后,定位精度完全足够。
2.2 字符识别模块的考量:分割识别与端到端识别
检测到车牌区域后,下一步就是识别上面的字符。这里主要有两种思路:
- 先分割,后识别:先将车牌图像进行二值化、字符分割,得到单个字符的切图,然后利用一个分类网络(如CNN)对每个字符进行分类。这种方法思路直观,但严重依赖分割的准确性。车牌字符间隔不均、粘连、污损都会导致分割失败,进而导致整个识别流程崩溃。
- 端到端识别:不进行显式的字符分割,直接将整个车牌区域图像输入一个序列识别模型,直接输出字符序列。这种方法避免了分割的难题,但需要模型能够自动学习字符间的上下文关系。
我选择了第二种方案,采用了CRNN(卷积循环神经网络)模型。CRNN结合了CNN和RNN的优势:CNN部分(如ResNet、VGG的变体)负责从图像中提取丰富的视觉特征序列;RNN部分(常用BiLSTM,即双向长短时记忆网络)负责对特征序列进行建模,捕捉字符之间的上下文依赖关系;最后接一个CTC(连接主义时序分类)损失层,它允许模型在训练时不需要对每一个输入帧都进行精确的字符对齐,非常适合这种不定长序列的识别任务。对于中文车牌(包含汉字、字母、数字),CRNN是当时非常成熟和高效的方案。
2.3 系统整体工作流
基于以上选择,整个系统的Pipeline就清晰了:
- 输入:一张车辆图像或视频帧。
- 车牌检测:图像输入训练好的YOLOv5模型,输出一个或多个车牌的边界框坐标
(x1, y1, x2, y2)和置信度。 - 车牌矫正与裁剪:根据边界框从原图中裁剪出车牌区域。由于拍摄角度问题,裁剪出的车牌可能是倾斜的,这里通常需要进行透视变换或仿射变换进行矫正,将其“拉正”,得到一个规整的矩形车牌图像。这一步对后续字符识别的准确性提升巨大。
- 字符识别:将矫正后的车牌图像输入训练好的CRNN模型,模型直接输出识别出的字符串,如“京A·12345”。
- 输出:结构化信息,通常包括车牌号、置信度、位置等。
这个流程看似线性,但每个环节都有大量细节需要处理,任何一个环节的疏忽都会导致最终结果不佳。
3. 数据:模型效果的基石与预处理的艺术
“数据决定模型的上限,算法只是逼近这个上限”。这句话在深度学习领域是至理名言。对于车牌识别项目,数据的准备和处理占据了至少一半的工作量。
3.1 数据收集与标注
当时的数据来源主要有几个:公开数据集(如CCPD,一个大型的中国车牌数据集)、网络爬取(需注意版权和隐私)、以及自己拍摄。我混合使用了CCPD数据集的一部分和自己收集的一些图片,总共大约有2万张包含车牌的车辆图像。
标注工作是体力活也是技术活。对于检测任务,需要使用标注工具(如LabelImg、CVAT)为每张图中的车牌画上矩形框,并打上统一的标签,如license_plate。对于识别任务,则需要在检测框的基础上,标注出正确的车牌号码字符串。这里有一个关键点:检测和识别的标注最好能关联起来。也就是说,一张图里每个车牌的边界框和对应的车牌号是一一对应的。这样在制作训练集时,可以很方便地生成检测标签(.txt文件,包含类别和归一化坐标)和识别用的图像-标签对。
3.2 数据预处理与增强策略
原始数据不可能完美覆盖所有场景,因此数据增强是提升模型泛化能力的利器。对于车牌识别,我主要采用了以下几种增强方式,并解释了为什么用它们:
- 几何变换:包括随机旋转(小角度,如±15度,模拟车牌轻微倾斜)、缩放、平移、裁剪。这能让模型适应不同距离、角度拍摄的车牌。
- 颜色空间变换:调整亮度、对比度、饱和度,模拟不同天气(阴天、黄昏、夜晚车灯照射)和光照条件。车牌识别最怕的就是反光和阴影。
- 模拟噪声与模糊:添加高斯噪声、椒盐噪声,以及运动模糊、高斯模糊。这能模拟车辆移动、摄像头抖动或低质量摄像头带来的图像退化。
- 模拟真实干扰:在车牌区域随机添加一些水滴、泥点污渍的模拟图案。这一点增强对提升模型在恶劣天气下的鲁棒性非常有效。
注意:数据增强需要合理设置参数幅度。过度的旋转可能导致车牌形状扭曲到不现实的程度;过度的模糊可能让字符根本无法辨认。增强的目的是“扩充”数据分布,而不是“扭曲”它。
3.3 制作CRNN训练数据
对于CRNN,其训练数据是单张车牌图片和对应的文本标签。我们需要从已标注的检测数据中,根据边界框裁剪出车牌区域,并进行高度归一化。例如,将所有车牌图像的高度resize到32像素,宽度按比例缩放。这是因为CRNN的CNN部分通常要求输入图像高度固定,而宽度可以变化以适应不同长度的车牌。标签文件则是一个简单的文本文件,每行记录图片路径和对应的车牌字符串,例如:
./plate_images/001.jpg 京A12345 ./plate_images/002.jpg 粤B·AB888字符集需要预先定义好,包含所有可能出现的字符:31个省简称汉字(京、津、冀…)、24个英文字母(I和O通常禁用)、10个数字。总共约65个类别。
4. 模型训练实战:参数、技巧与坑位实录
有了数据,就可以开始训练模型了。这里分开讲检测模型和识别模型的训练。
4.1 YOLOv5车牌检测模型训练
我使用的是YOLOv5的官方代码库。训练前的主要配置工作:
- 数据配置文件 (
data.yaml):指定训练集、验证集的图片路径、类别数量(nc: 1,只有‘车牌’一类)和类别名称。 - 模型配置文件:选择
yolov5s.yaml,这是一个比较小的模型,在速度和精度上比较平衡,适合快速实验和部署在普通算力设备上。 - 超参数调整:
img-size: 设置为640。YOLOv5训练时会自动进行多尺度训练,这是其优势之一。batch-size: 根据GPU显存调整,我用的卡是GTX 1080Ti,batch-size设为16。epochs: 总共训练300个epoch。通常会观察训练损失和验证集指标(如mAP@0.5)的变化,在指标平稳后可以提前停止。optimizer: 使用默认的SGD优化器,学习率lr0设为0.01,并配合余弦退火学习率调度,让学习率在训练后期逐渐减小,有助于模型收敛到更优的局部最优点。
训练过程中,要密切关注损失曲线和评价指标。YOLOv5的训练脚本会实时输出这些信息,并保存最好的模型权重(best.pt)。一个常见的问题是模型在训练集上损失下降,但在验证集上指标不升反降,这可能是过拟合的迹象。对策包括:增加数据增强的多样性、使用更轻量的模型、或者加入正则化(如Dropout,但YOLO结构本身已有设计)。
实操心得:在训练早期,建议先在小规模数据(如1000张图)上跑几个epoch,快速验证整个数据管道和训练流程是否正确。比如,检查数据加载是否正确、增强后的图像是否合理、损失是否在下降。这能避免在大规模数据上训练几天后才发现基础错误。
4.2 CRNN车牌字符识别模型训练
CRNN的训练相对独立。我参考了经典的CRNN-Pytorch实现。关键点如下:
- 网络结构:CNN部分我使用了轻量化的MobileNetV2的卷积层来提取特征,替代了原论文中较重的VGG。RNN部分使用了两层双向LSTM,隐藏层单元数为256。这样在保证精度的同时,大大提升了推理速度。
- 输入与输出:输入是归一化高度(如32)的车牌图像,宽度不定。经过CNN后,特征图在宽度方向上被压缩(通常通过池化或步幅卷积),形成一个特征序列。这个序列输入BiLSTM,最终每个时间步的输出通过一个全连接层映射到字符类别概率,再交给CTC损失计算。
- CTC损失的理解:这是CRNN训练的核心。它最大的好处是不需要在训练数据中标注每个字符在图像中的具体位置。只需要提供整个图像和对应的序列标签。CTC会自动处理对齐问题,允许模型输出比标签更长的序列(通过引入“空白”标签),然后通过动态规划算法找到最优的对齐方式。这极大地简化了标注工作。
- 训练技巧:
- 学习率策略:同样使用余弦退火或步进下降。
- 数据增强:对单张车牌图片同样可以进行轻微的仿射变换、颜色抖动,但幅度要比检测阶段小得多,因为字符的几何结构需要保持。
- 处理长尾分布:车牌字符中,数字和某些字母(如A,B)出现的频率远高于某些汉字(如“藏”、“琼”)。可以在损失函数中引入类别权重,或者对稀有字符样本进行过采样,以平衡各类别的学习。
训练CRNN时,一个直观的评估方法是看模型在验证集上的字符准确率和序列准确率。字符准确率是指所有预测字符中正确的比例;序列准确率是指整个车牌字符串完全预测正确的比例。后者显然要求更高,也是我们更关注的指标。
5. 集成与优化:让系统真正跑起来
训练好两个模型只是第一步,如何将它们集成到一个稳定、高效、可用的系统中,是另一个挑战。
5.1 推理流程的代码实现
推理脚本需要串联整个流程。以下是一个简化的核心代码逻辑:
import cv2 import torch from models.experimental import attempt_load # YOLOv5 from crnn_model import CRNN # 自定义CRNN模型 from plate_processing import warp_plate # 车牌矫正函数 # 1. 加载模型 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') detect_model = attempt_load('./weights/yolov5s_plate.pt', device).eval() crnn_model = CRNN(...).to(device).eval() crnn_model.load_state_dict(torch.load('./weights/crnn_plate.pth')) # 2. 读取图像 img = cv2.imread('car.jpg') img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 3. YOLOv5检测 with torch.no_grad(): detections = detect_model(img_rgb)[0] # 获取检测结果 # detections 包含 [x1, y1, x2, y2, conf, cls] # 4. 遍历每个检测框 for det in detections: x1, y1, x2, y2, conf, cls = det if conf < 0.5: # 置信度阈值过滤 continue # 裁剪车牌区域 plate_patch = img[int(y1):int(y2), int(x1):int(x2)] # 5. 车牌矫正 plate_corrected = warp_plate(plate_patch) # 6. CRNN识别 plate_text = recognize_plate(crnn_model, plate_corrected, device) print(f"检测到车牌: {plate_text}, 置信度: {conf:.2f}") # 7. 可视化(可选) cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0,255,0), 2) cv2.putText(img, plate_text, (int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0,255,0), 2) cv2.imshow('Result', img) cv2.waitKey(0)5.2 性能优化技巧
在实际部署,尤其是处理视频流时,性能至关重要。
- 模型轻量化:如果对速度要求极高,可以考虑将YOLOv5替换为更轻量的版本(如YOLOv5n),或者使用专门的模型压缩技术,如剪枝、量化。PyTorch提供了动态量化和静态量化工具,可以将FP32模型转换为INT8模型,在几乎不损失精度的情况下显著提升推理速度并减少内存占用。
- 推理引擎优化:使用
torch.jit.trace或torch.jit.script将模型转换为TorchScript,可以获得更优的运行时性能。更进一步,可以尝试使用ONNX将模型导出,然后利用TensorRT或OpenVINO等针对特定硬件(NVIDIA GPU, Intel CPU)优化的推理引擎进行部署,速度提升可能达到数倍。 - Pipeline并行:对于视频流,检测和识别可以尝试并行处理。例如,当一帧在进行车牌识别时,下一帧可以同时进行车牌检测。但这需要更复杂的多线程或异步编程。
5.3 处理疑难案例:提升系统鲁棒性
一个健壮的系统必须能处理各种边缘情况。我在测试中遇到了不少问题,并总结了应对方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 车牌检测不到 | 光照过暗/过曝、车牌尺寸过小、严重遮挡 | 1. 在检测前对图像进行自适应直方图均衡化(CLAHE)或伽马校正,改善光照。 2. 训练数据中加入更多极端光照和小目标样本。 3. 适当降低检测置信度阈值,但需警惕误检。 |
| 车牌定位框不准 | 车身有类似车牌纹理的干扰物、车牌倾斜角度大 | 1. 使用更准确的数据集训练,确保标注框紧贴车牌边缘。 2. 在数据增强中加入更多大角度倾斜和复杂背景的样本。 3. 后处理中使用NMS(非极大值抑制)的变体,如Soft-NMS,处理重叠框。 |
| 字符识别错误 | 车牌污损、模糊、字体特殊、字符粘连 | 1. 强化CRNN训练数据,加入模拟污损、模糊的增强。 2. 在识别前,对矫正后的车牌图像进行超分辨率重建(使用ESPCN等轻量模型)或去模糊处理,提升图像质量。 3. 引入语言模型进行后处理纠错。例如,利用车牌编码规则(如省份简称+发牌机关代号+序号)构建一个简单的概率模型,对CRNN的原始输出进行校验和修正,可以显著降低“0”和“D”、“8”和“B”这类形似字符的误识别率。 |
其中,引入语言模型进行后处理是一个性价比极高的优化。它不需要重新训练深度学习模型,只是基于规则和统计信息对结果进行微调,往往能将序列识别准确率提升好几个百分点。
6. 项目总结与可扩展方向
回顾这个项目,从数据爬取、清洗、标注,到模型选型、训练、调试,再到最后的集成、优化和测试,是一个完整的深度学习应用闭环。它不仅仅是对YOLO和CRNN两个模型的应用,更涉及到计算机视觉项目全流程的实践。最大的收获不是调出了一个高精度的模型,而是对整个流程中可能遇到的问题有了预判和解决思路。
这个原型系统还有很多可以深化和扩展的方向。例如,可以尝试更先进的检测模型如YOLOv8或DETR,识别模型可以尝试基于Transformer的Vision Transformer (ViT) 结合序列建模。对于更复杂的场景,如双层车牌(香港)、摩托车牌、新能源车牌的特殊结构,需要专门的数据和模型调整。此外,将整个系统封装成RESTful API或GRPC服务,方便其他系统调用,或者部署到嵌入式设备(如Jetson Nano)实现边缘计算,都是非常有价值的工程化工作。
最后,分享一个我踩过的“坑”:在训练CRNN时,初期准确率一直上不去,排查了很久才发现是字符集定义出了问题。我最初漏掉了车牌号码中可能出现的字母“I”,但实际上有些地区的车牌(如某些特殊号段或早期车牌)是包含的。虽然出现频率极低,但一旦测试集里出现,模型必定认错。这提醒我们,在定义分类问题时,务必穷举所有可能的类别,哪怕某些类别样本极少,也可以通过数据合成或增强的方式补充进去,否则就会留下系统性的缺陷。深度学习模型很强大,但它的能力边界,最终是由我们喂给它的数据决定的。
本文还有配套的精品资源,点击获取