新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于YOLOv8和LPRNet的车牌识别系统:从跑通到改进的完整指南

发布时间:2026/9/27 1:30:25来源:尧图网络
基于YOLOv8和LPRNet的车牌识别系统:从跑通到改进的完整指南
简介这份资源是面向计算机、数学、电子信息等专业学生的毕业设计与课程设计参考资料核心为基于 YOLOv8 与 LPRNet 的车牌识别系统提供完整 Python 源码与训练好的模型权重下载后可直接运行调试。项目采用 Flask 搭建后端服务前端使用 Vue 构建可视化界面并配套 Dockerfile 与依赖清单便于环境部署与二次开发。压缩包共 60 个文件约 31.35MB涵盖 13 个 py 脚本、3 个 pt 模型文件、1 个 pth 权重、3 个 json 配置、3 个 vue 组件以及 jpg 示例图片、md 说明文档等覆盖数据处理、模型训练、推理部署与前端展示全流程。目前已有 606 人学习下载适合作为毕设选题、期末大作业或算法入门练手项目读者可借此理解车牌检测与字符识别的两阶段实现思路掌握数据集标注、模型训练与 Web 端集成的完整链路并在此基础上自行扩展功能。1. 车牌识别这套源码为什么我建议先跑通再谈改进车牌识别这个方向每年毕业设计都有人做但真正能一次跑通的没几个。我拿到这份「基于 YOLOv8 和 LPRNet 的车牌识别系统 python 源码 模型」时第一反应不是看代码写得多漂亮而是先确认两件事检测和识别是不是真的解耦成两个独立模型以及模型权重是不是随包给的。答案是肯定的——YOLOv8 负责把车牌从整张图里框出来LPRNet 负责把框出来的车牌小图转成字符序列。这个两阶段结构是目前工业界车牌识别最稳的路线也是最适合拿来改毕业设计的骨架。它解决的核心问题是你不需要从零训练一个端到端模型也不需要自己标注几万张车牌数据。包里已经带了训练好的检测权重和识别权重你拿到手就能对一张普通街景图跑出「京A12345」这样的结果。适合谁适合正在做毕业设计、需要快速出 demo 的本科生也适合想拿一个干净 baseline 做二次开发的研究生。但前提是你得先把环境跑通而不是一上来就改网络结构。2. 拆开这个 zipYOLOv8 检测 LPRNet 识别的两阶段结构2.1 为什么是两阶段而不是端到端车牌识别看起来是一个「图进字出」的任务但实际工程里几乎没人用纯端到端。原因很直接车牌在整张图里占比很小端到端模型既要定位又要识别训练时梯度会被背景淹没收敛慢且容易过拟合。两阶段的好处是每一段只做一件事——YOLOv8 只管「车牌在哪」LPRNet 只管「车牌上写了什么」。这样你调检测的时候不影响识别调识别的时候不影响检测排查问题的时候能快速定位是哪一段出了错。YOLOv8 在这份源码里用的是 nano 或 small 级别的权重输入尺寸通常是 640×640。检测输出的是车牌的边界框坐标代码里会做一个 padding 扩展把框稍微放大一点再裁剪目的是让 LPRNet 能看到完整的字符边缘。这个 padding 比例一般设在 0.1 到 0.15 之间太小会切掉字符太大会引入过多背景。LPRNet 是一个轻量级的字符识别网络核心是 CNN CTC 损失。它不需要对每个字符做分割直接输出字符序列的概率分布再用 CTC 解码成最终字符串。这个设计对车牌这种定长但字符间距不固定的场景非常友好。源码里 LPRNet 的输入尺寸通常是 94×24 或 128×32具体看训练时的配置。2.2 源码目录里每个文件干什么拿到包之后先别急着跑花五分钟把目录结构看清楚后面能省很多时间。典型的目录长这样plate_recognition/ ├── detect/ │ ├── yolov8_plate.pt # YOLOv8 车牌检测权重 │ └── detect.py # 检测推理脚本 ├── recognize/ │ ├── lprnet_best.pth # LPRNet 识别权重 │ ├── lprnet.py # 网络定义 │ └── recognize.py # 识别推理脚本 ├── utils/ │ ├── plate_chars.py # 字符集定义 │ └── image_process.py # 裁剪、归一化等预处理 ├── demo.jpg # 示例图片 └── main.py # 串联检测和识别的入口plate_chars.py这个文件特别重要它定义了 LPRNet 能识别的字符集。中国大陆车牌通常包含省份简称、字母、数字和特殊字符一共 68 个左右。如果你后面要换自己的权重字符集必须和训练时完全一致否则解码出来的就是乱码。main.py是整条流水线的入口逻辑是读图 → YOLOv8 检测 → 裁剪车牌区域 → 预处理 → LPRNet 识别 → 输出结果。你第一次跑通就用这个文件不要一上来就改detect.py或recognize.py。2.3 环境依赖和版本锁定这份源码对版本比较敏感尤其是 ultralytics 和 PyTorch 的搭配。我建议直接用 Python 3.8 或 3.9不要用 3.11 以上否则某些依赖会编译失败。下面是我验证过能跑通的依赖清单# 创建虚拟环境避免污染系统 Python python -m venv venv_plate source venv_plate/bin/activate # Windows 用 venv_plate\Scripts\activate # 安装 PyTorchCPU 版本就够跑推理 pip install torch1.13.1 torchvision0.14.1 --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics 和其他依赖 pip install ultralytics8.0.145 opencv-python4.8.0.74 numpy1.24.3 pillow9.5.0这里锁ultralytics8.0.145是有原因的新版本改了 APIYOLO类的某些参数名变了源码里如果写的是旧参数名跑起来会报TypeError。opencv-python锁 4.8 是因为 4.9 之后某些图像解码行为有变化可能导致裁剪区域偏移。这些版本号不是随便写的是我踩过坑之后回退确认的。提示如果你用的是 Windowspip install之前先确认 Visual C Redistributable 已经装了否则 opencv 可能导入失败。3. 从一张图到一串字符推理流水线的代码级拆解3.1 检测阶段YOLOv8 怎么框出车牌检测这一步的代码不复杂但有几个参数直接决定后面识别能不能做。先看核心代码from ultralytics import YOLO import cv2 # 加载车牌检测模型 model YOLO(detect/yolov8_plate.pt) # 读图并推理 img cv2.imread(demo.jpg) results model(img, conf0.25, iou0.45, imgsz640) # 提取边界框 boxes results[0].boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf box.conf[0].item() cls int(box.cls[0].item()) print(f检测到车牌: 坐标({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}), 置信度{conf:.2f})conf0.25是置信度阈值低于这个值的框会被丢弃。车牌检测场景下不建议设太高因为远距离或模糊车牌的置信度天然偏低设 0.5 以上容易漏检。iou0.45是 NMS 的 IoU 阈值控制重叠框的合并程度。imgsz640是推理分辨率如果你图片里车牌特别小可以调到 1280但速度会慢一倍左右。检测完之后不要直接把x1,y1,x2,y2拿去裁剪先做一个 padding 扩展import numpy as np def expand_box(x1, y1, x2, y2, w, h, pad_ratio0.12): 按比例扩展边界框避免切掉字符边缘 bw, bh x2 - x1, y2 - y1 pad_w, pad_h bw * pad_ratio, bh * pad_ratio nx1 max(0, int(x1 - pad_w)) ny1 max(0, int(y1 - pad_h)) nx2 min(w, int(x2 pad_w)) ny2 min(h, int(y2 pad_h)) return nx1, ny1, nx2, ny2pad_ratio0.12是我试出来的经验值。太小比如 0.05时如果检测框本身偏紧字符的上下边缘会被切掉LPRNet 识别出来会缺字。太大比如 0.3时裁剪区域会包含太多车身或背景LPRNet 的注意力被分散置信度下降。0.1 到 0.15 之间是比较稳的区间。3.2 识别阶段LPRNet 的输入预处理和 CTC 解码裁剪出来的车牌图不能直接丢给 LPRNet要先做尺寸归一化和归一化。LPRNet 对输入尺寸有固定要求源码里一般是 94×24import cv2 import numpy as np import torch def preprocess_plate(plate_img, target_size(94, 24)): 将裁剪的车牌图缩放到 LPRNet 输入尺寸并归一化 img cv2.resize(plate_img, target_size, interpolationcv2.INTER_LINEAR) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img img.astype(np.float32) / 255.0 img (img - 0.5) / 0.5 # 归一化到 [-1, 1] img np.transpose(img, (2, 0, 1)) # HWC - CHW tensor torch.from_numpy(img).unsqueeze(0) # 增加 batch 维度 return tensor这里有两个容易翻车的点。第一cv2.resize的插值方式用INTER_LINEAR就够不要用INTER_CUBIC后者在放大时会产生振铃效应反而让字符边缘变模糊。第二归一化参数(img - 0.5) / 0.5必须和训练时一致如果你换了权重但没改归一化参数识别结果会完全错乱。LPRNet 的输出是一个(T, N, C)的张量T 是时间步N 是 batchC 是字符集大小。CTC 解码用贪心策略就够了def ctc_greedy_decode(logits, chars): CTC 贪心解码取每个时间步最大概率的字符去重去 blank indices torch.argmax(logits, dim2) # (T, N) indices indices[:, 0].cpu().numpy() # 取第一个 batch result [] prev -1 for idx in indices: if idx ! prev and idx ! 0: # 0 是 blank result.append(chars[idx - 1]) prev idx return .join(result)chars就是plate_chars.py里定义的字符集。注意idx - 1是因为 CTC 的 blank 占了索引 0实际字符从 1 开始。这个偏移量如果搞错所有识别结果都会整体错位一位。3.3 把两段串起来main.py 的完整调用链理解了检测和识别各自的逻辑之后main.py就是一条直线import cv2 from detect.detect import detect_plates from recognize.recognize import recognize_plate img cv2.imread(demo.jpg) h, w img.shape[:2] # 第一步检测 boxes detect_plates(img, conf0.25, iou0.45) # 第二步逐个裁剪并识别 for (x1, y1, x2, y2) in boxes: nx1, ny1, nx2, ny2 expand_box(x1, y1, x2, y2, w, h, pad_ratio0.12) plate_img img[ny1:ny2, nx1:nx2] text recognize_plate(plate_img) print(f车牌号: {text}) cv2.rectangle(img, (nx1, ny1), (nx2, ny2), (0, 255, 0), 2) cv2.putText(img, text, (nx1, ny1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imwrite(result.jpg, img)跑完这一步你会得到一张画了框、标了车牌号的result.jpg。如果框的位置对但字符是乱的问题在识别段如果框都没出来问题在检测段。这种分段排查的思路比端到端模型省时间得多。4. 避坑排查跑不通时先看这五个地方4.1 报错No module named ultralytics或版本不兼容现象pip install ultralytics之后导入仍然报错或者YOLO()初始化时报TypeError: __init__() got an unexpected keyword argument。原因ultralytics 8.x 的 API 在 8.0.100 之后有过一次调整YOLO类的某些参数名从model改成了weights或者predict的参数名变了。源码如果是按旧版本写的装最新版就会挂。解决先pip show ultralytics看版本如果不是 8.0.145 左右直接pip install ultralytics8.0.145锁死。如果还报错把YOLO(xxx.pt)改成YOLO(modelxxx.pt)试试这是最常见的参数名差异。4.2 检测框位置对但识别结果是乱码现象result.jpg里框的位置很准但putText出来的字符是「###」或者完全不相干的字母。原因九成是字符集不匹配。LPRNet 的输出索引要映射到plate_chars.py里的字符列表如果你换了权重但没换字符集或者字符集顺序和训练时不一致解码出来就是乱的。解决打开plate_chars.py确认字符列表的顺序和长度。中国大陆车牌通常是 68 个字符含 blank如果你用的是新能源车牌权重字符集可能不一样。最稳妥的办法是找权重作者确认字符集或者用一张已知车牌号的图反推——如果「京」被识别成「A」说明索引偏移了。4.3 图片里车牌很小检测直接漏掉现象整张图能看到车牌但 YOLOv8 一个框都没输出或者只输出了置信度很低的框。原因YOLOv8 默认输入 640×640如果原图是 4000×3000车牌在缩放后可能只有几十个像素特征太弱。解决两个方向。一是把imgsz调到 1280 或 1536让车牌在输入里占更多像素二是先对原图做裁剪把车牌区域大致裁出来再送检测。我一般会先跑一次 640如果没框就自动升到 1280 重跑这个逻辑在detect.py里加个循环就行。4.4 中文车牌省份简称识别不出来现象数字和字母都对但第一个汉字如「京」「沪」「粤」总是错或者变成 blank。原因LPRNet 的训练数据里如果省份简称样本太少模型对这类字符的响应就弱。另外省份简称的笔画复杂在 94×24 的输入尺寸下信息损失严重。解决短期办法是把 LPRNet 输入尺寸从 94×24 调到 128×32给汉字更多像素。长期办法是拿一批带省份简称的车牌图对 LPRNet 做 fine-tune只调最后几层学习率设 1e-4跑 10 个 epoch 左右就能明显改善。4.5 CPU 推理速度太慢一张图要好几秒现象在没独显的笔记本上跑YOLOv8 推理就要 2 秒以上加上 LPRNet 总共 3 到 4 秒。原因ultralytics 默认用 PyTorch 的 CPU 推理没有做图优化。YOLOv8n 在 CPU 上理论可以做到 100ms 以内但默认配置没启用。解决导出 ONNX 模型用 onnxruntime 推理速度能快 2 到 3 倍。命令是yolo export modelyolov8_plate.pt formatonnx然后用onnxruntime.InferenceSession加载。如果不想换推理框架至少把torch.set_num_threads(4)加上利用多核。5. 换自己的数据从标注到微调的完整路径5.1 用 labelme 标注车牌并转 YOLO 格式如果你要做自己场景的车牌检测比如停车场、收费站第一步是标注。labelme 是最顺手的工具标完得到的是 JSON需要转成 YOLO 的 txt 格式import json import os def labelme_to_yolo(json_path, output_dir, class_id0): 将 labelme 的 JSON 标注转成 YOLO 格式的 txt with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x1, x2 min(xs), max(xs) y1, y2 min(ys), max(ys) # YOLO 格式class_id cx cy w h全部归一化到 0-1 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h lines.append(f{class_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) txt_name os.path.splitext(os.path.basename(json_path))[0] .txt with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines))class_id0是因为车牌检测只有一个类别。归一化的时候注意cx和cy是中心点坐标不是左上角。这个转换脚本我一般会批量跑把整个目录的 JSON 一次性转完。5.2 YOLOv8 微调的关键参数转完格式之后写一个data.yamlpath: ./dataset train: images/train val: images/val nc: 1 names: [plate]然后启动训练yolo detect train modelyolov8n.pt datadata.yaml epochs100 imgsz640 batch16 lr00.01epochs100对车牌这种单类别任务通常够了如果 loss 还在降可以加到 150。batch16是 8G 显存下的安全值显存不够就降到 8。lr00.01是初始学习率微调时如果数据量小于 2000 张建议降到 0.001避免把预训练权重冲掉。训练完之后runs/detect/train/weights/best.pt就是你的新检测权重。把它替换掉detect/yolov8_plate.pt重新跑main.py就能看到自己场景的效果。5.3 LPRNet 微调时字符集怎么对齐LPRNet 的微调比 YOLOv8 麻烦一点因为字符集必须和训练数据完全对齐。如果你只做标准蓝牌字符集不用动如果要加新能源绿牌或黄牌字符集要扩充。扩充字符集的步骤是先统计你所有训练图片里出现过的字符去重后排序确保 blank 在索引 0。然后修改lprnet.py里最后一层全连接层的输出维度改成len(chars) 1。最后加载预训练权重时用strictFalse跳过最后一层的形状不匹配model LPRNet(charschars) state_dict torch.load(lprnet_best.pth, map_locationcpu) model.load_state_dict(state_dict, strictFalse) # 最后一层会重新初始化strictFalse的意思是允许部分层不匹配不匹配的层保持随机初始化。这样你既能复用前面卷积层的特征提取能力又能让最后一层适应新的字符集。微调时学习率设 1e-4跑 20 到 30 个 epoch观察验证集上的准确率。5.4 验证微调效果别只看 loss训练完之后不要只看 loss 曲线要拿一批没参与训练的图做端到端测试。我一般会准备 50 张图人工记录真实车牌号然后跑main.py统计完全正确的比例。如果检测框准但字符错问题在 LPRNet如果框偏了问题在 YOLOv8。这个分段的验证方法比看 loss 直观得多。另外注意车牌识别里「完全正确」的标准很严格——只要有一个字符错就算错。所以如果你的准确率是 80%意味着 5 张里有 1 张至少错一个字符。这个指标在毕业设计里够用但离工业级还有距离。想往上提要么加数据要么在 LPRNet 后面接一个语言模型做后处理把「京A12345」这种不符合规则的组合纠正过来。6. 把 demo 变成能演示的系统三个我踩过的坑第一个坑是路径问题。源码里很多地方写的是相对路径比如detect/yolov8_plate.pt你在项目根目录跑没问题但如果你在detect/目录里跑python detect.py就会报文件找不到。我的习惯是在所有脚本开头加os.chdir(os.path.dirname(os.path.abspath(__file__)))把工作目录强制切到脚本所在目录这样不管从哪跑都不会错。第二个坑是 OpenCV 的imread读中文路径会返回 None。如果你的图片放在「D:/毕业设计/测试图/1.jpg」这种路径下cv2.imread直接失败。解决办法是用cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)代替或者把图片路径改成全英文。这个坑我当年调了一下午才定位到血泪经验。第三个坑是模型权重加载时的设备不匹配。如果你在 GPU 上训练的权重拿到 CPU 机器上加载会报RuntimeError: Attempting to deserialize object on a CUDA device。解决是在torch.load里加map_locationcpu这个参数一定要养成习惯加上不管当前有没有 GPU。从那以后我每次拿到一个新的识别项目都强制走一遍「单图推理 → 批量测试 → 边界 case 检查」的流程不跳过任何一步。这份源码的价值不在于它多完美而在于它给了一个能跑通的起点你在这个起点上改网络、换数据、调参数都比从零搭要快得多。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

