最近在做一个停车场预约系统的边角料项目需要把出入口的车牌识别做出来。调研了一圈现成方案商业SDK按次计费聊下来不便宜几个开源项目又觉得太重。自己本身就在搞AI应用落地索性决定用 YOLO11 做车牌检测、RapidOCR 做车牌字符识别自己搭一套轻量级识别链路。这篇文章把完整的落地过程、关键参数和踩过的坑都整理出来给同样想自己动手做车牌识别的朋友一条可以直接参考的路线。项目本身不复杂但里面的细节——数据集怎么弄、模型怎么训、OCR怎么调、CPU占用怎么压——都值得展开讲讲。1. 项目背景先搞清楚我们到底要解决什么问题1.1 需求场景与核心痛点车牌识别的应用场景太常见了停车场出入口、小区门禁、园区访客登记、高速收费站、加油站自动结算、货车进出厂区管理。本质上做的是同一件事——从一张包含车辆的图像里把车牌区域抠出来再把车牌上的字符读出来输出成标准格式的文本字符串。真实场景里的难点比想象中多。首先是环境复杂白天强光反光、夜间低照度、雨雪天气、车牌倾斜旋转、污损遮挡、运动模糊这些都会直接拉低识别准确率。其次是车牌的尺寸问题相机架得高、视场角大画面里一辆车可能只占一小块区域车牌更只是其中几十个像素的方框属于典型的小目标检测。再就是省份简称和字母数字的混淆问题——京和津、B和8、O和0OCR阶段很容易翻车。商业SDK确实省事但有两个绕不开的问题。一是费用按调用次数计费车流量大的场景一个月下来成本不低。二是数据安全车牌属于个人敏感信息很多物业和园区项目明确要求本地化部署图像不能出内网。自己做一套离线方案一次性的开发成本后面就没有边际成本了这就是这个项目的出发点。1.2 技术选型为什么是 YOLO11 RapidOCR先说检测。车牌检测模型可选的不多主流就那几个思路老牌HyperLPR用的是一种轻量级检测网络加LSTM识别效果不错但定制性弱直接用YOLOv8也能做但YOLO11出来后精度和速度的提升对这类小目标场景更友好。YOLO11是Ultralytics在2024年底推出的新一代目标检测模型。和v8相比核心变化在于用C3k2模块替代了原来的C2f模块同时在骨干网络里引入了C2PSACross Stage Partial with Position-Sensitive Attention模块说白了就是让网络在提取特征时更关注空间位置关系。这对车牌这种位置关键、尺寸小、周围干扰多的目标很有用。而且YOLO11提供了n/s/m/l/x五个尺寸从边缘设备到服务器都能选到合适的档位生态也成熟——训练、导出、部署一条龙都给你包好了。再说OCR。RapidOCR不是某个厂商的SDK而是一个开源项目组做的推理框架核心是把PaddleOCR训练好的检测和识别模型转换成了ONNX格式然后用ONNX Runtime做推理。好处是显而易见的三点第一模型是静态导出的ONNX不需要装PaddlePaddle全家桶部署体积小第二ONNX Runtime对CPU优化得非常好实测在纯CPU环境下单张图片全流程识别也就几百毫秒第三RapidOCR同时支持DBNet检测和CRNN/SVTR识别两条路径精度和速度的平衡点不错对车牌这种字符排列整齐、背景相对干净的场景完全够用。为什么不直接用HyperLPR那种检测识别端到端的方案原因是HyperLPR更偏专用车牌检测和识别耦合在一起想换检测模型必须动整体架构而YOLO11 RapidOCR是两条独立的链路检测、识别各自可以单独调优、单独替换。后面如果想升级检测精度只需要换更好的检测模型OCR那边完全不动反过来想优化识别也不影响检测。这种松耦合的设计在实际项目里维护起来要舒服得多。2. 环境准备十分钟把依赖装利索2.1 硬件环境的取舍我的开发机配置是i5-12400 16GB内存无独立GPU训练阶段用了租来的云GPU一张RTX 4090推理部署放在本地CPU上。如果你的机器比我好那自然更顺利如果连GPU都没有也不用慌YOLO11训练虽然吃GPU但推理阶段CPU也够用只是帧率上不去后面我会展开说性能这块。部署目标环境我定的是CPU-only这有两个原因一是客户现场的工控机普遍没有GPU二是有GPU的机器成本高、功耗大为了一个出入口识别去配显卡不太现实。所以整个项目从选型阶段就把CPU能跑作为硬指标。YOLO11的nano和small版本在CPU上的表现都还行RapidOCR的ONNX模型在CPU上更是毫无压力这个组合本身就是为了轻量部署而存在的。2.2 安装步骤与踩坑记录第一步装基础依赖Python版本建议3.10及以上太低的话ultralytics和rapidocr的依赖会有兼容性问题。我用的Python 3.10.11。pip install ultralytics pip install rapidocr_onnxruntime pip install opencv-python安装过程有几个坑值得说。第一个坑是ultralytics会自动依赖torch如果你不打算用GPU训练它会默认装CPU版torch这没问题但如果你电脑上已经有GPU版torch记得装ultralytics的时候不要指定版本让它跟随你现有的torch。第二个坑是rapidocr_onnxruntime这个包名早期的版本叫rapidocr新版拆出来一个带运行时的专用包直接装rapidocr_onnxruntime就对了。第三个坑是OpenCV的版本如果你后续要处理视频流最好装opencv-python而不只是opencv-python-headless否则打开视频文件时容易报错。装完之后做个快速验证跑通最小流程from ultralytics import YOLO model YOLO(yolo11n.pt) results model(test.jpg) print(results[0].boxes.xyxy)from rapidocr_onnxruntime import RapidOCR ocr RapidOCR() result, elapse ocr(test_plate.jpg) print(result, elapse)能正常输出结果说明环境就绪了。yolo11n.pt第一次调用会自动下载如果网络慢可以考虑用镜像或者提前在官网下载好放到当前目录。3. 车牌检测YOLO11 的训练与推理3.1 数据集从哪里来车牌检测模型不能用官方预训练的COCO权重直接跑COCO里没有车牌这个类别你得用自己的数据集训练。数据来源有两个方向。第一个方向是直接用公开的数据集。国内最常用的车牌数据集是CCPD中国城市车牌数据集包含约25万张图片覆盖了合肥、上海、广州等城市的停车场、道路场景图片里有标注好的车牌框。缺点是场景偏单一主要是安徽省的牌照而且图像分辨率固定。另一个是CRPD中国道路车牌数据集场景更丰富一些但标注质量参差不齐。用公开数据集的好处是量大、标注规范缺点是可能和你实际部署场景不太匹配——比如你的入口相机装在雨棚下CCPD里没这种视角。第二个方向是自己采集标注。这是我更推荐的路径尤其在项目有明确落地场景的时候。在客户现场架一台相机拍上两三天不同时段、不同光照、不同车道的图片基本都能覆盖到。然后用LabelImg或者X-AnyLabeling这类工具标注导出成YOLO格式的txt文件每行是类别 x_center y_center width height坐标都是归一化到0-1的。我自己标注了大概3000张再混合公开数据集的一些图片总共凑了1.2万张覆盖了晴天、阴天、雨天、夜间、强逆光这些典型场景。这里要特别提醒标注要做到宁多勿漏。车牌被遮挡了一部分只要还能辨认是车牌就要把可见部分完整框出来严重模糊到人眼都读不出来的直接删掉不要让模型去学这种不好学的样本。还有一个小技巧把图像里出现的多块车牌比如前方车和旁边车同时入镜全部标注不要只标主要的那块这样模型才能学会处理多目标的场景。3.2 训练参数配置与网络结构解读训练前先理解一下YOLO11的结构这直接关系到你怎么调参。YOLO11的骨干网络采用了C3k2模块代替v8的C2f每个C3k2内部做了更高效的梯度分流设计减少了冗余计算所以YOLO11在相同精度下比v8更快。骨干末端的C2PSA模块引入了基于位置的空间注意力可以把注意力放到图像中更有判别性的区域这对小目标的定位帮助很大。我选的是yolo11s作为训练起点。yolo11n虽然最快但精度上限略低对车牌这种小目标不太放心yolo11m精度更好但部署时CPU推理速度翻倍往下掉。s是折中实测下来精度和速度都符合预期。训练命令如下yolo detect train \ --model yolo11s.pt \ --data license_plate.yaml \ --epochs 200 \ --imgsz 640 \ --batch 32 \ --patience 30 \ --device 0license_plate.yaml的内容path: /data/license_plate train: images/train val: images/val names: 0: license_plate几个关键参数说明一下。imgsz我用的640为什么不用1280因为车牌虽然小但通常不是超小目标——一辆车在画面里占300像素的话车牌大概有60到80像素宽640的分辨率下模型能学到足够特征。如果你们现场相机视场角特别大车牌只有三四十像素那建议imgsz改成1280或者用下面说的P2小目标增强。batch设成32是4090上比较合适的选择显存不够就改16。epochs设200但配合patience 30做早停一般训练到120到150轮就会收敛后面基本是震荡。YOLO11官方还提供了小目标增强的思路——给模型加P2检测头。P2头在高分辨率特征图上做检测对小目标的召回率提升非常明显。实现方式是在模型配置里加一个head结构Ultralytics的文档有说明。我另一个项目里用过小目标小于20像素的mAP确实有明显提升但推理速度会下降15%左右。如果你的车牌经常很小值得一试如果画面里车牌都在50像素以上用默认的P3-P5头部就好没必要损失速度。训练过程中要盯几个指标mAP50要尽量到0.98以上mAP50-95能到0.85左右就算不错了。训练集loss在下降、验证集loss在150轮左右趋于平缓说明收敛正常。如果验证集loss一直下不去或者反弹优先怀疑数据集问题——标注不准确、场景样本不足、光照变化太大导致的类内差异过强。3.3 推理与后处理训练好之后导出best.pt推理就很简单from ultralytics import YOLO import cv2 model YOLO(best.pt) frame cv2.imread(scene.jpg) results model(frame, conf0.35, iou0.45, imgsz640, verboseFalse) for r in results: boxes r.boxes for box in boxes: x1, y1, x2, y2 box.xyxy[0].tolist() score box.conf[0].item() cls int(box.cls[0].item())这里conf和iou两个参数是要实际调一调的。conf是置信度阈值设太低就会出现很多误检——比如把车尾的装饰板、车灯识别成车牌设太高就会有漏检。我的经验是0.35到0.4之间比较稳如果你们现场背景复杂、误检多可以往0.5提。iou是非极大值抑制NMS的阈值负责去掉重复检测框一般保持默认的0.45到0.5就行。如果检测结果的框不够准——比如框偏大把旁边字体也框进去了或者框偏小切掉了一个字符——那就是数据集标注精度的问题需要回头检查标注框是否紧贴车牌边缘。另一个能立刻见效的优化是对检测框做轻微的手工膨胀比如向外扩5%的像素让OCR时字符区域更完整。4. 车牌字符识别RapidOCR 的接入与调优4.1 RapidOCR 为什么适合这个场景RapidOCR的技术来源是PaddleOCR但把模型转成了ONNX用ONNX Runtime推理。它的文本检测模型是DBNet可微分二值化网络文本识别模型是CRNN加CTC解码同时可选的还有SVTR。这套组合在通用中文OCR场景下表现一直不错而且ONNX Runtime在CPU上的推理速度非常可观。这里要回应一个常见的疑虑——网上有不少人吐槽python上利用rapidocr太吃cpu。我也遇到过这种情况排查下来大部分不是rapidocr的问题而是使用姿势不对。最典型的是把整张1080p图片直接丢给OCR检测阶段会在全图上跑如果图上有大量文字区域CPU占用自然高。车牌的crop区域很小、文字行就一行RapidOCR处理起来其实是轻量级的。所以正确姿势是先用YOLO11把车牌区域裁出来再把这个小图交给OCR根本不存在CPU压力的问题。另一个CPU优化的点是ONNX Runtime的执行提供程序。默认是CPUExecutionProvider但在较新的CPU上建议明确指定为DNNLoneDNN模式可以进一步提速from rapidocr_onnxruntime import RapidOCR ocr RapidOCR( det_use_cudaFalse, rec_use_cudaFalse )初始化时保持默认就ok不需要额外配置ONNX Runtime自己会发挥CPU的向量化指令集。4.2 基本调用与车牌区域的专用调用方式RapidOCR的基础调用很简单from rapidocr_onnxruntime import RapidOCR import cv2 ocr RapidOCR() plate_crop cv2.imread(plate_crop.jpg) result, elapse ocr(plate_crop) if result: for line in result: box line[0] # 四个角点坐标 text line[1] # 识别文本 conf line[2] # 置信度 print(text, conf)默认情况下RapidOCR在自己的流程里会先跑一遍文本检测DBNet检测出文字行区域再做方向分类cls最后做文字识别rec。当我们传入的已经是YOLO11裁剪出来的车牌小图时文本检测这步实际上是冗余的——因为图里就一行字而且大概率已经水平排列。针对这种场景可以跳过多余步骤直接用RapidOCR的识别模块处理裁剪后的图片把检测和方向分类省掉一半计算。具体做法是用RapidOCR内部暴露的文字识别接口先初始化再直接调recfrom rapidocr_onnxruntime import RapidOCR ocr RapidOCR() # 对裁剪好的车牌小图直接做识别 result, elapse ocr.text_recognition(plate_crop)这里需要注意text_recognition接收的输入是经过det处理的区域参数直接传图片的话需要用更底层的方式。更稳妥的推荐是依然走全流程但传入前把图先旋转校正成水平。因为RapidOCR全流程的处理速度在车牌小图上也就是几十毫秒省那一点时间意义不大反而容易把流程搞复杂。这个取舍我实际跑过结论是单张处理场景直接走全流程如果是实时视频流、需要控制每帧耗时的场景再考虑拆模块优化。4.3 针对车牌识别的图像预处理与参数调优RapidOCR在干净的车牌图上识别效果很好但真实场景的车牌图不会那么干净。我在测试集上跑了一轮发现几个典型的识别错误一是蓝色车牌的底色干扰二是倾斜导致字符粘连导致皖识别成晚或安三是夜间补光不足导致字符对比度低。针对这些要上预处理手段。倾斜校正是最重要的。YOLO11检测出来的是水平矩形框但如果车牌本身在画面里是歪的——比如车头微微偏转、相机安装角度不水平——裁剪出来的图里字符就是斜的。OCR对这种图识别率会明显下降。我的做法是先检测车牌的角点或者用OpenCV做透视变换。简单方案是用minAreaRect拿到最小外接矩形的角度再旋转校正。复杂一点但更通用的是用YOLO11的OBB旋转框检测能力直接输出带角度的框——YOLO11支持旋转框检测这对本项目是个很好的补充选项后面整合章节再说。对比度增强也很关键。强光下的车牌字符被晒得发白夜间补光不足是另一极端。我用了CLAHE限制对比度自适应直方图均衡化效果非常直接import cv2 def enhance_plate(img): lab cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l, a, b cv2.split(lab) clahe cv2.createCLAHE(clipLimit3.0, tileGridSize(8, 8)) l clahe.apply(l) lab cv2.merge([l, a, b]) return cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)参数clipLimit我用的3.0太高会把噪点放大太低了没效果。另外还有一个小技巧很多车牌是蓝底白字可以先用颜色过滤把蓝色通道突出再转灰度能进一步拉大字符和背景的对比度。RapidOCR还暴露了一些推理参数可以调整。其中text_score默认0.5是识别置信度的过滤阈值车牌场景建议稍微调高到0.6把低置信度的垃圾结果过滤掉宁可miss也不输出错误文本——因为后端系统对错误车牌的容忍度比对未识别到更低。use_det和use_cls按上面说的在车牌crop场景下如果走全流程可以保留默认如果拆模块则直接禁用。4.4 识别结果的后处理与字符纠错OCR输出的原始文本经常带着空格、特殊符号或者把字符读错。对车牌识别必须做一层严格的后处理。车牌字符的语法规则要先了解。大陆车牌分两类普通蓝牌和黄牌是7位字符第一位是省份简称汉字第二位是发牌机关代号大写字母后面5位是字母和数字混合新能源绿牌是8位字符结构是省份简称字母6位字母数字混合。还有特殊牌照比如使馆牌照、警车牌照、挂车牌照字符规则有些差异。后处理第一步是清洗字符去掉OCR输出里的空格、横杠、小数点等非法字符。第二步是格式校验用正则去匹配合法的车牌模式import re plate_pattern re.compile( r^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁使领学警港澳] r[A-Z] r[A-Z0-9]{5,6}$ ) def normalize_plate(raw_text): text re.sub(r[\s\-\.·], , raw_text).upper() if not plate_pattern.match(text): return None return text第三步是针对OCR常见的字符混淆做纠错映射。最容易混的是0和O、1和I、8和B、2和Z。中国车牌里字母I和O基本不会出现在民用车辆上使馆车和警车除外所以遇到1和I这种争议字符时按位次规则处理位置4到7的字符优先认为是数字。这个纠错规则看起来简单实际使用中能把错误率压低不少。5. 检测 识别的完整流程整合5.1 整体流程设计把YOLO11和RapidOCR串起来整个识别流程是五步视频帧输入、YOLO11检测车牌、裁剪车牌区域、RapidOCR识别字符、后处理输出标准车牌号。看似简单但工程上要处理几个细节。第一个是图像来源。我做的是图片和视频流两种模式。图片模式是给入口相机拍的单张抓拍图识别一次返回结果视频流模式是从摄像头实时读帧需要加一个帧率控制避免每帧都跑全流程导致CPU打满。我的做法是视频模式每0.5秒抽一帧做识别拿到的车牌号做滑动窗口投票——连续三帧识别出同一个车牌才确认输出。这样既省CPU又能过滤掉单帧误识别。第二个是车牌裁剪的保底逻辑。YOLO11有时候会把车牌外的区域也框进来比如框里带了车标或前杠边缘。我的处理是裁剪时按检测框的中心向四周外扩一定的比例外扩量设为原框宽高的10%大部分情况下能保证字符完整。第三个是结果缓存。同一辆车的车牌在连续几帧里是同一个如果每次都重新跑OCR就浪费了。我给每个检测框加了一个特征——框的位置和尺寸——如果上一帧已经识别过同一位置的框就直接复用上帧的识别结果只做置信度的累积更新。这个逻辑在车辆静止排队时特别好用CPU占用直接降一半。5.2 核心代码实现整合代码不复杂核心结构如下import cv2 import re import numpy as np from ultralytics import YOLO from rapidocr_onnxruntime import RapidOCR class PlateRecognizer: def __init__(self, det_model_pathbest.pt): self.detector YOLO(det_model_path) self.ocr RapidOCR() self.pattern re.compile( r^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼宁使领学警港澳] r[A-Z][A-Z0-9]{5,6}$ ) self.last_cache {} # 简单的帧间缓存 def detect_plates(self, frame): results self.detector(frame, conf0.35, iou0.45, imgsz640, verboseFalse) boxes [] for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() boxes.append((int(x1), int(y1), int(x2), int(y2))) return boxes def recognize_crop(self, crop): result, _ self.ocr(crop) if not result: return None raw result[0][1] return self.normalize_plate(raw) def normalize_plate(self, text): text re.sub(r[\s\-\.·], , text).upper() if not self.pattern.match(text): return None return text def process_frame(self, frame): boxes self.detect_plates(frame) plates [] for (x1, y1, x2, y2) in boxes: pad_x int((x2 - x1) * 0.1) pad_y int((y2 - y1) * 0.1) x1 max(0, x1 - pad_x) y1 max(0, y1 - pad_y) x2 min(frame.shape[1], x2 pad_x) y2 min(frame.shape[0], y2 pad_y) crop frame[y1:y2, x1:x2] crop enhance_plate(crop) plate self.recognize_crop(crop) if plate: plates.append((plate, (x1, y1, x2, y2))) return plates这段代码能直接跑通核心链路。实际项目里我还会加一个可配置的开关——是否启用增强预处理、是否启用帧间缓存、置信度阈值和NMS阈值都从配置文件读取方便在不同现场调整不用改代码。5.3 性能优化与CPU占用控制这个项目最大的性能瓶颈不在OCR而在YOLO11的检测阶段。yolo11s在CPU上一次推理大约150到250毫秒取决于CPU型号和输入分辨率RapidOCR处理一个车牌crop只需要20到40毫秒。所以优化重心放在检测端。第一招是控制推理分辨率。如果现场相机输出是1080p甚至更高直接喂给模型会非常慢。我的做法是把整帧缩放到960像素宽再输入检测车牌检测的精度几乎不受影响但速度提升明显。缩放比例怎么定先测一下你们现场画面里车牌的像素宽度保证缩放后车牌仍然有40像素以上就够。第二招是ROI区域约束。出入口场景中车牌只会在画面特定区域出现——比如车道中间、画面下半部分。可以配置一个ROI感兴趣区域只在ROI内做检测把画面上下两端的天空、绿化带全部排除。这个优化在固定相机的场景下效果显著检测耗时能再降30%以上。第三招是推理线程控制。ONNX Runtime本身支持多线程RapidOCR初始化时会自动探测CPU核心数。在并发高的服务场景要注意避免每个请求都创建新的RapidOCR实例最好做成单例复用——实例内部有线程池频繁创建销毁会白白消耗资源。我把它封装成了模块级单例实测并发8路请求时CPU占用反而比并发4路各自建实例要低。6. 踩坑实录与问题速查6.1 高频问题与排查思路把实际测试中遇到的问题整理成了一张速查表每一条都是踩过坑换来的现象可能原因解决方案频繁漏检车辆开太快识别不到相机的触发帧率低或车辆运动模糊严重缩短曝光时间视频流模式抽帧间隔从0.5秒改到0.15秒开启YOLO11的Mosaic增强提升泛化画面中有多块非车牌的矩形物被误检置信度阈值过低conf从0.35提到0.5检查训练集里是否缺少负样本补充不包含车牌的背景图做负样本训练OCR输出字符缺位少了一两个字符车牌裁剪框太小边缘字符被切掉裁剪外扩比例从5%提到10%检查YOLO11标注框是否过紧B识别成8、0识别成OOCR长尾字符混淆加入显式的字符映射纠错对辨识度差的字符用多帧投票机制做字符级置信度投票夜间识别率大幅下降补光不足、红外模式下的图像噪声大使用CLAHE增强有条件时改用双模态相机可见光红外提高灰度图的对比度权重CPU占用高服务响应慢每帧都跑全流程或OCR实例反复创建按5.3的三招做降分辨率、ROI约束、OCR实例单例化强逆光下车牌一团黑相机HDR未开启或动态范围不足开启相机WDR/HDR模式在图像预处理中增加局部亮度修正用Gamma矫正新能车牌绿色车牌的字符识别差绿色底色和字符对比度低对绿色通道做直方图拉伸把这个情况作为专项数据增强加入训练集6.2 我个人的避坑清单有几个细节是普通文档里不太会写到的单独拎出来说。第一个是关于训练数据配比。很多人只加正样本不关注负样本。我在项目里故意往训练集里塞了1000多张没有车牌的道路/停车场背景图标注文件为空。这个手段显著降低了误检率因为模型学会了这个画面里没有车牌的分布。YOLO训练时对空标签文件是支持的没必要删掉这些图。第二个是关于OCR的置信度运用。RapidOCR返回的confidence是一个0到1的小数不要只在后处理时一刀切。我的做法是置信度大于0.9的结果直接输出0.6到0.9之间的结果标记为待确认用下一帧的识别结果做投票低于0.6的丢弃。这个机制让最终输出错误的车牌号大幅减少代价是识别响应时间会多一帧左右的延迟。第三个是部署时的模型格式。YOLO11支持的导出格式很多包括ONNX、TensorRT、OpenVINO。在同一台CPU机器上我把PyTorch格式的模型导出成ONNX推理速度提升了将近一倍。如果部署环境是Intel CPU强烈建议试试导出成OpenVINO格式速度还能再上一个台阶。这个优化代价极低收益却非常明显。导出命令很简单yolo export modelbest.pt formatonnx dynamicTrue imgsz640导出的ONNX文件加载方式和原来一致改动量只有一行。7. 项目扩展方向与个人体会7.1 后续可以做的几个方向这套检测加识别的链路做完只是第一步。实际项目中我还在考虑的扩展方向有四个。第一个是增加车牌颜色识别。蓝牌、黄牌、绿牌、白牌在不同业务场景里含义不同——比如黄牌货车在某些路段限行绿牌新能源在某些城市优先通行。实现方式很简单在YOLO11检测框内取车牌区域的主色调做分类蓝色、黄色、绿色各训练一个简单的分类头或者用回归法判断HSV空间的主色值成本很低但业务价值大。第二个是结合跟踪算法做车辆级识别。现在的方案是每帧独立识别车在画面里长时间停留时会产生大量重复识别结果。给检测框挂一个简单的跟踪器——比如ByteTrack或BoT-SORT——就能把同一辆车的多次识别结果汇在一起做时间维度的投票和融合识别准确率还能再提升一截。YOLO11的box输出天然兼容这些跟踪框架接入成本很低。第三个方向是用SAM3做精细化分割。现在这个方案里OCR吃的是矩形裁剪图如果车牌旁边有前车车牌或者反光物体干扰OCR可能把其他区域的内容也读进来。如果检测后用分割模型把车牌像素精确分割出来再做OCR能彻底排除背景干扰。这个方向的代价是增加了推理耗时适合对精度要求极高、对速度不太敏感的场景。第四个是端到端的部署封装。现在代码是Python可以直接用FastAPI包一层HTTP服务也能用PyInstaller打包成独立可执行文件方便分发到客户现场工控机。车牌识别的输入往往还需要对接道闸控制系统、收费系统的接口做好输入输出协议的定义图片Base64上传、JSON结果返回整个项目才能真正落地。7.2 一些实际操作的体会做了这么多次AI落地的项目我最深的体会是不要追求单个模型的极致精度要追求整个链路的稳定性。车牌识别这个项目里YOLO11单模型的检测精度已经很高了RapidOCR单模块的识别能力也在线但把两者串起来之后90%的问题出在衔接环节——裁剪是否合适、图像预处理是否到位、字符纠错是否合理。把这些工程细节打磨好比单纯换一个更大更精准的模型收益大得多。另外一个体会是CPU部署真的没那么可怕。只要选对模型尺寸、控制好推理分辨率、该缓存缓存、该复用复用即使在无GPU的工控机上也能做到单张250毫秒以内出结果加上视频流抽帧策略完全能支撑一个出入口的实时识别需求。网上很多讨论动不动就建议上GPU其实在车牌识别这个场景CPU方案已经足够用了。最后分享一个小技巧如果你们现场测试时发现某类车牌的识别率总是不行别急着怀疑模型先用手机多拍几张不同角度的照片喂进去看看。很多时候问题出在相机的安装角度——车牌反光角度导致字符被强光淹没或者相机架得太高导致车牌在画面中过于倾斜。调整相机位姿往往比调模型参数更有效。把相机安装规范写进项目交付文档比写一千行调优代码都管用。
阅读完成 · 觉得有帮助?