新闻详情

新闻详情

首页 / 资讯中心 / 详情

YOLOv8车牌识别实战:从数据标注到部署的完整指南

发布时间:2026/9/24 22:42:41来源:尧图网络
YOLOv8车牌识别实战:从数据标注到部署的完整指南
简介基于YOLOv8实现车牌检测与车牌识别的完整项目源码及详细流程教程面向计算机视觉开发者、算法研究人员和智能交通项目从业者解决从车牌定位到字符识别的端到端落地问题。资源包共55个文件压缩包约32.93MB涵盖jpg/png图片样本、ipynb交互式分析笔记、pt模型权重、mp4演示视频、yaml配置与README说明文档等类型丰富且目录结构清晰当前已有298人学习下载。读者可借助项目中的车牌与车辆检测Notebook、预训练模型和演示视频快速复现识别流程并通过教程全面了解数据集准备、模型训练与性能评估等关键步骤。整体思路结合YOLOv8的多尺度特征融合与高效损失函数设计兼顾检测速度与精度并对复杂光照、角度变化等场景进行了针对性处理可作为毕业设计、竞赛实践或工业级车牌识别系统开发的直接参考。1. 车牌识别项目实战YOLOv8检测加识别这条线值不值得上手一个 YOLOv8 车牌识别项目听上去是两个任务先定位车牌再把车牌号读出来。很多教程包喜欢把整套流程封装成几十个 py 文件新手解压后最容易卡在“demo 能跑换自己的图片就翻车”。这篇文章只讲一件事如何用 YOLOv8 把车牌检测和车牌识别真正跑通。从环境搭建、数据标注、训练、后处理到部署每一步都给可复现的命令和参数适合刚做完 YOLOv8 目标检测练习、想转一个落地小项目的开发者。你可以把它当成第一个能放到边缘盒子上演示的视觉方案也可以当作理解两段式识别架构的起步案例。只要手里有一批带车牌的图片和一块普通显卡就能跟着做下来。2. 车牌识别架构拆解YOLOv8做检测、再做字符识别两条路线怎么选2.1 两段式才是落地主流检测车牌和识别字符分开做的原因拿到车牌识别项目先别急着训练一个模型同时输出车牌位置和字符串。无论标题里怎么写实际工程里更常见的做法是两段式YOLOv8 先检测车牌再对裁剪后的车牌图片做文字识别。原因有两点。第一两段的数据组织难度完全不同。车牌检测只需要一类plate几百张图就能见效车牌识别要覆盖 31 个汉字、24 个字母和 10 个数字还要应对蓝牌、绿牌、黄牌、白牌和双层车牌字符级标注工作量更大。第二错误定位方便。如果识别结果乱码我可以在检测框输出位置直接裁剪原图判断是检测框偏了还是文字识别错了一段式模型一旦出错很难判断问题出在哪个环节。车牌识别算法本身也有多种实现。常见的是用 YOLOv8 做字符检测即第二个模型在裁剪图上检测每个汉字、字母、数字的位置并同时给出类别另一种是检测车牌后接 CRNN 或 PaddleOCR 做整牌识别。标题里“基于 YOLOv8 实现车牌检测车牌识别算法”更贴近前一种两个模型都在 Ultralytics 生态里训练、导出、部署的接口一致学习成本最低。还要说清楚一个容易踩的误区不要指望 YOLOv8 一个模型直接输出字符串。YOLO 输出的是框和类别车牌识别本质上是变长序列识别字符之间有强顺序关系。即使把每个字符当成独立目标检测出来也必须在后处理里做排序、去重和长度校验。这部分排序逻辑就是整个项目里最容易写错、也最值得专门写测试的地方。2.2 最小可跑环境Ubuntu 20.04 CPU与GTX 1660 Ti的YOLOv8配置环境配置不用一步到位。常见做法是先在一台 Ubuntu 20.04 机器上把 CPU 版本跑通确认代码链路没有断再换到 NVIDIA 显卡上训练。这里给两套我常用的组合用途推荐环境说明流程验证Ubuntu 20.04 CPU Python 3.10只跑推理和少量训练imgsz 降到 320模型训练GTX 1660 Ti 6GB CUDA 11.8适合 YOLOv8n、YOLOv8sbatch 控制在 8 左右安装命令很直接# 推荐 Python 3.10Ultralytics 对 3.8~3.11 都支持3.10 兼容性最稳 conda create -n lpr python3.10 -y conda activate lpr pip install ultralytics opencv-python # 验证安装能打印出模型结构说明环境正常 python -c from ultralytics import YOLO; model YOLO(yolov8n.pt)这里yolov8n.pt是 YOLOv8 官方预训练权重。如果安装时网络受限可以先把权重文件放到当前目录再执行上面的验证命令。CPU 环境不需要安装 CUDAUltralytics 会自动退回 CPUExecutionProviderGPU 环境建议先执行nvidia-smi确认驱动版本再装对应 CUDA。GTX 1660 Ti 是 6GB 显存训练 YOLOv8n 时可以开batch8, imgsz640换到 YOLOv8s 后建议降到batch4。千万不要直接上 YOLOv8l显存不够会出现难排查的CUDA out of memory而且对车牌这类小目标提升有限。2.3 最小推理脚本解压源码后先确认检测环节是否通拿到项目源码包后我一般不会立刻看完整教程而是先找一个测试图片把检测模型的最小推理跑通。这样能快速排除环境问题也能确认best.pt是否真的存在。from ultralytics import YOLO # 换成你训练好的车牌检测权重没有就用官方yolov8n.pt做流程验证 model YOLO(best.pt) result model.predict( test.jpg, imgsz640, # 现场图片车牌较小用640视频流可降到416 conf0.4, # 漏检多就降到0.25误检多就升到0.5 device0, # CPU环境改为 devicecpu verboseFalse ) # 拿到检测框后自己打印不要只依赖可视化结果 boxes result[0].boxes.xyxy.cpu().numpy() for x1, y1, x2, y2 in boxes: print(fplate: {int(x1)}, {int(y1)}, {int(x2)}, {int(y2)})conf的调整需要现场判断。车牌检测的置信度通常不会像通用物体那么高逆光、模糊、夜间场景下 0.4 都可能漏检但如果道闸场景里经常把车灯当车牌就要上调到 0.5 以上。第一版先不追求最优只要框能落在车牌周围后面的识别流程才有意义。3. 车牌数据集与训练环节从LabelMe标注到YOLOv8训练自己的车牌模型3.1 数据从哪来开源数据集、自采补拍与多场景扩样训练车牌检测200 到 500 张图就足够出第一个可用版本。网上有公开的中国车牌检测数据集也有类似 CCPD 这种偏停车场场景的蓝牌数据集。它们适合做预训练但直接拿预训练权重到现场往往不够因为采样角度看门禁、路侧和停车场差别很大。我的做法是三步先用公开数据集把模型结构验证一遍再补拍自己项目里的真实角度照片最后从现场视频里抽帧筛选。补拍时要注意一个容易被忽略的问题同一辆车在过去几秒内会被连续抽到十几帧这些帧高度相似。如果不做去重训练集和验证集里可能同时出现同一辆车mAP 虚高换新场景就翻车。扩样时不要只做随机翻转。车牌上的汉字、字母翻转后会失去语义所以水平翻转只能用于检测阶段且要谨慎。比较可靠的增强是轻微旋转、亮度扰动、运动模糊、仿射变换和随机遮挡。把这些增强配置写进 Ultralytics 的augment参数比自己在数据预处理里手工拼更省事。3.2 LabelMe JSON转YOLO格式转换脚本与四个边界坑LabelMe 是车牌标注里很常用的工具但它保存的是 JSON 格式YOLOv8 训练需要的是 YOLO txt 格式。每个标注文件转换逻辑不复杂问题常出在细节上。import json from pathlib import Path def labelme2yolo(json_path, out_dir, class_names): 把LabelMe JSON转成YOLO txt。 class_names: 训练YAML里names的列表顺序必须一致。 with open(json_path, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] txt_path Path(out_dir) / (Path(json_path).stem .txt) lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue # 用所有标注点的外接矩形而不是假设只有2个点 pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) # YOLO格式类别 中心x 中心y 宽 高全部归一化到0~1 cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{class_names.index(label)} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) Path(txt_path).write_text(\n.join(lines), encodingutf-8) # 用法示例假设当前目录下是LabelMe导出的JSON for f in Path(labelme_json).glob(*.json): labelme2yolo(f, labels, [plate])这个脚本里有四个边界坑值得专门说。第一LabelMe 里画车牌时不要随手画成四点四边形然后把外接矩形交给 YOLO。对于倾斜但角度不大的车牌外接矩形可以接受但一旦视角偏得厉害外接矩形会包进大量车身边缘影响检测精度。建议标注时保留四个角点信息另存一份points.json用于车牌识别阶段的透视矫正。第二类别顺序一旦训练时改了之前生成的 txt 全部作废。很多项目翻车就是因为class_names是[blue, plate]后面又改成[plate]导致所有训练样本的类别标签错位。第三空标注图片不要删除。现场图片里没有车牌的情况很常见这类图没有 txt 文件放进images/train目录即可YOLOv8 会当负样本处理。如果所有训练图都有车牌模型很容易把背景里的其他矩形区域误判成车牌。第四转换后一定要做一次反向校验。最简单的办法是随机挑 20 张图把 txt 读出来画框看框是否贴合车牌。这一步能省下后面大量排查时间。3.3 训练自己的车牌检测模型train命令、YAML与关键参数数据集目录建议按 Ultralytics 默认风格组织lpr_dataset/ images/ train/ val/ labels/ train/ val/对应的 YAML 文件保持简洁# plate.yaml path: /home/user/lpr_dataset train: images/train val: images/val names: 0: plate训练命令如下# 基于官方预训练权重继续训练收敛速度比从零开始快很多 yolo detect train dataplate.yaml modelyolov8n.pt \ epochs80 imgsz640 batch8 device0 patience15 \ projectruns/lpr nameplate_v8nepochs80对车牌检测足够不需要一上来就训 300 轮。patience15表示连续 15 轮验证集没有提升就提前停止避免无效等待。batch8对应 GTX 1660 Ti 6GB 显存如果你的卡显存更大可以提高到 16CPU 训练时把device0改成devicecpu同时把epochs降到 20只用来验证流程不要追求效果。训练结束后很多人想画损失函数曲线图不需要去翻黑匣子。Ultralytics 会把每次迭代的指标写到runs/lpr/plate_v8n/results.csv直接用 pandas 读出来画图即可import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/lpr/plate_v8n/results.csv) # csv列名首尾可能带有空格先清理 df.columns [c.strip() for c in df.columns] plt.plot(df[epoch], df[train/box_loss], labeltrain box loss) plt.plot(df[epoch], df[val/box_loss], labelval box loss) plt.legend() plt.savefig(loss_curve.png, dpi150)看曲线时重点不是训练集 loss 降得多快而是验证集 loss 是否同步下降、收敛后有没有明显反弹。如果训练 loss 一直降、验证 loss 不动先检查数据划分是否泄漏再考虑加正则或减小模型尺寸。4. 车牌识别算法实现把检测框变成车牌号的三步后处理4.1 方案一用YOLOv8字符检测加排序还原车牌训练第二个 YOLOv8 模型时数据集不是车牌框而是每个字符一个框类别直接用字符本身。比如蓝牌常见类别是省份汉字加字母数字训练时把“京”“A”“B”“0”各自作为独立类别。不要统一标成char再额外接分类网络那样等于把一个模型拆成两个训练和推理都更麻烦。推理流程分四步先用车牌检测模型得到best.pt的检测框裁出车牌区域再把裁剪图送入字符检测模型拿到所有字符框后按位置排序最后把类别 ID 映射成字符拼成车牌号。from ultralytics import YOLO import numpy as np # 类别映射必须和训练字符检测模型时的YAML完全一致 idx2char {0: 京, 1: 津, 2: A, 3: B, 4: 0, 5: 1} plate_model YOLO(plate.pt) char_model YOLO(char.pt) def recognize_plate(frame, box): x1, y1, x2, y2 [int(v) for v in box] crop frame[y1:y2, x1:x2] res char_model.predict(crop, imgsz320, conf0.3, verboseFalse)[0] if res.boxes is None or len(res.boxes) 0: return xyxy res.boxes.xyxy.cpu().numpy() cls res.boxes.cls.cpu().numpy().astype(int) # 用字符中心点做排序 cx (xyxy[:, 0] xyxy[:, 2]) / 2 cy (xyxy[:, 1] xyxy[:, 3]) / 2 # 双层车牌先按行粗分再按列排序 sort_idx np.lexsort((cx, np.round(cy / 20))) return .join(idx2char[cls[i]] for i in sort_idx)np.lexsort((cx, np.round(cy / 20)))是关键。普通蓝牌一行字符直接按cx排序就行双层绿牌或大型车牌照要先把字符按cy分成两行再在行内按cx排序。20这个像素值根据裁剪图高度调整裁剪图越高分行的阈值也应越大。字符检测模型的置信度阈值不要固定得太高。字符尺寸小特征少推理时大量正确字符可能只有 0.3 左右的置信度。先设 0.3 看结果再根据误检情况调整。4.2 方案二整牌OCR识别省掉字符坐标排序如果项目周期紧不想标注字符级数据可以使用整牌 OCR 方案。车牌检测仍然用 YOLOv8识别部分换成 PaddleOCR 或 CRNNCTC 这类通用文字识别模型。这样能很快看到效果。from paddleocr import PaddleOCR ocr PaddleOCR(use_angle_clsTrue, langch) def recognize_plate_ocr(crop): result ocr.ocr(crop, clsTrue) if not result or not result[0]: return # 车牌号通常是识别结果里面积最大的第一个文本 text result[0][0][1][0] return text.replace( , )这个方案的优点是省掉字符排序逻辑缺点也很明显PaddleOCR 训练数据面向通用场景对车牌字体的针对性不够0和O、1和I这种相似对容易混。把它作为快速原型可以生产环境最好用一批现场车牌图微调识别模型或者切换到字符检测方案。4.3 脏数据清洗汉字省份、字母与数字的相似对处理车牌识别结果里最让人头疼的不是模型太大而是几个相似字符反复认错。汉字里“京”和“津”轮廓接近字母里O和数字0几乎一样还有蓝色车牌边缘的铆钉和白色边框会被当成字符框。处理办法分两层。第一层是输入侧尽量做透视矫正和边缘裁剪。如果标注阶段保留了车牌四角点识别前先透视变换成固定尺寸能明显减少倾斜带来的字符粘连。import cv2 import numpy as np def four_point_warp(img, pts): # pts 是车牌四个角点顺序为左上、右上、右下、左下 src np.array(pts, dtypenp.float32) dst np.array([[0, 0], [440, 0], [440, 140], [0, 140]], dtypenp.float32) M cv2.getPerspectiveTransform(src, dst) return cv2.warpPerspective(img, M, (440, 140))第二层是输出侧建立字符白名单。车牌第一位必须是汉字第二位必须是字母后面是字母和数字混合。后处理里把不符合规则的识别结果直接丢弃比硬猜更有价值。比如一位有效数字的置信度只有 0.4但白名单里不允许出现O那就强制输出0这类规则在真实场景里非常管用。5. 车牌识别项目避坑实录现象、原因与处理办法5.1 模型把进风口或车灯当车牌现象训练时 mAP 在 0.9 以上现场图片却把黑色进风口、车灯轮廓或远处广告牌里的“车牌”框出来。原因负样本太少。很多车牌检测数据集只在有车的图上标注车牌没有专门收集“很像车牌但不是车牌”的背景图。解决从现场视频里抽帧挑 50 到 100 张没有车牌的背景图放进images/train和images/val不给 txt 文件。同时把容易误检的框保存下来作为难例负样本加入下一轮训练。5.2 检测框准了OCR却多一位或少一位现象车牌框很标准但识别结果总是第一二位多一个字符或者最后一位丢失。原因字符检测模型把牌照边框、铆钉或汉字旁边的小色块也当成了字符框另一个常见原因是字符框顺序排序写错导致字符串错位。解决先用可视化把每个字符框画在裁剪图上看是不是边框污染。如果是在裁剪时去掉上下各 5% 的边缘像素如果排序错把字符框的中心点打印出来核对lexsort的键顺序。5.3 CPU推理只有不到1帧/秒视频卡到没法用现象用官方权重在 Ubuntu 20.04 CPU 环境推视频每帧耗时超过 1 秒完全没有实时性。原因imgsz640对 CPU 来说负载太大而且很多人误以为 YOLOv8 必须用 640 输入。解决先把imgsz降到 416 或 320。车牌在视频里通常已经占了几十像素320 输入对检测影响没有想象中大。再检查predict里的device是否真的设成了cpu避免它跑去调用不存在的 CUDA。CPU 部署还想更快就把模型导出成 OpenVINO 格式推理速度能比原始 PyTorch 快一倍以上。5.4 TensorRT或RK3588部署后精度下降现象PC 上 PyTorch 推理效果正常导出成 TensorRT 或部署到 RK3588 后检测框整体偏移置信度普遍下降。原因模型导出时预处理没统一。YOLOv8 训练时默认用 letterbox 填充灰边部署端如果直接用resize改变宽高比检测框坐标自然就偏了。另外 INT8 量化用的校准图片太少也会让精度掉得厉害。解决部署端前处理严格复刻训练时的 letterbox 逻辑记录填充比例推理输出坐标后反算回原图坐标。INT8 量化至少准备 200 张覆盖不同光线和车色的代表图做校准不要用训练集里明显重复的帧。5.5 训练Loss下降但验证集mAP不涨现象训练日志里 loss 一直掉验证集 mAP 却在十几轮后停在 0.8 附近换模型结构也没用。原因最常见的是数据划分泄漏。同一辆车的连续帧同时出现在 train 和 val 里模型记住的是车而不是车牌其次是类别 ID 不一致训练时0是蓝牌验证时0是车牌导致评估完全错乱。解决按车辆而不是按帧划分数据集确认同一辆车只出现在 train 或 val 其中一边。训练前做一次标签自检遍历 labels 目录检查每个类 ID 是否都在 YAML 的names范围内。6. 导出与验证让YOLOv8车牌识别从训练机走到生产6.1 用ONNX导出检测模型并用ONNX Runtime推理训练完plate.pt后把它导出成 ONNX 是在道闸设备、RK3588 这类平台部署的关键一步。Ultralytics 已经把导出封装得很简单from ultralytics import YOLO model YOLO(runs/lpr/plate_v8n/weights/best.pt) model.export(formatonnx, imgsz640, dynamicFalse, opset12)dynamicFalse表示输入尺寸固定为 640x640大多数视频检测场景不需要动态尺寸固定尺寸还能减少 ONNX Runtime 的推理波动。如果需要在 Java 服务里调用车牌识别我一般把检测模型导出成 ONNX再用 onnxruntime-java 加载识别部分仍然用 Python 服务或单独导出字符检测模型。导出后先不要急着部署用同一张测试图分别跑 PyTorch 和 ONNX 输出对比检测框坐标误差。误差在 0.5 像素以内才算正常。很多部署端翻车都出在 letterbox 填充没有对齐这一步能提前拦住大部分问题。6.2 生产环境的验收清单最后留一份简短的验收清单项目值不值得上线跑完这几点心里就有底检查项目标不合理时的处理近景车牌检测10 米内漏检率低于 5%补训练夜间和逆光样本远景车牌检测能框住但不强求识别降低置信度阈值或分两路模型单字符识别字母数字错误率低于 2%检查相似对替换规则CPU 推理耗时单帧低于 200 毫秒换模型为 YOLOv8n、降 imgsz、导出 OpenVINO部署端精度与 PC 端相差不超过 2%统一预处理、增加量化校准集如果你现在正要在自己的项目里接车牌识别我的习惯是先不急着写界面而是把检测、裁剪、识别三个动作拆成独立函数在控制台上用 20 张现场图片跑一遍记录每个环节的耗时和错误样本再谈后续优化。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

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

