ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLOv8+PaddleOCR车牌识别本地部署全攻略

YOLOv8+PaddleOCR车牌识别本地部署全攻略 简介目标检测与光学字符识别OCR是计算机视觉中的两大基础技术分别解决“物体在哪里”和“文字是什么”的问题。车牌识别作为智慧交通的典型场景天然需要两者协同先通过目标检测模型定位车牌区域再利用OCR引擎进行字符识别。YOLOv8作为当前高效的目标检测网络具备精度与速度的平衡PaddleOCR则提供强大的中文文本识别能力两者结合能构建一套完整的本地化车牌识别pipeline。这种两阶段方案具有模块解耦、易于优化、部署灵活等优势在停车场管理、门禁系统等场景中广泛应用。本文基于实际项目从环境配置、数据准备、模型训练到代码实现完整梳理了YOLOv8与PaddleOCR车牌识别系统的搭建过程并总结了常见问题与优化技巧为开发者提供可落地的参考路径。 我最近刚做完一个车牌识别的本地部署项目核心方案就是标题里那套组合YOLOv8负责把车牌从整张图片里抠出来PaddleOCR负责把抠出来的车牌图片转成字符串。这套流程听起来简单但真要从零开始把环境配好、数据备好、模型训起来、最后再落地成可用的识别接口中间能踩的坑比我预想的多一倍。这篇文章就按我实际动手的顺序把整个设计思路、配置步骤、训练细节和排查经验完整梳理一遍给正在做类似车牌识别、OCR识别或者其他两阶段目标识别项目的朋友一个可以直接参考的路线图。1. 项目整体设计与技术选型思路1.1 为什么是检测识别两阶段方案车牌识别这件事本质上包含两个不同难度的问题第一车牌在哪第二车牌的字符是什么。很多新手一上来就想用一个模型直接搞定端到端输出粤B12345这种方案确实存在比如一些基于CRNN或者LPRNet的模型但实际用起来有几个痛点一是端到端模型对车牌区域之外的干扰特别敏感车灯、保险杠、甚至是车身贴纸都可能被误识别成字符二是端到端模型的数据标注成本高需要把整张图的标注信息和字符序列绑在一起自己做数据时非常痛苦。两阶段方案就很直白先用YOLOv8这类的目标检测模型定位车牌位置输出一个边界框再把这个边界框裁剪出来交给OCR模型做字符识别。这样做的好处是每个模型只干一件事检测模型不关心字符内容OCR模型也不用管车牌的尺寸和位置变化。而且两个模型可以独立优化、单独替换比如你发现夜间识别率低只需要针对检测模型补充夜间数据如果发现汉字识别老出错单独调OCR的字典就行互不干扰。我实际做完以后觉得这种解耦设计在维护和迭代上比端到端方案舒服太多。1.2 检测端选YOLOv8而不是其他模型的原因目标检测模型可选的范围其实很宽老牌的Faster R-CNN、SSD后来的YOLOv5、YOLOv7再到这次的YOLOv8我最后选YOLOv8的原因归纳下来有三点。第一点是训练和部署的生态成熟。Ultralytics把YOLOv8的训练、验证、导出封装得非常完整命令行几行就能开始训练导出ONNX、TensorRT、OpenVINO都是现成指令这对于后面要往嵌入式设备或者Windows本地部署的场景来说太重要了。第二点是检测精度和速度的平衡。YOLOv8在COCO上的mAP表现放在同体量模型里是第一梯队而车牌这种目标相对规整、类别单一就是车牌一类的场景用n或者s这种小模型就很容易跑出很高的精度实测下来推理速度在普通GTX 1660 Ti上也能做到实时以上。第三点是改进空间大。YOLOv8的C2f结构、Anchor-Free设计、Decoupled Head都是近年比较前沿的做法如果觉得精度不够后续想加注意力机制比如SE、ECA、CBAM或者换更轻量的Backbone社区里都有大量现成改进方案可以参考。这一点对于想拿这个项目练手或者写论文的朋友很有价值。1.3 识别端选PaddleOCR的决策依据字符识别这块PaddleOCR可能不是唯一选择但绝对是最省心的选择之一。PaddleOCR目前已经迭代到PP-OCRv4这个版本对中文的支持非常完善。车牌识别最关键的就是中文汉字省份简称和字母数字混排PP-OCR的中文识别模型本身就是基于海量中文语料训练的对京沪粤鲁这些字的识别准确率相当高。而且PaddleOCR提供了一套完整的pipeline包括文本检测DB系列、方向分类、文本识别CRNN系列虽然我们车牌场景不一定用它的文本检测但可以直接调用它的识别模型。另一个关键点是PaddleOCR的部署方案很灵活。它原生支持Paddle Inference也支持导出ONNX还能通过PaddleOCR的whl包一行代码调用。我在网上搜过不少OCR库比如Tesseract它对中文车牌的支持说实话比较一般识别津和冿这种形近字经常翻车而商用云OCR虽然准但车牌数据涉及隐私很多场景不允许把图片传到云端所以本地化部署就成了刚需。PaddleOCR的本地部署体验在国产OCR引擎里目前确实是最好的。1.4 这套方案的适用边界与性能预期在动手之前我要先给这套方案画个边界。用YOLOv8PaddleOCR最适合的是那种单张图片里有一块或多块车牌且车牌整体清晰可辨的场景比如停车场出入口、小区门禁、高速卡口的抓拍图片。如果你的场景是那种远距离、低分辨率、车牌在画面里只有几十个像素的监控视频那这套方案会非常吃力因为检测模型都很难稳定输出目标框更别说OCR了。性能预期方面我用GTX 1660 Ti 6G显卡做过实测YOLOv8n模型推理一张1280x720的图片大约需要20到30毫秒PaddleOCR识别一张裁剪后的车牌图片大约需要30到50毫秒加起来单张图片的端到端耗时在60毫秒左右帧率大概15到16 FPS。如果不跑可视化界面只做批量离线识别吞吐量会更高。这个速度对大部分非实时的业务场景来说完全够用。2. 环境配置与模型部署准备2.1 Python环境与CUDA版本规划车牌识别项目涉及PyTorch和PaddlePaddle两套深度学习框架在配置环境的时候容易出问题的地方就在于这两套框架对CUDA、Python版本的依赖不完全一致。我在第一次配置时直接用Python 3.12结果PaddleOCR的依赖安装报错后来查了一下官方适配最稳的版本还是Python 3.10。所以这里建议第一步就装Python 3.10能少折腾很多事情。CUDA方面我最终稳定使用的版本是CUDA 11.8配合cuDNN 8.6。PyTorch这边用pip安装torch2.0.1cu118这个版本即可。PaddlePaddle这边安装paddlepaddle-gpu2.5.2对应CUDA 11.8的版本然后安装paddleocr2.7.0。需要特别提醒的是PaddleOCR的2.7版本和3.x版本在API调用上有差异网上的教程很多是旧版的写法后面看代码的时候要留意你装的到底是哪个主版本。2.2 PaddleOCR安装与本地部署细节PaddleOCR的安装本身不复杂核心是三步。第一步安装PaddlePaddle框架第二步安装PaddleOCR库第三步在首次运行时它会自动下载训练好的模型权重文件如果下载失败就需要手动配置模型路径。我当时遇到的第一个坑就是模型下载。PaddleOCR在第一次初始化OCR实例时会尝试从B站或者GitHub下载检测、分类、识别三个模型网络条件不好的时候经常卡在某个模型上下载不完。解决办法是手动到官方的模型库页面把三个模型文件下载下来放到本地目录然后在代码里通过det_model_dir、cls_model_dir、rec_model_dir参数指定本地路径。这样不仅稳定后面离线部署到其他机器上也直接用这个本地路径不用每次联网下载。这里放一段我实际调通的初始化代码from paddleocr import PaddleOCR ocr PaddleOCR( det_model_dir./models/ch_PP-OCRv4_det_infer, rec_model_dir./models/ch_PP-OCRv4_rec_infer, cls_model_dir./models/ch_ppocr_mobile_v2.0_cls_infer, use_angle_clsTrue, langch, show_logFalse, use_gpuTrue, ) result ocr.ocr(./test_plate.jpg, clsTrue) print(result)注意use_angle_clsTrue这个参数它的作用是判断图片里的文字是否需要旋转车牌虽然是水平方向为主但在一些特殊角度的抓拍里开启方向分类能避免识别结果倒着出。2.3 用conda管理两套框架依赖因为要同时用PyTorch和PaddlePaddle非常建议用conda单独建一个虚拟环境不要直接在base环境里装。我习惯的建环境命令是这样conda create -n plate_recognition python3.10 conda activate plate_recognition pip install torch2.0.1cu118 torchvision0.15.1cu118 --index-url https://download.pytorch.org/whl/cu118 python -m pip install paddlepaddle-gpu2.5.2 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/ pip install paddleocr2.7.0 pip install ultralytics8.1.0装完以后可以分别验证一下两套框架是否正常调用GPU。PyTorch用import torch; print(torch.cuda.is_available())PaddlePaddle用import paddle; print(paddle.is_compiled_with_cuda())。这两个都返回True说明GPU环境就绪。2.4 验证环境时的版本兼容性提醒我在配置过程中查过不少相关的版本兼容问题比如有人问PaddleOCR能不能用在Python 3.14上说实话这个不要想PaddlePaddle官方对Python版本的支持向来滞后目前3.14连PyTorch官方都还在适配中老老实实用3.10最稳妥。还有人问PyTorch 2.13支不支持YOLOv8目前PyTorch官方稳定版还在2.xYOLOv8的Ultralytics框架适配的是主流的2.0、2.1、2.2版本没必要追新。这些版本匹配的经验是深度学习的项目稳定比新版本重要得多除非有明确的新特性需求否则就选已经有大半年以上社区验证记录的版本组合。3. 数据集准备与标注实操3.1 CCPD2020数据集快速上手车牌识别领域最有名的公开数据集就是CCPDChinese City Parking Dataset目前最新的版本是CCPD2020。这个数据集包含超过25万张图片全部来自真实停车场场景覆盖了不同角度、不同光照条件下的中国车牌。数据集的图片命名规则里就藏着标注信息比如025-95_113-154383_386473-386473_177454_154383_363402-0_0_22_27_27_33_16-37-15.jpg这样一个文件名里面包含了车牌四个角点的坐标、车牌字符内容等信息。如果是做学术验证或者比赛直接用CCPD的原始标注就可以不需要自己动手标。不过CCPD有个需要注意的地方它里面绝大多数是蓝牌小型汽车号牌绿牌新能源车和黄牌大型车的占比不算高。如果你的业务场景里有大量绿牌车那光靠CCPD训练出来的模型对绿牌的召回率会比较差这时候需要自己补充一些绿牌数据。我自己的做法是先从CCPD2020里筛选出一万张图片做训练集再额外去停车场拍了两三百张绿牌车的照片补充进去最后测试效果好了不少。3.2 自己采集数据时的标注操作细节如果目标场景和公开数据集差异较大比如是那种斜向45度安装的监控摄像头、或者离车牌很近的大角度画面我建议还是采集一些现场数据。采集的时候注意覆盖不同时段白天强光、傍晚暗光、夜间车灯直射这三个光照条件是车牌识别的三大难点数据里如果缺少某一样模型在那个时段的表现会肉眼可见地变差。标注工具我用的是LabelImg虽然界面朴素了点但胜在轻量、支持YOLO格式导出。标注时候有两个细节值得单独说一下第一车牌框要尽量贴合车牌边缘不要留太多背景尤其是不要把车标、进气格栅框进来这些背景信息对检测模型来说是纯噪声第二如果车牌有倾斜旋转框更贴合目标但YOLO系列原生只支持水平框所以在标注时要框住整个倾斜车牌的最外接水平矩形宁可稍微多框一点也不要切掉车牌边角。标注完成后LabelImg会生成对应的txt文件每行内容格式是class_id x_center y_center width height注意这里的四个数值都是相对图片宽高的归一化坐标。如果是从CCPD这种带角点标注的数据集转换过来就需要按角点坐标计算外接矩形的中心点和宽高我自己写了个转换脚本核心逻辑大概是先取四个角点的最小x、最大x、最小y、最大y然后换算成中心点坐标和宽高再除以图片宽高做归一化。3.3 数据集划分与目录结构组织数据准备好之后要按YOLOv8的习惯把数据组织成规范的目录结构。Ultralytics框架对数据集的目录结构有约定最简单的形式是这样的datasets/ └── plate/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/images和labels目录下的文件名需要一一对应除了扩展名不同train和val的比例我一般按8比2或者9比1来分。划分数据集的时候注意一点同一个停车场的图片不要既出现在train又出现在val里否则会造成数据泄漏验证结果虚高。理想情况是按拍摄场景划分比如A停车场的图片全放trainB停车场的全放val这样验证集才能真实反映模型的泛化能力。3.4 训练数据增强策略数据量不够的时候数据增强是提升模型精度的最有效手段。YOLOv8内置了Mosaic、随机仿射变换、HSV色域变换等增强手段在训练时默认开启。针对车牌场景我觉得效果最明显的是两项一是随机调整亮度对比度模拟白天黑夜不同光照二是随机旋转模拟倾斜拍摄角度。Ultralytics的配置里对应的是hsv_h、hsv_s、hsv_v和degrees这些参数。不过增强也不是越猛越好。转得太厉害会让车牌看起来不真实比如旋转超过30度之后车牌在画面里已经严重变形了反而干扰模型学习正常形态的特征。我实测下来degrees10是比较合适的范围既能模拟实际场景的轻微倾斜又不至于把目标扭曲得失去意义。另外因为车牌区域在整张图中的占比通常比较小如果训练时原图直接缩放车牌可能缩到很小的像素范围这时候可以考虑用yolov8n.pt的默认输入尺寸640或者改成768让目标在输入图里占更多像素。4. YOLOv8车牌检测模型训练全流程4.1 模型配置与训练参数选择YOLOv8系列按参数量从小到大有n、s、m、l、x五个版本。车牌检测这个任务的特点是目标类别单一、目标特征明显蓝底白字或绿底黑字颜色和纹理都与周围环境差异大所以不需要用太大的模型。我实际对比过n和s两个版本n体量的模型训练速度快大约一倍精度上在测试集上的mAP50差距不到1个百分点但n模型的权重只有6M左右后面部署到嵌入式设备上优势明显。最终我选的方案就是yolov8n作为baseline。训练脚本用Ultralytics的Python API核心配置参数如下from ultralytics import YOLO model YOLO(yolov8n.pt) model.train( data./plate.yaml, epochs120, imgsz640, batch16, lr00.01, lrf0.01, optimizerSGD, patience20, device0, workers4, augmentTrue, project./runs/plate, nameyolov8n_plate, )几个关键参数的选择逻辑我解释一下。batch16是GTX 1660 Ti 6G显存能稳定撑住的数值如果显存不够就降到8不要硬扛OOM会中断训练。optimizerSGD是我个人的偏好虽然Adam收敛更快但SGD最终收敛的泛化性能通常更好特别是在数据量不大的情况下SGD加学习率衰减能有效避免过拟合。patience20表示连续20个epoch验证集指标没有提升就提前停止训练这个设置能在模型收敛后自动节省时间。4.2 训练过程监控与损失曲线分析训练开始后要养成看日志的习惯。YOLOv8的日志会输出每个epoch的box_loss、cls_loss、dfl_loss以及precision、recall、mAP50、mAP50-95这些指标。我一般关注几个关键信号第一box_loss和cls_loss是否在稳定下降如果loss值来回震荡甚至上升优先怀疑学习率设置或者数据标注有问题第二训练集loss和验证集loss的差距如果训练loss持续下降但验证loss不怎么动说明过拟合了这时候可以加数据增强或者减小模型容量第三precision和recall的平衡对于车牌检测漏检的代价通常比误检大所以我会更在意recall的数值。Ultralytics默认会保存训练过程中的results.png这个图包含损失曲线和指标曲线的变化趋势。如果想自己绘制更精细的损失函数曲线图可以读取训练日志里的原始数据我习惯在训练结束后用matplotlib重新画一版把train loss和val loss画在同一张图上这样能更直观地观察拟合情况。YOLOv8的日志文件默认保存在runs/plate/yolov8n_plate/目录下有一个results.csv文件所有指标都按epoch记录在里面直接pandas读取然后画图就行。4.3 模型评估与性能数据解读训练结束后Ultralytics会在验证集上自动跑一轮评估并输出最终的mAP指标。我那次训练的最终成绩大概是mAP5098.7%mAP50-9585.2%这个结果对于单类别的车牌检测来说已经很不错了。但我提醒自己一点mAP是整体数据分布上的平均表现要真正判断模型能不能用还是要去看badcase。所以我训练完以后专门做了一步用模型跑了一遍测试集里那些夜间、逆光、倾斜角大的困难样本把检测结果可视化保存出来一张张翻看。这一步能直观地看出问题——到底是大车灯把车牌照得白花花导致漏检还是车牌在画面里太小导致框不稳或者是把车尾的装饰条误检成车牌。我建议所有做检测项目的朋友都不要跳过这个环节指标是给别人看的badcase分析才是自己优化的依据。4.4 权重导出与模型压缩训练得到的best.pt是PyTorch格式的权重在推理和部署之前一般要导出成更高效的格式。如果是在本地用Python推理直接加载.pt文件就行如果要部署到嵌入式设备或者用C调用可以导出ONNX或者TensorRT格式。Ultralytics的导出命令非常简洁from ultralytics import YOLO model YOLO(./runs/plate/yolov8n_plate/weights/best.pt) model.export(formatonnx, opset12, simplifyTrue) model.export(formatengine, halfTrue) # 仅在TensorRT环境可用导出ONNX时simplifyTrue会通过onnx-simplifier做一些图优化减小模型体积。导出engine格式时halfTrue表示使用FP16精度在支持TensorRT的显卡上能把推理速度再提一档但精度会有极微小的损失可忽略不计。我自己的经验是先导出ONNX用onnxruntime验证一下和PyTorch的推理结果是否一致数值误差在很小范围内就行再决定要不要进一步转TensorRT。5. 车牌识别完整流程与代码实现5.1 检测到识别的全流程编排模型训练好之后就到了把它们串起来的时候。整个车牌识别流程可以拆成五个环节图片输入、车牌检测、区域裁剪、方向矫正、字符识别、结果输出。完整的处理逻辑我用一个Python类来封装这样不管是做命令行工具还是接Web API都很方便。流程中有一个容易被忽视的步骤裁剪后的车牌图片在送入OCR之前最好先做一下预处理。车牌检测模型输出的边界框是原始图片上的一个矩形直接裁剪会带上不少背景但这些背景通常很干净影响不大。真正影响识别效果的是图片分辨率和颜色空间。我测试过PP-OCR的识别模型对输入图片有一个比较合适的尺寸范围车牌裁剪图太小时比如高度小于32像素识别率会明显下降。所以我在裁剪后会做一个放大处理保证车牌区域的高度至少在48像素以上这样OCR的识别率会高很多。另外如果原图是BGR格式OpenCV读图默认就是BGR而PaddleOCR内部使用的是RGB格式这个不做转换的话可能会导致识别结果偏差但实际测试发现PaddleOCR内部已做了处理所以OpenCV读图后直接调用问题不大。5.2 核心推理代码与参数调优下面是我项目中实际使用的核心推理代码为了方便理解我把注释写在了关键位置import cv2 import numpy as np from ultralytics import YOLO from paddleocr import PaddleOCR class PlateRecognizer: def __init__(self, det_weightsbest.pt, use_gpuTrue): self.detector YOLO(det_weights) self.ocr PaddleOCR( use_angle_clsTrue, langch, show_logFalse, use_gpuuse_gpu, ) def _preprocess_plate(self, plate_img): # 如果车牌区域太小先放大到合适尺寸 h, w plate_img.shape[:2] if h 48: scale 48.0 / h new_w int(w * scale) plate_img cv2.resize(plate_img, (new_w, 48)) return plate_img def recognize(self, image_path): img cv2.imread(image_path) results self.detector(img, conf0.5, iou0.5, verboseFalse) plates [] for r in results: boxes r.boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() x1, y1, x2, y2 int(x1), int(y1), int(x2), int(y2) # 略做外扩保证车牌完整 margin_x int((x2 - x1) * 0.02) margin_y int((y2 - y1) * 0.02) x1 max(0, x1 - margin_x) y1 max(0, y1 - margin_y) x2 min(img.shape[1], x2 margin_x) y2 min(img.shape[0], y2 margin_y) plate_img img[y1:y2, x1:x2] plate_img self._preprocess_plate(plate_img) result self.ocr.ocr(plate_img, clsTrue) if result and result[0]: text result[0][0][1][0] confidence result[0][0][1][1] plates.append({ bbox: [x1, y1, x2, y2], plate: text.strip().replace( , ), confidence: round(confidence, 4), }) return plates if __name__ __main__: recognizer PlateRecognizer(det_weights./best.pt) result recognizer.recognize(./test_images/car_001.jpg) for item in result: print(item)代码里有一个细节我在实际调参时发现的conf0.5这个检测置信度阈值很关键。设高了误检变少但夜间那种模糊的车牌容易被漏掉设低了召回率上去了但会把一些蓝色车身、圆形车标误判成车牌。经过在测试集上的多次实验0.5是一个兼顾精度和召回的位置。如果你只关心识别结果、不介意多跑几次检测可以降到0.3。5.3 车牌字符后处理与纠错规则OCR吐出来的结果不能直接当最终答案原因很简单PaddleOCR是一个通用中文OCR模型它对车牌这种固定格式的字符序列并不了解所以有时会识别出不可能存在的排列比如粤B。12345多了一个句号或者京A 88888里多了一个空格。这些都需要在后处理阶段清洗和纠正。我做的后处理逻辑有三步。第一步是过滤掉非车牌字符中国车牌的标准格式是省份简称一个汉字发牌机关代号一个字母序号5位字母数字组合其中新能源车牌是6位所以凡是结果里出现不在字典里的符号比如句号、逗号、空格全部剔除。第二步是长度校验普通蓝牌去掉汉字和字母后应该是5位新能源绿牌是6位如果识别结果长度不符合要么判为识别失败要么结合置信度决定是否重试。第三步是形近字纠正OCR在这种场景下最容易把京识别成亰或者京多一点0和O之间也经常混淆。我的做法是维护一个映射表把常见的错字映射回正确字符比如{亰: 京, 0: O}但要注意0和O的替换不能盲目做得结合位置判断——车牌序号里字母O是不使用的所以如果第2位发牌机关代号出现0大概率是O而序号内部的O则更可能是0。类似这种基于车牌规则的纠错能有效提升最终识别准确率。5.4 Windows本地部署与界面封装项目做完模型和推理代码后如果只是自己在命令行里跑那还谈不上落地。为了让不太懂技术的人也能用我用Streamlit写了一个简单的Web界面上传一张图片后台调用识别接口前端返回标注了车牌框和识别结果的图片。Streamlit这个框架的优势是代码量少不需要写HTML和JavaScript一个Python脚本就能跑起来。如果要把方案集成到现有的业务系统里更推荐的方式是把识别逻辑封装成一个HTTP服务。我用FastAPI写过一版核心代码非常简单只需要定义一个接收图片的接口内部调用上面的PlateRecognizer类即可。但要注意一点PaddleOCR的初始化耗时较长要加载三个模型所以不要每次请求都重新初始化OCR实例应该把识别器对象定义成全局单例在服务启动时初始化一次。这一点我以前吃过亏第一次用Flask封装时每次请求都重新new一个PaddleOCR结果一个请求要等好几秒后来改成初始化一次之后单次请求耗时立刻降到了100毫秒以内。6. 常见问题与排查技巧实录6.1 训练阶段常见问题速查表在训练和部署过程中我整理了下面这份速查表每个问题都是实际遇到或验证过的对应的解决方案具备可操作性问题现象产生原因排查与解决方案训练时OOM显存不足batch_size过大或输入分辨率过高将batch_size从16降到8或4关闭训练时的Mosaic增强换yolov8n更小的模型loss不下降或震荡学习率过大、标注框质量差将lr0从0.01降到0.001检查标注框是否出现明显偏移确认数据集中没有空标签图片验证集mAP很高但实际效果差数据泄漏或训练/测试分布不一致按场景划分数据集检查train和val是否包含同一场景的重复图片补充现场数据夜间漏检严重训练数据中夜间样本太少在数据增强中加大亮度对比度扰动范围单独采集夜间图片扩充训练集PaddleOCR识别汉字总出错OCR模型默认字典对生僻汉字支持不足使用车牌专用字典做识别后映射对识别结果做规则校验和纠错OCR识别速度慢GPU未启用或模型过大确认use_gpuTrue换用mobile版本的识别模型导出成ONNX用onnxruntime推理6.2 部署到嵌入式设备的思路与坑项目做完之后我一直想着把它往RK3588这类嵌入式设备上迁移因为车牌识别的很多现场场景是边缘端部署的不可能在每台设备上都配一张独立显卡。在这方面我有几个初步的经验。YOLOv8往RK3588上部署主流路线是先转ONNX再转RKNN格式。转换过程中要特别注意算子兼容性YOLOv8的某些算子比如SiLU激活函数在RKNN工具链的早期版本里支持不完善需要升级RKNN-Toolkit2到比较新的版本或者在转换时做算子的等价替换。PaddleOCR往RK3588上部署相对更麻烦一些因为Paddle的推理框架在ARM平台上的支持不如x86成熟。我调研过两种方案一是直接编译Paddle Lite二是把PaddleOCR的识别模型转成ONNX之后再转RKNN。第二种方案踩坑会更多因为PP-OCRv4识别模型里的某些算子对ONNX导出支持并不完美。如果你也要做类似部署建议先在目标板子上用onnxruntime的CPU版本跑一遍看看精度和速度能不能达到业务要求再决定是否需要继续优化到NPU上。6.3 热词里高频疑问的集中回应整理这篇文章的时候我顺手翻了翻大家搜索这类项目时的高频问题有几个很有代表性集中回应一下。有人问YOLOv8怎么画损失函数曲线图其实最简单的方法就是打开训练输出目录下自动生成的results.png里面已经包含了边界框损失、分类损失、DFL损失的曲线。如果想画得更精细直接读取results.csv用matplotlib画即可。有人问GTX 1660 Ti能不能跑YOLOv8答案是完全可以而且跑起来还比较轻松。1660 Ti有6G显存训练yolov8n模型、batch_size设16没问题推理速度上面说过能到15到20 FPS。如果你要训练yolov8m或更大的模型显存就不够了要么牺牲batch_size要么用更小的输入分辨率。还有人问YOLOv8手机安装包Ultralytics确实支持导出到NCNN和TFLite格式理论上可以部署到手机端但车牌识别这种场景在手机上更多是用拍照上传云端处理。如果你是想做一个离线手机App建议导出NCNN格式在安卓上跑苹果手机上用Core ML导出。关于PaddleOCR在Windows本地部署我之前已经详细写过安装步骤核心就是两条一是确保PaddlePaddle能正确调用GPU二是提前下载好模型文件放到本地目录避免首次运行时的联网下载问题。这两步做好了Windows上的稳定性和Linux基本没有区别。6.4 精度优化的进阶方向如果按照上面的流程走完识别率应该已经能到95%以上。如果你的业务要求更高或者遇到的场景更复杂还有几个进阶方向值得尝试。第一个方向是给YOLOv8的C2f模块融入注意力机制比如ECA或CA。ECA是一个很轻量的通道注意力模块在C2f里嵌入后参数量增加很少但在车牌这类小目标检测上有时能带来1到2个点的精度提升。这个网上有很多现成的改进实现搜索YOLOv8 ECA改进能找到大量参考代码。第二个方向是针对倾斜车牌做透视矫正。目前的方案对水平车牌的识别率非常高但遇到严重倾斜的车牌时检测框内包含大量背景OCR识别效果会下降。一个可行的优化是在检测之后、OCR之前加入一个透视矫正步骤先用车牌角点检测模型也可以用YOLOv8的pose模式定位车牌的四个角点然后再做透视变换把车牌矫正成正面视角。这个方案会让整个pipeline复杂不少但提升效果非常明显。第三个方向是目标跟踪与多帧融合。如果你处理的是视频流而不是单张图片可以引入ByteTrack这类跟踪算法对同一个车牌在连续多帧中的识别结果做投票融合能有效消除单帧识别偶尔出现的字符错误。这个思路在停车场道闸场景里非常实用因为车辆在接近道闸的过程中摄像头会拍到连续多帧车牌把多帧结果综合起来基本能达到接近100%的识别准确率。7. 项目扩展与实际落地经验分享7.1 从识别单张图到接入实时视频流我这个项目最开始只是做单张图片的识别但实际使用场景里基本都是视频流。后来我加了一个视频检测线程从摄像头读取帧每隔几帧抽取一帧送入识别模块识别结果通过队列传回主线程做展示。这里有几个细节值得注意第一视频流里目标车辆是运动的如果每隔太多帧才抽一次车辆可能已经开过了摄像头识别区域导致漏检。我的经验是10到15帧抽一帧比较合适既不会太耗算力也不会漏掉关键帧。第二检测到车牌之后不要每一帧都调OCR同一块车牌在连续几帧里的识别结果是一样的重复调用浪费算力。更聪明的做法是检测到新的车牌时启动一个计时器在2秒内只对该车牌执行一次OCR后续帧只做检测跟踪不重复识别。7.2 停车场道闸场景的落地体会我的项目最后落地在一个模拟停车场道闸的场景里。这个场景比单纯识别的门槛要高一点因为道闸的抬杆放行逻辑和识别结果绑定误识别会造成严重问题。在实际联调的时候我发现两个影响使用体验的细节第一个是车还没停稳就拍的问题车辆在减速滑行时拍到的车牌可能存在运动模糊导致识别失败。解决办法是加一个简单的去模糊判断车牌区域图片的梯度方差过低就判定为模糊提示重拍。第二个是防重识别车辆在道闸前等待时系统会不断识别到同一块车牌如果不做去重每一次识别成功都会触发抬杆指令。我的做法是用一个字典记录每块车牌的最近识别时间如果同一车牌在5秒内重复识别直接忽略。这些都是业务层的小逻辑但对用户体验的影响比模型精度还大。7.3 代码维护与模型迭代的规范化建议项目做完以后代码和模型的版本管理是我特别想强调的一点。第一次训练完模型之后我只记了一个best.pt文件名结果过了一个月想对比不同版本的效果完全分不清哪个文件是哪个参数配置训练出来的。后来我养成了一个习惯每个训练版本都单独建一个目录目录名包含训练时间、模型大小、训练集数量、mAP这几个关键信息比如20240615_yolov8n_10000imgs_mAP98.2。同时在训练脚本里固定随机种子保证同样的代码和参数可以复现出几乎一样的结果。看似无足轻重但在项目需要持续迭代时这种感觉很方便。7.4 最后再分享一个小技巧车牌识别这个项目做完之后我发现很多问题其实都出在图片质量而不是模型能力上。比如有些摄像头安装位置太高导致车牌在画面里只占很小的面积这时候用什么模型都救不回来。所以如果你准备在某个现场部署车牌识别我建议你花一天时间去现场看看摄像头安装角度尽量让车牌在画面里占到足够大的比例同时避免逆光。这比自己花半个月采集数据、调模型参数要有效得多。技术能解决的问题是有上限的选址和摄像头安装这些土办法往往才是决定项目成败的关键。我做这个项目的最大体会是YOLOv8和PaddleOCR这两个开源工具真的很成熟按文档操作基本不会出大问题真正的难点在于怎么把两个模型无缝地拼在一起怎么处理各种边界情况。希望这篇分享能帮你少走一些弯路。本文还有配套的精品资源点击获取
返回列表