拒绝拖延!百度网站官网网址搭建完整流程 2026/9/27 4:39:01

拒绝拖延!百度网站官网网址搭建完整流程

拒绝拖延!百度网站官网网址搭建完整流程 改个需求建站公司拖一周,这种憋屈事儿谁没遇到过?很多老板找外包做站,合同签得清清楚楚,结果上线后想改个Banner图、调个导航栏,对方要么找借口推脱,要么报价离谱,直接把项目周期拖垮。其实,问题根本不…

阅读更多 →
RK3588还是T527?工业芯片选型对比与实操指南 2026/9/27 4:39:01

RK3588还是T527?工业芯片选型对比与实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
告别多客服系统割裂:迭我分身消息机器人,海内外 IM 全渠道统一接入的 AI 客服智能体 2026/9/27 4:38:54

告别多客服系统割裂:迭我分身消息机器人,海内外 IM 全渠道统一接入的 AI 客服智能体

企业客服长期面临多渠道消息分散、多软件来回切换、人工重复咨询量大的痛点。市面上大量 AI 客服仅实现单点对话能力,本质只是网页聊天框,无法深度融入业务流程。本文介绍迭我分身消息机器人,一套支持海内外 IM 平台统一接入的 AI 客服智能体…

阅读更多 →
如何建立网站自己做站长:5个步骤+避坑指南,新手零失败 2026/9/27 4:38:48

