ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于YOLOv8+LPRNet的车牌识别实战:从数据到部署全流程解析

2026/9/8 17:48:10 拓冰建站 浏览量
基于YOLOv8+LPRNet的车牌识别实战:从数据到部署全流程解析 简介基于YOLOv8与LPRNet的车牌识别系统是一套面向毕业设计、课程设计及期末大作业的完整工程适合具备基础Python知识、希望快速获得可演示项目的本科生与开发者。资源整合目标检测与车牌字符识别两条技术链路内含Python源码、已训练模型权重pt/pth、Vue前端页面及Docker部署文件共60个文件压缩包约31.36MB其中jpg评估图表、json配置、py脚本等分别对应可视化分析、参数调整与核心逻辑实现。已有164人学习参考。除代码注释外还随包提供部署教程和准确率、召回率等评估指标曲线可帮助使用者快速复现实验、理解模型训练效果并为答辩展示提供数据支撑。系统功能完善、界面简洁经严格调试可直接运行也支持二次开发是冲击高分毕设的实用选择。 很多朋友在做车牌识别相关课题时第一步就被卡在方案选型上到底是先检测再识别还是端到端一步到位模型用哪个数据集怎么处理说实话网上各种零散资料不少但真正能把“检测识别”全链路串起来、还能给出实操细节的教学却不多。年前我完整跑通了一个基于YOLOv8LPRNet的车牌识别项目从数据准备、模型训练到最终部署推理踩了不少坑也沉淀了一套相对成熟的流程。这套方案的特点很明确YOLOv8负责在复杂场景下精准定位车牌区域LPRNet负责对定位到的车牌进行端到端字符识别两者串联形成完整的识别管线。相比传统的“字符分割独立识别”方案这套组合在速度、精度和工程落地难度之间取得了很好的平衡尤其适合作为毕业设计的核心框架——技术栈新、落地性强、评估指标完整而且扩展开销都在可控范围内。这里把整个项目的核心细节、训练心得和部署经验全部整理出来大家完全可以按图索骥复现。不管你是准备拿它做毕设还是想真正落地到实际场景这篇文章应该能帮你省掉好几个星期的摸索时间。1. 整体方案设计与思路拆解1.1 为什么选择“YOLOv8检测 LPRNet识别”双阶段架构车牌识别这个任务边界其实相当清晰第一步找到车牌的精确位置第二步读出车牌上的字符。这两个步骤适合交给不同类型的模型去解决这也是我最终放弃端到端单一模型方案、坚持“检测识别”双阶段架构的根本原因。先用YOLOv8做检测是因为它在通用目标检测领域的能力已经被大量工程验证过。车牌虽然是一个相对固定的目标但实际场景里它可能处于夜晚低光照、强逆光、雨天泥泞、部分遮挡等复杂状态YOLOv8在特征提取和尺度适应上表现足够稳定能够保证车牌候选框的召回率。而且YOLOv8支持非常细粒度的类别定义——比如“蓝牌”“黄牌”“绿牌新能源”“白牌警用/军用”可以作为不同类别分别训练这在统计不同类型车牌数量、区分大型车小型车等业务需求中非常有用。至于字符识别环节LPRNet是一个极其轻量且高效的方案。它的核心思路是端到端识别不需要提前把车牌字符一个个切割出来而是采用CTCConnectionist Temporal Classification损失函数直接完成序列预测。传统方案需要先做字符分割对车牌倾斜、铆钉遮挡、字符粘连等情况非常敏感而LPRNet天然绕开了这些问题整个识别过程只有一次前向推理速度极快CPU上单张车牌识别仅需几毫秒。从技术合理性上看双阶段方案还有一个隐性优势检测和识别可以独立优化。如果检测召回率低只调检测模型如果字符识别准确率不够单独训练LPRNet即可不用推翻整套系统。这种“低耦合、高内聚”的分层设计对于课程设计或者毕业设计这样需要快速迭代、稳定交付的开发场景简直不要太友好。1.2 方案选型时的另一条路线为什么没选它做方案评估的时候我还认真考虑过类似YOLOv8直接输出车牌字符串的“全流程单模型”方案。这条路本身就存在一个硬伤车牌字符集合包含省份简称汉字31类、大写字母24类排除I和O以及数字10类加起来超过60个类别如果直接在YOLO的检测头里做多标签分类类别不平衡问题会非常突出——有些省份汉字出现的频率极低模型几乎学不到有效特征。而且车牌字符之间的间距不一、字体多样把它们当作普通目标框去检测精度上限不高。另外还有一个“目标检测字符级检测”的方案先检测车牌再检测每一个字符最后用规则拼出车牌号。这个方案在标准化车牌上表现尚可但字符粘连、边框干扰、新能源绿色车牌的字符间隔变化都会让它捉襟见肘而且推理链路里串联了两个检测模型整体帧率明显下降。相比之下LPRNet的稳定性和性能优势确实很难替代。所以我的结论是这样的如果你只是做实验Demo单模型方案或许可以应付但如果要交付一个能在不同场景下稳定运行、还能拿出完整评估报告的系统YOLOv8LPRNet的双阶段方案是实际工程验证过的最优解。2. 环境准备与工程结构解析2.1 软硬件环境说明整个项目我是在一台配置不算高的机器上完成的具体环境如下大家可以参考环境项版本/参数操作系统Ubuntu 20.04 / Windows 11 均可显卡NVIDIA GeForce GTX 1660 Ti6GB显存CPUIntel i5-10400F内存16GBPython3.8PyTorch1.12.0CUDA 11.6Ultralytics YOLOv88.0.xOpenCV4.6.0这里多说一句很多同学关心“训练YOLOv8一定要GPU吗”。如果你只是推理CPU足够一张车牌检测大概几十毫秒但训练阶段强烈建议用GPU1660Ti这个级别就能顺利训练yolov8n和yolov8s再大的模型就有点吃力了。没有独立显卡的话云GPU平台AutoDL、恒源云这类按小时租用对学生党来说成本完全可以接受。2.2 项目目录结构与核心功能工程代码的目录结构也是我反复调整过的最终形成一个单向依赖清晰的结构每一层职责单一排查问题的时候会非常省力。license_plate_recognition/ ├── checkpoints/ # 训练好的模型权重存放目录 │ ├── yolov8n_plate.pt # YOLOv8 车牌检测权重 │ └── lprnet_plate.pth # LPRNet 字符识别权重 ├── config/ # 配置文件 │ ├── detect.yaml # YOLOv8 数据集配置 │ └── plate_config.py # LPRNet 相关配置 ├── dataset/ # 数据集目录 │ ├── CCPD/ # 原始数据集 │ └── yolo_dataset/ # YOLO 格式标注后的数据集 ├── scripts/ │ ├── convert_ccpd2yolo.py # CCPD 标注转 YOLO 格式 │ └── split_datasets.py # 训练集/验证集划分 ├── models/ │ ├── det_model.py # YOLOv8 推理封装 │ └── lprnet.py # LPRNet 网络定义 ├── train_yolo.py # YOLOv8 训练脚本 ├── train_lprnet.py # LPRNet 训练脚本 ├── evaluate.py # 整体评估脚本 └── infer.py # 实时推理脚本从工程习惯的角度讲把配置和代码分离、数据预处理独立成脚本、模型封装成类这些看起来是小事但对毕业设计答辩和后续交接维护来说价值非常大。至少不会出现“跑通了一次改个路径就跑不通了”的尴尬情况。2.3 环境安装与依赖配置创建独立的虚拟环境是第一步不要偷懒直接装在系统Python里后面依赖冲突会让你怀疑人生。conda create -n plate_recognition python3.8 -y conda activate plate_recognition # 安装 PyTorch根据自己的 CUDA 版本选择命令 pip install torch1.12.0 torchvision0.13.0 --extra-index-url https://download.pytorch.org/whl/cu116 # 安装 YOLOv8 依赖库 pip install ultralytics8.0.43 # 安装其他依赖 pip install opencv-python numpy matplotlib pandas tqdm tensorboard需要注意的一点是ultralytics这个库更新非常频繁不同版本之间的API和训练参数存在差异。如果你照着网上的教程跑不通大概率是版本不一致导致的。我这边锁定的是8.0.43跑完整个项目没有遇到兼容问题。版本号写进requirements.txt保证环境可复现。3. 数据准备与模型训练实战3.1 数据集从哪来怎么处理车牌识别领域的公开数据集最权威的就是中科大的CCPDChinese City Parking Dataset包含超过25万张真实场景下的中国车牌图像覆盖了蓝牌、黄牌、绿牌等多种类型并且在角度倾斜、光照变化、模糊程度等方面做了比较细致的划分。CCPD数据集的标注信息是直接在文件名里的例如025-95_113-154383_386473-386473_177454_154383_363402-0_0_22_27_27_33_16-37-15.jpg文件名里包含了车牌边框坐标、四个角点坐标、车牌字符内容等信息但格式比较晦涩需要写脚本转换成YOLO训练所需的txt格式。转换逻辑稍有些绕核心步骤如下从文件名中解析出车牌的四个角点坐标计算最小外接矩形的中心点坐标、宽度和高度将中心点坐标、宽度、高度分别除以图像宽高归一化到0~1之间写入txt文件格式为类别ID x_center y_center width height。这个转换脚本是整个数据准备流程的关键我直接把它独立出来了。转换完成后按照8:1:1的比例划分训练集、验证集和测试集。需要注意的是划分时要确保同一张图片不会同时出现在训练集和验证集中CCPD数据集中存在部分场景连拍的图像如果随机打乱划分很有可能会让验证集“作弊”最终评估结果虚高。3.2 YOLOv8检测模型训练要点YOLOv8的数据集配置文件是一个yaml文件里面指定了训练集和验证集路径、类别数量以及类别名称# config/detect.yaml train: dataset/yolo_dataset/train.txt val: dataset/yolo_dataset/val.txt nc: 1 names: [license_plate]注意这里的train和val路径指向的是存放图片路径列表的txt文件每行一个绝对路径。实际图像和对应标注文件放在同一个目录下YOLO会自动匹配相同文件名但后缀为txt的标注文件。训练命令比较简单yolo detect train dataconfig/detect.yaml modelyolov8n.yaml pretrainedyolov8n.pt epochs150 batch16 imgsz640 device0几个参数的取舍我说一下自己的实际经验模型规模上因为我只需要检测一个类别“车牌”属于简单任务yolov8n和yolov8s的差异不大。最终我选了yolov8n推理帧率更高模型体积只有6MB左右方便部署。imgsz我设置为640这是YOLOv8的默认训练尺寸对车牌检测来说完全够用。如果想进一步提升小目标远处车辆的车牌的检测效果可以试试960但训练时间和显存占用都会上升。epochs我设了150实际上在大约80轮之后mAP就趋于稳定了。建议配合早停机制也就是连续多轮验证集指标不提升就提前结束节省不少时间。如果显存不够报OOM错误优先把batch_size降到8或4同时关闭部分数据增强选项如mosaic0.5不要一上来就换更大的GPU。训练完成后在runs/detect/train目录下会得到best.pt和last.pt两个权重文件。后续推理统一使用best.pt它是在验证集上表现最好的那一轮权重。3.3 LPRNet识别模型训练细节LPRNet的输入是一张已经裁剪好的车牌图像输出是车牌字符串。网络结构的关键点在于CNN主干提取特征后经过一个双向LSTM捕捉字符序列的上下文依赖最后通过CTC损失对齐预测序列和真实车牌字符串。车牌字符的类别定义是这个模型的核心配置需要覆盖完整省份简称汉字京、津、冀、晋、蒙、辽、吉、黑、沪、苏、浙、皖、闽、赣、鲁、豫、鄂、湘、粤、桂、琼、渝、川、贵、云、藏、陕、甘、青、宁、新共31类大写字母除I和O外的24类车牌中不使用这两个字母避免与数字1和0混淆数字0~9共10类。所以LPRNet的最终输出维度是65312410使用CTC损失函数可以让模型在不知道字符精确边界的情况下完成序列识别。训练LPRNet时我的数据来源主要有两个一是CCPD数据集中裁剪好的车牌区域二是通过YOLOv8检测模型批量对图片做推理将检测到的车牌区域裁剪下来作为训练样本。第二种方式有一个额外好处——检测模型和识别模型在真实推理环境中的数据分布更接近识别率会更高。训练参数上learning_rate初始设置为0.01使用Adam优化器batch_size为64。输入图像resize到(94, 24)这个宽高比接近真实车牌的比例。训练过程中加了一些数据增强包括随机亮度调整、高斯模糊、小角度旋转和透视变换这些增强对提升模型的鲁棒性非常关键——尤其要模拟倾斜、逆光、暗光环境下拍摄的车牌。3.4 数据标注的一个关键坑训练YOLOv8之前建议先用LabelImg或X-AnyLabeling人工标注两三百张图片作为初始训练集训练一个初步模型再对剩余图片做自动标注、人工抽检修正。完全从零手工标注上万张图片的工作量实在太大了这种“半自动标注”的人机协作策略在真实的工程项目中是最高效的做法。还有一个容易被忽略的点YOLO格式的标注框必须是水平的矩形框不能输出旋转矩形的角度。实际采集的照片中车牌可能有倾斜但YOLO的检测框就是水平框这个没关系的。检测框稍微大一点、包含一点非车牌区域不影响后续LPRNet裁剪识别但如果框太小把车牌字符截掉了那就必错无疑。4. 评估指标与结果分析4.1 检测模型评估YOLOv8训练完在验证集上Pack了完整的评估结果包括precision、recall、mAP0.5、mAP0.5:0.95等指标。我当时用yolov8n训练150轮后的结果为Precision98.2%Recall97.5%mAP0.599.1%mAP0.5:0.9589.7%mAP0.5指的是IoU阈值设为0.5时的平均精度mAP0.5:0.95则是在0.5到0.95区间取多个IoU阈值计算的平均值后者对框的定位精度要求更高几乎可以看作是模型整体定位能力的黄金标准。训练过程产出的results.png里包含了损失曲线和指标曲线。重点关注训练集损失和验证集损失是否同步下降如果出现训练损失持续下降但验证损失停滞甚至反弹的情况就要考虑过拟合了需要增加数据增强、加Dropout或者减小模型容量。4.2 识别模型评估LPRNet的评估指标主要有两个字符准确率Character Accuracy和整牌准确率Plate Accuracy。字符准确率计算每个字符的预测正确率整牌准确率要求整块车牌的字符串完全匹配才算正确。实际测试中LPRNet的字符准确率可以达到96.5%左右整牌准确率约93%。字符错误主要集中在“0”和“O”的混淆、“冀”和“翼”这类相似字形、以及模糊图像的字符漏识别。所以字符识别的结果一般会配合一个白名单规则做后处理过滤——如果车牌的第2位不是省份简称汉字、第3位不是字母等自动标记为“识别异常”可以显著降低误报率。4.3 整体系统性能端到端性能评估在CPUi5-10400F和GPU1660Ti上分别测试设备检测耗时识别耗时总耗时帧率CPU45ms8ms53ms约18 FPSGPU8ms3ms11ms约90 FPS实际测试用的是场景录像抽帧320张图片全流程识别准确率约92.7%。误检和漏检主要集中在远距离小目标车牌宽度小于20像素和严重逆光场景。这也给部署提出了一条实用建议摄像头安装角度和距离是影响系统效果的最大外部因素。5. 部署与推理实现5.1 图片与视频文件推理推理脚本的设计思路是先用YOLOv8检测车牌位置得到裁剪图像再送入LPRNet进行识别。整个流程封装成一个类PlateRecognizer支持单张图片、视频文件和摄像头实时流。核心推理代码逻辑如下import cv2 import torch import numpy as np from ultralytics import YOLO class PlateRecognizer: def __init__(self, det_weightcheckpoints/yolov8n_plate.pt, rec_weightcheckpoints/lprnet_plate.pth): # 初始化检测模型 self.det_model YOLO(det_weight) # 初始化识别模型 self.rec_model LPRNet(in_channels3, num_classes65) self.rec_model.load_state_dict(torch.load(rec_weight, map_locationcpu)) self.rec_model.eval() # 字符映射表按训练时定义的顺序 self.char_dict 京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新ABCDEFGHJKLMNPQRSTUVWXYZ0123456789 def recognize_plate(self, image): # 检测车牌位置 results self.det_model.predict(image, conf0.45, imgsz640, verboseFalse) plates [] for result in results: boxes result.boxes if boxes is None: continue for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() # 裁剪车牌区域 crop image[int(y1):int(y2), int(x1):int(x2)] # 识别车牌字符 plate_no self.recognize_chars(crop) plates.append({ bbox: [x1, y1, x2, y2], plate_no: plate_no, conf: float(box.conf[0]) }) return plates这里有一个细节值得单独提出来识别的置信度阈值不要设太低。YOLOv8在低置信度下会输出大量冗余检测框导致同一块车牌被多次识别浪费计算资源。实测下来conf0.45是一个比较合理的起点如果场景复杂、误检偏高可以提高到0.5甚至0.55。5.2 摄像头实时识别接入摄像头最常用的是OpenCV的VideoCapture支持USB摄像头设备编号0、1等和RTSP网络摄像头输入流地址。实时推理的完整流程中包含一个性能优化小技巧跳帧处理。对于帧率25~30的摄像头画面车牌识别系统完全不需要每一帧都做检测和识别。我一般设置每3帧检测一次中间帧直接沿用上一次的检测结果这样CPU占用能降低约60%对识别精度几乎没有影响。cap cv2.VideoCapture(0) # 或者传RTSP地址 frame_interval 3 frame_count 0 while True: ret, frame cap.read() if not ret: break if frame_count % frame_interval 0: plates recognizer.recognize_plate(frame) last_plates plates # 绘制检测框和车牌号 for item in last_plates: x1, y1, x2, y2 item[bbox] label item[plate_no] cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(License Plate Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break frame_count 1 cap.release() cv2.destroyAllWindows()需要特别提醒的是不要把检测框画在原始帧上之后再对加了框的整图做识别。正确顺序是先裁剪识别再绘制结果。因为画框和文字会污染图像干扰后续识别这个逻辑错误我在调试阶段犯过一次排查了半天才找到原因。5.3 模型导出与轻量化部署如果要部署到嵌入式设备或边缘计算盒子上比如Jetson Nano、RK3588这类平台PyTorch模型直接跑不够高效需要先导出成通用格式。YOLOv8的导出非常便捷一条命令搞定yolo export modelcheckpoints/yolov8n_plate.pt formatonnx opset12 simplifyTrueLPRNet的ONNX导出稍微麻烦一点因为网络里包含LSTM和CTC相关算子导出时偶尔会遇到不兼容问题。我的处理方案是把LPRNet的forward拆分成两部分只导出CNN主干加LSTM特征部分CTC解码则在ONNX Runtime外部用纯Python实现。这样既保证了ONNX导出顺利又不会影响推理速度。对于树莓派这类更轻量的设备更极端但实用的做法是用YOLOv8的nano模型检测车牌裁剪后直接把小图发给LPRNet识别。实测在树莓派4B上整体识别速率能够达到8~10 FPS基本满足低速通道场景的需求。如果场景固定还可以用图像差分或背景建模判断画面中是否有车辆出现只有检测到新目标时才触发车牌识别进一步降低功耗和资源占用。6. 常见问题与排查技巧实录6.1 训练YOLOv8时损失震荡、不收敛这是训练阶段最常遇到的问题损失曲线像锯齿一样上下跳动看不出明确下降趋势。最常见的原因是学习率设置过高或者数据增强过强。YOLOv8默认开启Mosaic、MixUp等增强如果数据集本身比较小增强太猛反而会让模型学不到稳定特征。我的做法是先关掉Mosaic和MixUp等模型在基础数据上收敛了再逐步打开增强项。另外检查一下标注是不是有问题用可视化脚本把标注框画到图片上肉眼看一遍看有没有标注错位、宽高为0、类别标错等问题。很多“不收敛”其实是因为标注数据错误太多。6.2 LPRNet对中文汉字识别差怎么办LPRNet训练初期最常见的失败模式是字母和数字都认得准但省份汉字老是识别错。原因很简单CCPD数据集中各省车牌的分布极不均匀广东车牌「粤」占了绝对大头冷门省份的汉字样本寥寥无几模型根本没机会学会。针对这个问题除了尽量补充数据外可以在训练时按省份对样本做重采样确保每个省份汉字的样本量不低于阈值。还有一个实用的后处理技巧中国车牌第一位一定是省份简称汉字如果模型输出的第一位不是汉字而是字母或数字直接判定识别失败重新截取或拒识避免输出明显不合理的车牌号。6.3 检测框抖动、同一车牌多次识别视频流中检测框抖动有两种典型原因一是单帧检测的置信度波动导致框忽大忽小二是因为模型在一帧画面里输出了多个交叠的检测框。第一种情况可以通过跟踪算法解决——给每个目标分配ID用卡尔曼滤波平滑检测框的位置和大小常见方案是ByteTrack和YOLOv8能无缝衔接。第二种情况YOLOv8自带的NMS已经做了大部分去重如果还出现同一个车牌被识别成两个框检查一下NMS的IoU阈值设置适当调高。6.4 CPU推理速度太慢CPU上跑YOLOv8最有效的优化手段是换更小的输入尺寸。车牌检测对分辨率不算特别敏感把推理imgsz从640降到416速度可以提升近一倍精度损失大约只有1~2个百分点。再配合ONNX Runtime的CPU优化和OpenVINO推理后端在普通办公电脑上跑出25 FPS是完全可行的。7. 项目扩展思路与经验总结这个项目做完以后我最大的体会是车牌识别虽然看起来“小而专”但麻雀虽小、五脏俱全它几乎覆盖了计算机视觉落地项目里所有核心环节——目标检测、图像分类、序列识别、模型部署、性能调优。把这一整套流程跑通了再做其他视觉项目基本就是换数据集和调参数的问题了。如果想把毕设往上提升一个档次可以在当前基础上加一些扩展功能比如车位占用识别与统计结合车牌信息实现车辆入场出场匹配这是智慧停车场景里的刚需车辆颜色与品牌识别在YOLOv8里增加对应的类别标签共用同一个检测框架违规停车检测检测到车辆在禁止区域停留超过阈值时间即触发报警需要结合跟踪逻辑才能做到夜间红外补光效果优化通过收集夜间数据做针对性训练用Gradio或者Flask包一层Web界面把模型服务化方便演示和答辩时现场操作。这套系统的所有核心代码、训练好的模型权重和评估指标曲线我已经整理归档。部署教程也写得比较详细从conda环境创建到最终的摄像头实时识别每一步都验证过可行。如果你按照这篇文章的顺序操作应该能比较顺利地把整个系统跑通。要是遇到文章里没有覆盖到的问题欢迎留言交流我看到了会尽量回复。本文还有配套的精品资源点击获取