基于YOLOv5的车牌识别系统开发与实践

📅 2026/7/27 14:19:04
基于YOLOv5的车牌识别系统开发与实践
1. 项目概述作为一名计算机视觉方向的毕业生我在完成毕业设计时选择了基于YOLOv5的车牌识别系统开发。这个选题源于我对智能交通系统的兴趣以及车牌识别技术在现实生活中的广泛应用场景。从高速公路收费到停车场管理再到城市交通监控车牌识别技术都在发挥着重要作用。传统的车牌识别方法主要依赖图像处理和机器学习技术但在复杂场景下的识别准确率往往不尽如人意。近年来深度学习技术特别是目标检测算法的发展为车牌识别带来了新的突破。YOLOv5作为单阶段目标检测算法的代表以其速度快、精度高的特点成为车牌检测的理想选择。我的毕业设计目标是构建一个完整的车牌识别系统包含从数据准备、模型训练到应用部署的全流程实现。系统需要能够处理静态图片、视频流以及实时摄像头输入并在各种光照条件和复杂背景下保持较高的识别准确率。2. YOLOv5算法解析2.1 YOLOv5网络架构特点YOLOv5的网络架构可以划分为四个主要部分输入端、基准网络、Neck网络和Head输出端。这种模块化设计使得算法具有很好的灵活性和可扩展性。在输入端YOLOv5采用了Mosaic数据增强技术将四张训练图片随机裁剪拼接成一张图片进行训练。这种方法不仅增加了数据的多样性还能让模型学习到更多小目标的特征。同时自适应锚框计算和自适应图片缩放技术使得模型能够更好地适应不同尺寸的输入图片。基准网络部分YOLOv5融合了Focus结构和CSPDarknet53结构。Focus结构通过切片操作将输入图片的通道数扩展为原来的4倍同时将分辨率降为原来的1/4这种设计既保留了原始图像信息又提高了计算效率。CSP结构则通过跨阶段部分连接增强了特征提取能力同时减少了计算量。2.2 YOLOv5的改进与优化相比前代YOLOv4YOLOv5在多个方面进行了优化。在Neck网络部分YOLOv5采用了FPNPAN的结构。FPN特征金字塔网络通过自上而下的路径将高层语义信息传递到低层而PAN路径聚合网络则通过自下而上的路径将低层位置信息传递到高层。这种双向特征融合机制大大提升了模型对不同尺度目标的检测能力。在损失函数方面YOLOv5使用GIOU_Loss替代了传统的IOU损失。GIOU不仅考虑了预测框和真实框的重叠区域还考虑了它们的最小外接矩形使得边界框回归更加准确。在预测阶段YOLOv5采用DIOU_nms进行非极大值抑制考虑了目标框中心点距离的因素有效减少了重叠目标的误检。提示YOLOv5有四个版本s、m、l、x区别主要在于网络深度和宽度。对于车牌识别这种相对简单的任务YOLOv5s通常就能达到很好的效果而且推理速度更快。3. 数据准备与处理3.1 数据集选择与标注高质量的数据集是训练优秀模型的基础。在车牌识别项目中我选择了CCPDChinese City Parking Dataset数据集它包含了超过25万张中国车牌图片涵盖了各种光照条件、天气情况和拍摄角度。每张图片的分辨率为720×1160并提供了车牌的四个角点坐标和车牌号码的标注信息。对于自定义数据集我推荐使用labelimg工具进行标注。安装非常简单只需执行pip install labelimg然后运行labelimg命令即可启动标注工具。标注时选择YOLO格式它会为每张图片生成一个对应的.txt文件包含类别编号和归一化的边界框坐标。3.2 数据预处理技巧在数据预处理阶段我采用了以下几种增强技术来提升模型的鲁棒性色彩空间变换随机调整图片的亮度、对比度和饱和度模拟不同光照条件几何变换包括随机旋转±10度、平移±20%和缩放0.8-1.2倍Mosaic增强将四张图片拼接训练增加小样本场景的识别能力添加噪声随机添加高斯噪声和椒盐噪声提高模型抗干扰能力特别需要注意的是中国车牌有蓝牌、黄牌、绿牌等多种颜色在数据增强时要确保各类车牌的样本均衡避免模型对某种颜色车牌产生偏好。4. 模型训练与调优4.1 训练环境配置我使用的训练环境配置如下GPUNVIDIA RTX 3080 (10GB显存)CUDA版本11.3PyTorch版本1.10.0Python版本3.8.10建议至少使用8GB显存的GPU进行训练否则可能需要减小batch size。安装依赖时可以使用YOLOv5官方提供的requirements.txt文件pip install -r requirements.txt4.2 关键训练参数设置在train.py中有几个关键参数需要特别注意parser.add_argument(--weights, typestr, defaultyolov5s.pt, helpinitial weights path) parser.add_argument(--cfg, typestr, defaultmodels/yolov5s.yaml, helpmodel.yaml path) parser.add_argument(--data, typestr, defaultdata/coco128.yaml, helpdataset.yaml path) parser.add_argument(--epochs, typeint, default300) parser.add_argument(--batch-size, typeint, default16, helptotal batch size for all GPUs) parser.add_argument(--img-size, nargs, typeint, default[640, 640], helptrain, val image sizes)对于车牌识别任务我的推荐配置是epochs: 300-500取决于数据集大小batch-size: 根据显存调整一般16-32img-size: [640, 640]平衡精度和速度学习率: 0.01使用余弦退火调度4.3 训练过程监控训练开始后可以通过TensorBoard监控训练过程tensorboard --logdirruns重点关注以下几个指标损失函数变化包括box_loss、obj_loss和cls_loss精度指标mAP0.5和mAP0.5:0.95验证集表现确保没有过拟合如果发现验证集指标明显低于训练集可以尝试增加数据增强强度或使用早停策略。5. 系统实现与部署5.1 PyQt5界面开发为了使系统更易用我使用PyQt5开发了图形界面主要功能包括图片识别选择本地图片进行车牌识别视频识别处理视频文件中的车牌实时检测通过摄像头进行实时车牌识别界面设计采用了模块化思路将检测结果显示、控制按钮和参数设置分区布局。核心检测功能通过多线程实现避免界面卡顿。5.2 模型优化与加速为了提升推理速度我采用了以下几种优化技术模型剪枝移除对输出贡献小的神经元量化将FP32模型转换为INT8减小模型体积TensorRT加速针对NVIDIA GPU的优化推理引擎ONNX格式导出提高模型兼容性使用TensorRT加速后在RTX 3080上推理速度可以从原来的15FPS提升到45FPS完全满足实时性要求。5.3 车牌识别后处理YOLOv5检测到车牌后还需要进行以下后处理步骤透视变换将倾斜车牌矫正为正视图字符分割基于投影法分割单个字符字符识别使用CRNN或传统OCR方法识别字符结果校验根据车牌规则校验识别结果这部分代码需要针对中国车牌特点进行专门优化比如处理中文字符和特殊字符。6. 效果评估与优化6.1 测试结果分析在CCPD测试集上模型的表现如下指标数值mAP0.598.2%精确率97.8%召回率96.5%FPS (RTX 3080)45在复杂场景下如雨天、夜间、遮挡等识别率会有所下降但仍在90%以上。常见错误包括极端光照条件下的车牌检测失败严重遮挡或污损车牌的字符识别错误特殊字体车牌的误识别6.2 性能优化技巧通过实践我总结了以下几个提升识别率的技巧针对特定场景收集数据并微调模型在检测阶段使用更高的置信度阈值如0.8加入车牌颜色识别辅助判断使用多模型集成提高鲁棒性对于实时应用可以通过以下方式优化性能使用多线程处理视频流实现感兴趣区域(ROI)检测减少计算量采用帧间差分法减少重复检测7. 关键代码解析7.1 模型定义核心代码YOLOv5的模型定义主要包含以下几个关键类class Detect(nn.Module): def __init__(self, nc80, anchors(), ch(), inplaceTrue): super().__init__() self.nc nc # 类别数 self.no nc 5 # 每个anchor的输出维度 self.nl len(anchors) # 检测层数 self.na len(anchors[0]) // 2 # 每个检测层的anchor数 self.grid [torch.zeros(1)] * self.nl # 初始化grid self.anchor_grid [torch.zeros(1)] * self.nl # 初始化anchor grid self.register_buffer(anchors, torch.tensor(anchors).float().view(self.nl, -1, 2)) self.m nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # 输出卷积 self.inplace inplace # 是否原地操作 def forward(self, x): z [] # 推理输出 for i in range(self.nl): x[i] self.m[i](x[i]) # 卷积 bs, _, ny, nx x[i].shape x[i] x[i].view(bs, self.na, self.no, ny, nx).permute(0, 1, 3, 4, 2).contiguous() if not self.training: # 推理阶段 if self.grid[i].shape[2:4] ! x[i].shape[2:4]: self.grid[i], self.anchor_grid[i] self._make_grid(nx, ny, i) y x[i].sigmoid() if self.inplace: y[..., 0:2] (y[..., 0:2] * 2 - 0.5 self.grid[i]) * self.stride[i] # xy y[..., 2:4] (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh else: xy (y[..., 0:2] * 2 - 0.5 self.grid[i]) * self.stride[i] # xy wh (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh y torch.cat((xy, wh, y[..., 4:]), -1) z.append(y.view(bs, -1, self.no)) return x if self.training else (torch.cat(z, 1), x)7.2 数据加载关键代码数据加载部分实现了Mosaic增强和自适应缩放class LoadImagesAndLabels(Dataset): def __init__(self, path, img_size640, augmentFalse): self.img_size img_size self.augment augment # 初始化代码... def __getitem__(self, index): # 实现Mosaic增强 if self.augment and random.random() 0.5: img, labels self.load_mosaic(index) else: img, labels self.load_image(index) # 数据增强 if self.augment: img, labels self.random_affine(img, labels) img, labels self.random_perspective(img, labels) return img, labels def load_mosaic(self, index): # 随机选择4张图片 indices [index] random.choices(range(len(self)), k3) random.shuffle(indices) # 创建Mosaic图像 img4 np.zeros((self.img_size * 2, self.img_size * 2, 3), dtypenp.uint8) labels4 [] # 拼接4张图片 for i, index in enumerate(indices): img, labels self.load_image(index) if i 0: # 左上 x1a, y1a, x2a, y2a 0, 0, self.img_size, self.img_size x1b, y1b, x2b, y2b 0, 0, img.shape[1], img.shape[0] elif i 1: # 右上 x1a, y1a, x2a, y2a self.img_size, 0, 2*self.img_size, self.img_size x1b, y1b, x2b, y2b img.shape[1]//2, 0, img.shape[1], img.shape[0] elif i 2: # 左下 x1a, y1a, x2a, y2a 0, self.img_size, self.img_size, 2*self.img_size x1b, y1b, x2b, y2b 0, img.shape[0]//2, img.shape[1], img.shape[0] elif i 3: # 右下 x1a, y1a, x2a, y2a self.img_size, self.img_size, 2*self.img_size, 2*self.img_size x1b, y1b, x2b, y2b img.shape[1]//2, img.shape[0]//2, img.shape[1], img.shape[0] img4[y1a:y2a, x1a:x2a] img[y1b:y2b, x1b:x2b] # 填充图像 padw x1a - x1b padh y1a - y1b # 调整标签坐标 labels[:, 1:] xywhn2xyxy(labels[:, 1:], img.shape[1], img.shape[0], padw, padh) labels4.append(labels) # 合并标签 labels4 np.concatenate(labels4, 0) return img4, labels48. 常见问题与解决方案8.1 训练过程中的常见问题问题1损失函数不收敛或波动大可能原因学习率设置不当、数据标注错误、数据分布不均衡解决方案调整学习率尝试0.01-0.001、检查标注质量、使用类别平衡采样问题2过拟合训练集表现好但验证集差可能原因模型复杂度过高、训练数据不足、数据增强不够解决方案使用更小的模型如YOLOv5s、增加数据增强、添加正则化Dropout、L2问题3显存不足CUDA out of memory可能原因batch size过大、图片尺寸过大、模型太复杂解决方案减小batch size、降低图片尺寸、使用梯度累积8.2 推理阶段的常见问题问题1漏检特别是小车牌可能原因anchor尺寸不匹配、小样本数据不足解决方案重新计算anchor使用k-means聚类、增加小车牌样本问题2误检将非车牌区域识别为车牌可能原因置信度阈值过低、训练数据包含干扰样本解决方案提高置信度阈值如从0.5调到0.8、清理训练数据问题3字符识别错误可能原因车牌倾斜、光照不均、字符分割不准解决方案添加透视变换、光照归一化、优化分割算法注意在实际部署时建议建立一个错误分析流程定期收集bad case并针对性优化模型。这是提升系统性能的最有效方法。9. 项目扩展与优化方向完成基础车牌识别功能后还可以考虑以下几个扩展方向多车牌识别改进算法同时检测和识别画面中的多个车牌车牌属性分析增加车牌颜色、类型普通、新能源、军警等识别车辆特征识别结合车辆品牌、型号、颜色等信息构建更完整的车辆识别系统云端部署将模型部署到云端提供API服务边缘计算使用TensorRT或OpenVINO优化部署到边缘设备对于性能优化可以考虑知识蒸馏用大模型训练小模型保持精度同时提升速度神经架构搜索自动寻找最优模型结构量化感知训练直接训练低精度模型减少量化损失在实际项目中我尝试了知识蒸馏方法使用YOLOv5x作为教师模型训练YOLOv5s在几乎不增加推理时间的情况下将mAP提升了2.3个百分点。