如何建立网站自己做站长:5个步骤+避坑指南,新手零失败

如何建立网站自己做站长:5个步骤+避坑指南,新手零失败 上周一个做独立站的朋友深夜炸锅,服务器日志里全是乱七八糟的跳转链接,首页秒变博彩广告。他慌得问我:“网站被黑挂马不知道怎么办?数据全乱了,客户全跑了!”这种惨剧太常见了。很多新手想…

阅读更多 →
强化学习图像分类实战:MNIST/Fashion-MNIST完整MDP建模与免模型训练 2026/9/27 4:38:42

强化学习图像分类实战:MNIST/Fashion-MNIST完整MDP建模与免模型训练

简介:本资源是一套面向机器学习初学者与进阶实践者的强化学习系统教程与可运行项目,聚焦理论理解与代码落地的结合,解决从MDP建模、策略优化到真实任务训练的知识断层问题。压缩包共18个文件(14个Python源码、1个说明文档、1个REA…

阅读更多 →
企业网站建立的流程全解析:不写代码也能搞定源码下载 2026/9/27 4:38:35

企业网站建立的流程全解析:不写代码也能搞定源码下载

企业网站建立的流程全解析:不写代码也能搞定源码下载 手里攥着几十万预算,脑子里全是画面,但打开电脑只会打字,不会写一行代码。这是无数中小企业主做官网时的噩梦。别慌,这行干了十年,我见过太多人因为不懂技术被外包公司坑得底裤都不剩。今天把【企业…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