较早相关资讯

最新相关资讯

魔百和CM311-5救砖指南:GK6323芯片卡刷与安卓9深度适配 2026/9/25 3:22:43

魔百和CM311-5救砖指南:GK6323芯片卡刷与安卓9深度适配

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

阅读更多 →
Cadence Cassandra Schema 管理指南:使用 cadence-cassandra-tool 完成建库、版本化迁移与生产部署 2026/9/25 3:22:43

Cadence Cassandra Schema 管理指南:使用 cadence-cassandra-tool 完成建库、版本化迁移与生产部署

后端任务调度工作流自动化微服务 【免费下载链接】cadence Cadence is a distributed, scalable, durable, and highly available orchestration engine to execute asynchronous long-running business logic in a scalable and resilient way. 项目地址: https://…

阅读更多 →
SAP开发IDE怎么选?从传输治理到云原生工具链的边界 2026/9/25 3:22:43

SAP开发IDE怎么选?从传输治理到云原生工具链的边界

每次聊到 SAP 开发环境,都会看到同一个争论:以后到底是只用一款官方 IDE,还是任意 IDE?一方是围着 Eclipse、ABAP Development Tools(ADT)和 SAP GUI 过了十几年的老顾问,手里攥着 SE80 和传输请…

阅读更多 →
Changesets实战:Monorepo版本管理与自动发布方案 2026/9/25 3:22:43

Changesets实战:Monorepo版本管理与自动发布方案

在维护开源包和工具库的这些年里,我几乎每天都在跟"版本管理"这四个字较劲。手动改 package.json 里的版本号、写完代码再回头补 changelog、发布前纠结到底是 patch 还是 minor ——这套流程在只有一个仓库、两三个包的时候还能勉强应付&#xff0…

阅读更多 →
深入 @urql/solid-start:基于 SolidStart 原生原语的 SSR GraphQL 集成方案 2026/9/25 3:22:37

深入 @urql/solid-start:基于 SolidStart 原生原语的 SSR GraphQL 集成方案

前端 【免费下载链接】urql The highly customizable and versatile GraphQL client with which you add on features like normalized caching as you grow. 项目地址: https://gitcode.com/gh_mirrors/ur/urql 点击查看 免费下载 urql/solid-start 是 urql 生态中…

阅读更多 →
SQL Server 扩展安全更新(ESU)注册信息采集脚本实战指南:T-SQL 单实例查询与 PowerShell 批量发现 2026/9/25 3:22:36

SQL Server 扩展安全更新(ESU)注册信息采集脚本实战指南:T-SQL 单实例查询与 PowerShell 批量发现

示例工程数据库教程后端 【免费下载链接】sql-server-samples Azure Data SQL Samples - Official Microsoft GitHub Repository containing code samples for SQL Server, Azure SQL, Azure Synapse, and Azure SQL Edge 项目地址: https://gitcode.com/gh_mirrors…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

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

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