ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

复杂环境下车牌识别技术实战:从检测到识别的完整方案解析

复杂环境下车牌识别技术实战:从检测到识别的完整方案解析 1. 复杂环境下车牌识别到底难在哪先看清问题再动手做车牌识别这个方向很多人一开始都会觉得“不就是先检测车牌再识别上面的字符吗”等自己拿真实数据跑一遍才发现完全不是那么回事。我最初也是在干净数据集上把准确率刷到95%以上结果一到停车场、收费站、路测场景立刻被打回原形。所以这篇文章我想先从问题本身聊起把我踩过的坑、试过的方法、最终能稳定跑通的方案都整理出来。先说结论复杂环境下的车牌识别核心难点根本不在“识别”这一步而在“能不能稳定拿到一张高质量的、正视角的车牌图像”。这里的复杂环境包括但不限于光照剧烈变化、车牌倾斜旋转、运动模糊、遮挡、雨雾天气、夜间低照度、高光反射甚至还有车牌本身被泥水污染、边框铆钉干扰、字体磨损老化等情况。任何一个因素叠加进来识别率都会明显下降。对于计算机视觉方向的初学者或者正在做大作业、课程设计的学生我建议不要一上来就追求端到端的黑盒模型。先理解“检测 校正 识别”这条经典链路把每个环节的问题拆清楚再决定哪些环节用深度学习、哪些环节用传统图像处理。实际工程里传统算法并没有过时有些环节它比深度模型更稳、更快、更省资源。一个典型的两阶段车牌识别系统可以拆成下面几个子任务车牌区域定位从复杂背景中找到车牌所在的矩形区域这一步属于目标检测任务需要排除汽车前脸、行人、路面文字、路牌等干扰。车牌角度校正实际拍摄中车牌往往不是理想的正视矩形而是带有透视畸变的四边形需要做透视变换还原。车牌字符分割与识别把校正后的车牌图像中的字符逐个或者整体序列化识别出来输出最终的字符串结果。后处理与规则校验根据车牌编码规则对识别结果进行合法性校验和纠错比如汉字省份简称、发牌机关代号、编号字符的组合规律。我先用一个生活化的类比来解释这套流水线这就像你在路边看一辆飞驰而过的汽车想记下它的车牌号。你首先要“锁定”车牌位置注意力机制接着如果车是歪着开过去的你脑袋会不自觉地把画面“摆正”再读空间变换网络最后你才能逐个字符念出来序列识别。计算机视觉做这件事本质上就是把这个人类感知过程拆成显式的算法步骤。2. 车牌检测阶段怎么选型深度学习检测器与传统图像处理的分工2.1 车牌检测的经典路线颜色特征 边缘特征 形态学操作在深度学习还没有普及的年代车牌检测主要依靠颜色空间转换和形态学操作。蓝底白字是中国最常见的车牌样式所以一个很自然的思路是先把图像从RGB转到HSV色彩空间提取蓝色区域作为候选区域候选。为什么用HSV而不是直接用RGB因为HSV将颜色信息分解为色相H、饱和度S和明度V对光照变化的鲁棒性远好于RGB。RGB三个通道都随光照强度整体缩放阈值不好定而H分量在小幅光照变化下基本稳定。具体操作上我用OpenCV实现过的流程大概是import cv2 import numpy as np def get_plate_candidates(image): # 转HSV色彩空间 hsv cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # 蓝色车牌阈值范围H,S,V lower_blue np.array([100, 80, 80]) upper_blue np.array([124, 255, 255]) mask_blue cv2.inRange(hsv, lower_blue, upper_blue) # 形态学闭运算连接相邻区域 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (15, 5)) mask_closed cv2.morphologyEx(mask_blue, cv2.MORPH_CLOSE, kernel) # 查找轮廓 contours, _ cv2.findContours(mask_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates [] for cnt in contours: # 计算外接矩形用长宽比筛选 x, y, w, h cv2.boundingRect(cnt) aspect_ratio w / h area w * h # 标准车牌长宽比约3:1适当放宽 if 2.0 aspect_ratio 5.0 and area 2000: candidates.append((x, y, w, h)) return candidates这套方法在光照均匀、背景简单的场景下效果不错计算速度快到可以在树莓派上实时跑。但它的缺陷也很明显一旦遇到白色新能源车牌渐变绿色底、黄色大车车牌或者车身本身就是蓝色、场景里有蓝色广告牌纯颜色阈值就崩了。所以我后来在真实项目里只用它做“快速初筛”候选区域会继续交给分类器确认。2.2 基于深度学习的检测器选择YOLO系列与开源车牌检测模型复杂环境下要稳还是得上深度学习目标检测。我实测过YOLOv5和YOLOv8在车牌检测上的表现精度差距不算特别大但YOLOv8在边缘设备上的推理效率更高部署也更方便。这里我分享一个关键经验用开源预训练模型做迁移学习比自己从头训练省太多事。网上比较成熟的开源车牌检测模型有几个方向直接用YOLO官方COCO权重但COCO数据集里没有专门的车牌类别所以这条路走不通得自己标注或找车牌数据集微调。用PP-OCR系列里的文本检测模型它的检测头能框出场景中的文本区域车牌本质上也是文本所以效果不错尤其是中国车牌。找现成的车牌检测开源项目比如GitHub上有很多基于YOLOv5的车牌检测仓库通常附带标注好的国内车牌数据集。我的建议是先下载一个已经用国内车牌数据微调过的YOLOv5或YOLOv8权重跑一遍你的测试视频看看哪些场景下漏检、误检再针对性补充数据微调。不要一上来就自己标几千张图标注成本太高而且新手容易在anchor参数上翻车。关于anchor这个细节我用YOLOv5时踩过一次坑。默认anchor是基于COCO数据集聚类出来的COCO里的大目标尺度和车牌分布差异很大导致训练初期loss降得很慢。后来我改用自定义数据集重新聚类anchor训练收敛速度明显加快最终mAP也高了几个点。具体操作是把标注好的车牌框长宽喂给k-means聚类生成适合车牌尺寸的先验框然后在yaml配置里替换默认值。2.3 候选区域再确认一个轻量分类器解决误检问题深度学习检测器偶尔也会把车灯、散热器格栅、前保险杠装饰条当成车牌。这时候引入一个轻量级二分类器车牌/非车牌做二次确认非常有效。我常用的做法是从检测结果里裁剪出候选区域缩放到统一尺寸然后丢给一个简单的CNN分类器。这里有一个工程上的省事技巧如果检测器本身够强比如mAP 0.5以上达到95%你可以跳过独立的二分类器直接用字符识别结果做反向确认。如果OCR出来一串字符并且符合车牌编码正则表达式那就接受检测框否则丢弃。这个策略我把误检率压到了很低几乎不影响速度。3. 透视几何在车牌校正中的应用倾斜车牌的摆正实战3.1 为什么必须做透视校正很多初学者直接把检测框里的图像送去识别结果发现识别率很不稳定。原因在于车辆在画面里不总是正对着镜头。侧方停车、转弯、跟车距离近、摄像头安装角度高都会让车牌产生透视畸变——车牌不再是标准矩形而是变成不规则的四边形。字符识别模型对输入图像的归一化要求很高。CRNN这类序列模型在稍微倾斜的文字上还能勉强工作但倾斜角度一大字符之间会发生重叠、粘连识别准确率断崖式下跌。所以在检测和识别之间加一个透视校正模块是整个系统稳定性的关键所在。3.2 四点透视变换的原理与实现四点透视变换的基本思路是在畸变图像上找到车牌的四个角点然后计算一个变换矩阵把四边形映射到一个预设尺寸的矩形上比如 440x140对应标准车牌尺寸比例。变换矩阵的计算在OpenCV里就是cv2.getPerspectiveTransform一行代码的事。真正的难点在于怎么稳定地找到那四个角点。我用的方法是基于轮廓的近似多边形拟合。车牌检测框已经缩小了范围在框内再做一次边缘检测和轮廓查找然后用cv2.approxPolyDP把轮廓拟合成四边形。如果轮廓点数为4就把它作为车牌的四条边如果点数不是4则退回到检测框的四个角点。def order_points(pts): # 按左上、右上、右下、左下顺序排列四个角点 rect np.zeros((4, 2), dtypefloat32) s pts.sum(axis1) rect[0] pts[np.argmin(s)] # 左上角xy最小 rect[2] pts[np.argmax(s)] # 右下角xy最大 diff np.diff(pts, axis1) rect[1] pts[np.argmin(diff)] # 右上角x-y最小 rect[3] pts[np.argmax(diff)] # 左下角x-y最大 return rect def four_point_transform(image, pts): rect order_points(pts) (tl, tr, br, bl) rect # 计算输出矩形的最大宽度和高度 width_top np.linalg.norm(tr - tl) width_bottom np.linalg.norm(br - bl) max_width max(int(width_top), int(width_bottom)) height_left np.linalg.norm(bl - tl) height_right np.linalg.norm(br - tr) max_height max(int(height_left), int(height_right)) dst np.array([ [0, 0], [max_width - 1, 0], [max_width - 1, max_height - 1], [0, max_height - 1] ], dtypefloat32) M cv2.getPerspectiveTransform(rect, dst) warped cv2.warpPerspective(image, M, (max_width, max_height)) return warped注意order_points这一步看起来很不起眼但角点顺序一旦错了变换结果就是镜像翻转或者对角线拉伸整个识别全废。四个点的排列逻辑是左上角的xy和最小右下角的xy和最大右上角的x-y差值最小左下角的x-y差值最大。这个技巧我在多个项目里复用过很可靠。3.3 透视校正后的尺寸归一化透视变换输出的尺寸是动态计算的不同帧之间可能大小不一致。在做字符识别前我还会把它统一缩放到固定尺寸。缩放比例建议保持车牌宽高比不变避免字符被横向或纵向拉伸变形。以中国蓝牌为例标准尺寸440mm x 140mm宽高比约3.14。我一般缩放到240x76或者300x96这个分辨率下的字符细节足够识别模型使用同时计算开销也不大。新能源车牌是480mm x 140mm宽高比约3.43检测到绿色车牌时应按这个比例归一化否则字符宽度会被压缩影响识别率。4. 车牌字符识别从传统分割到序列识别的演进与选型4.1 传统字符分割方案的局限早期的车牌识别系统普遍走“字符分割 单个字符分类”的路线先通过投影法找到字符间的空隙逐一切出单个字符图像再用分类器SVM、CNN等逐个识别。这个方案在图像质量好的时候表现尚可但一到复杂环境就非常脆弱。投影法分割字符的核心是统计每一列的像素值或边缘强度找到波谷作为字符分隔点。这个思路有个致命弱点车牌上的铆钉、边框、汉字联通笔画、反光条都会造成投影曲线异常导致分割位置偏移。而且一旦分割错误后面每个字符都错位整个识别结果报废——错误传递是串联系统的通病。4.2 CRNN CTC免分割序列识别的核心逻辑现在的方案主流是CRNNConvolutional Recurrent Neural Network CTCConnectionist Temporal Classification直接对整张车牌图像做序列识别不需要显式分割字符。它的原理可以这样理解CNN负责提取图像的空间特征RNN通常是双向LSTM按时间步处理这些特征序列CTC负责把预测序列对齐到最终标签并自动处理字符之间的空白和重复。CTC的关键作用是解决“对齐问题”。字符识别中我们不知道每个字符在图像序列里对应哪几个时间步。CTC为每个时间步预测一个字符分布并允许输出中包含空白符号blank然后在解码阶段把所有相邻重复字符和空白合并得到最终结果。举个简化例子如果时间步预测序列是A-A-空白-1-1-2-空白-3-3合并后就是A123。这个机制让模型可以端到端训练不需要人工标注字符位置。在车牌识别这个场景里我推荐直接用LPRNetLightweight Plate Recognition Network它是专为车牌识别设计的轻量级网络结构上也是CNN RNN CTC的思路但比通用CRNN更精简模型的参数只有几十万CPU上跑一张图只要几毫秒。LPRNet还有一个优势是支持不定长字符序列这对新能源车牌比蓝牌多一位特别友好。4.3 车牌字符集的特殊性汉字识别与易混淆字符车牌字符集和普通OCR有本质区别它包含省份汉字31个省级行政区简称、发牌机关字母24个不含I和O、以及数字和字母组合。分类空间大约70类左右比通用OCR小得多但难在字符间相似度极高。汉字里“京”“津”“冀”“鲁”等字形差异相对明显比较容易区分但字母和数字之间的混淆是重灾区——O和0、I和1、Z和2、B和8、G和6在低分辨率、模糊、污损情况下极其容易认错。我处理这类问题有两个经验在训练数据里做有针对性的数据增强对易混淆字符对做更多的模糊、噪声、形变扰动让模型学会在“模糊”状态下区分它们。在后处理阶段用车牌编码规则做约束修正。比如中国车牌第2位是省份简称的字母取值范围是A-Z但排除I和O编号位如果识别结果是O则可以依据上下文修正为0识别结果是I同理修正为1。这类规则用正则表达式实现很简单但能实打实把整体准确率提升1到2个百分点。4.4 正则表达式后处理技巧后处理这块值得专门说说。我的流水线里会先让模型输出原始识别结果再做一套基于规则的清洗import re PROVINCE_CODES set(京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新) LETTERS set(ABCDEFGHJKLMNPQRSTUVWXYZ) DIGITS set(0123456789) def validate_plate(text): # 移除可能混入的干扰字符 text text.strip().upper() if len(text) 7 or len(text) 8: return None # 第一位必须是省份汉字 if text[0] not in PROVINCE_CODES: return None # 第二位必须是发牌机关字母排除I和O if text[1] not in LETTERS: return None # 最后一位如果是新能源车牌可能为字母其他情况多为数字 # 这里做宽松处理仅保证不含非法字符 for ch in text[2:]: if ch not in LETTERS and ch not in DIGITS: return None return text正则表达式在实际工程里还有一个作用可以在README里作为验收标准。我给别人交付系统时都会直接写明“识别结果必须通过车牌字典校验”这样运维人员拿到系统后自我检测也有据可依。5. 光照、模糊与恶劣天气复杂环境下的图像预处理策略5.1 光照不均与高光反射的处理停车场出入口常见的场景是晴天正午阳光直射车牌蓝底变成淡蓝甚至泛白白字变成亮白对比度极低。这种高光情况下的车牌字符边缘信息几乎丢失直接用原图识别模型会非常挣扎。我实测有效的预处理组合是先转灰度图再做CLAHE对比度受限的自适应直方图均衡化。CLAHE和普通直方图均衡化的区别在于它是分块处理的避免全图均衡化对局部亮度的过度拉伸。def preprocess_plate(plate_bgr): # 转灰度 gray cv2.cvtColor(plate_bgr, cv2.COLOR_BGR2GRAY) # CLAHEclipLimit控制对比度限制tileGridSize控制分块大小 clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) equalized clahe.apply(gray) return equalized参数上我调试过一些组合clipLimit在1.5到3之间对车牌识别效果比较稳定太大容易放大噪声太小起不到增强作用。tileGridSize一般8x8就够车牌本身尺寸不大分块太多反而会出现块状伪影。夜间场景的处理思路不同。夜间车牌经常处于车灯直射或路灯暗光下图像整体噪声大、细节少。这时可以先做去噪双边滤波或NLM再适度提高亮度。另外暗光环境下用红外摄像头是另一个维度的事——很多收费站和停车场道闸直接用红外补光设备因为牌照表面是反光材料在特定角度的红外光下字符会异常清晰这种硬件方案能从源头上简化软件问题。5.2 运动模糊与图像去模糊车辆行驶中拍摄的视频帧多多少少会有运动模糊。运动模糊的成因是曝光时间内物体在传感器上产生了位移相当于图像在某个方向上被卷积了一个一维核。理论上可以用维纳滤波或Richardson-Lucy算法做去卷积但实际效果取决于模糊核估计的准确性而且计算开销大在实时视频流里很难落地。工程上我更推荐两个轻量替代方案从视频流里选帧如果车牌在连续多帧里都出现挑清晰度最高的一帧再识别。清晰度的度量可以用Laplacian算子的方差方差越大代表边缘越锐利图像越清晰。轻微锐化对图像做unsharp mask增强字符边缘对比让模型更容易提取特征。锐化幅度要控制过头会出现光晕状伪影。其实在真实系统中“选帧 多帧投票”是提升识别率性价比最高的方式。单帧准确率95%时连续抽5帧至少3帧识别一致的概率超过99%而且计算量没有实质增加。这个思路在道闸、停车场场景里非常实用。5.3 低分辨率图像的超分处理如果摄像头安装位置离车道较远车牌在画面里的像素宽度可能不到100像素人眼都看得费劲。字符识别模型在这种分辨率下表现很差可以尝试超分辨率重建。轻量级的超分方案比如ESPCNEfficient Sub-Pixel Convolutional Neural Network速度够快能在边缘设备上实时跑。不过要提醒的是超分不是万能的它只是放大图像并“脑补”细节如果原图本身已经模糊到字符轮廓断裂超分也难以恢复语义信息。在硬件允许的情况下优先考虑调整摄像头焦距、安装位置从源头保证车牌成像像素数远比后期算法修补更可靠。6. 端到端系统搭建代码框架与性能调优笔记6.1 完整推理流水线的代码结构把前面所有模块串成一个完整系统我习惯用类来封装方便后续维护和测试class PlateRecognizer: def __init__(self, detector_weights, recognizer_weights): self.detector self._load_detector(detector_weights) self.recognizer self._load_recognizer(recognizer_weights) def detect(self, image_bgr): # 返回候选车牌框列表 [(x1, y1, x2, y2, confidence)] results self.detector(image_bgr) boxes [] for det in results.xyxy[0].cpu().numpy(): x1, y1, x2, y2, conf, cls det if conf 0.5: boxes.append((int(x1), int(y1), int(x2), int(y2), conf)) return boxes def recognize(self, plate_region_bgr): # 预处理 透视校正 字符识别 corrected four_point_transform(plate_region_bgr, self._get_corners(plate_region_bgr)) processed preprocess_plate(corrected) text self.recognizer(processed) return validate_plate(text) def run(self, image_bgr): # 完整流程检测 - 校正 - 识别 - 校验 plates [] for box in self.detect(image_bgr): x1, y1, x2, y2, conf box region image_bgr[y1:y2, x1:x2] text self.recognize(region) if text: plates.append({box: (x1, y1, x2, y2), plate: text, confidence: conf}) return platesrun方法是核心入口输入一帧图像输出检测到的所有车牌及识别结果。多辆车同时出现在画面里时这个循环会遍历所有检测框分别做识别——这是停车场入口的刚需场景。6.2 性能优化从帧率瓶颈到实时推理如果目标是在普通PC上跑实时视频流25FPS以上性能优化是绕不开的。我实测过几个优化点的性价比排序图像缩放把输入图像缩放到合适尺寸再送给检测模型。YOLO系列输入分辨率对推理速度影响非常大从1280降到640帧率可能翻倍mAP损失却只有1到2个点。TensorRT加速NVIDIA GPU环境下把PyTorch模型导出为TensorRT引擎推理时间通常能压缩一半以上。导出时注意固定batch size动态shape会降低优化效果。检测与识别并行在视频流里车牌检测和字符识别可以放到不同的线程里用队列传递候选区域。识别一张车牌也只要几毫秒但并发处理能避免一些偶发的卡顿。跳帧策略对于实时视频流不需要每帧都做检测可以每隔一帧做一次检测识别阶段只对置信度高的新候选框执行减少重复计算。6.3 模型部署的注意事项真正把系统部署到现场后会发现训练时完全没遇到过的问题。比如摄像头视角固定但光照环境全天变化上午和下午的检测置信度差异巨大。我建议在项目早期就在测试机位采集全天候数据至少覆盖白天强光、黄昏逆光、夜间暗光三个时段。数据里如果连这种基本变体都没有模型泛化就是一句空话。硬件选型上小算力场景比如道闸一体机推荐用推理框架的INT8量化普通Jetson Nano上跑YOLOv5s LPRNet能稳定达到实时大算力场景如服务器集中处理多路视频流则直接用TensorRT加多路异步推理吞吐量优先。7. 实战踩坑记录六个最容易翻车的细节7.1 车牌边框和铆钉对字符识别的影响国内车牌的白色边框和防拆铆钉经常被一起抠进检测框里。CRNN这类序列模型对整张图做特征提取边框的竖线在特征图上会形成明显的垂直纹理严重干扰字符序列的特征分布。处理办法是透视校正后做一次内边框裁剪——车牌的字符区域不会紧贴外边缘通常向外边框方向收缩3到5个像素就能避开大部分干扰。如果车牌图像本身分辨率够高还可以用颜色分析去掉白色边框只保留蓝底区域。7.2 新能源车牌与蓝牌的尺寸差异新能源车牌是渐变绿色底、8位字符比蓝牌多一位且车牌的宽高比也不同。你的系统如果只按蓝牌的数据训练碰到绿色车牌时检测可能漏掉识别也可能因为字符长度不匹配而报错。比较好的做法是检测和识别模型都混入新能源车牌数据同时在后处理里分别适配8位和7位两种长度。不要偷懒只做7位验证否则路上跑的绿牌车会直接让你的准确率掉一截。7.3 夜间反光导致的字符“消失”车牌材质本身有反光涂层夜间遇到车灯直射时字符区域可能白得一片人眼都看不清边上写了什么。我试过很多图像增强算法什么gamma变换、MSRCR、暗通道先验去雾效果都不如直接在硬件上安装偏振片、调整补光灯角度来得立竿见影。如果软件方案受限至少可以做一个简单的过曝检测——图像中高光像素占比超过阈值时主动丢弃该帧等下几帧再识别。7.4 多车牌场景的排队问题监控画面里同时出现两辆车时检测器可能只输出了一个框尤其当前后车距离很近时前车车牌和后车车牌可能被框进同一个矩形里。这个问题的本质是NMS非极大值抑制的参数设置和特征不够细粒度。我处理方法是在NMS阶段调低IoU阈值这样两个重叠的车牌框更不容易被合并成一个另一个思路是训练时多加入遮挡和相邻车辆的场景数据。7.5 识别结果的稳定性多帧投票机制道闸场景中车辆是动态驶过的每帧图像质量波动很大。单帧识别结果可能今天准、明天就不准因为光照和角度在变。我最终上线用的是多帧投票机制连续5帧中如果3帧以上识别结果一致就作为最终结果输出否则等待下一轮。这个机制同时也抑制了偶发误检准确率比单帧要提高不少。代价是延迟增加了几百毫秒但对道闸放行这种场景完全无感。7.6 字符分割方案在复杂环境下为何彻底失败我前面提到了传统“分割分类”方案在复杂环境下的脆弱性。这里再展开一下很多课程设计或开源项目仍然用这种方案因为在实验室环境拍几张正视角、光照均匀的照片投影法确实能完美分割。可一旦遇到倾斜、逆光、字符磨损分割线就开始漂移。与其花大量时间调分割参数不如直接换CRNN/LPRNet这类免分割方案。同样一批数据免分割方案的准确率通常高出5到10个百分点我在不止一个数据集上验证过这个结论。8. 数据、评估与持续迭代让识别系统越用越准8.1 数据增强怎么加才不会过拟合车牌识别场景的数据增强和通用图像分类不太一样。除了常规的旋转、缩放、色彩抖动更应该关注的是模拟摄像头成像特性的增强运动模糊、高斯噪声、透视畸变、光照变化、雨天水滴效果。我用imgaug库做增强时会给每个样本随机组合3到5种增强操作而不是每次只做单一变换。这样模型见到的“真实”的复杂情况更多泛化能力明显更好。8.2 评估指标别只盯着OCR准确率做系统交付时不要太迷信识别准确率这个单一指标。一个完整的车牌识别系统至少要盯三个指标检测的召回率有没有漏掉车牌、检测的精确率误检多不多、识别准确率检测到的车牌里识别对的占比。很多人在演示时只报识别准确率95%但没统计漏检率——如果100辆车里有5辆压根没检测到那实际效果就打了折扣。工程评估时建议按“端到端准确率”来算从整段视频或整张图出发系统最终给出的所有车牌中完全正确且没有遗漏的比例。8.3 从失败样本中持续迭代系统上线后把每天识别失败的样本存下来定期做bad case分析。我统计过真实场景下的失败案例排名靠前的原因往往是车辆速度过快导致运动模糊、车牌部分遮挡比如拖车钩、防护栏、极端的逆光角度。针对模糊类失败我尝试过GAN-based去模糊但推理速度太慢工程上不划算针对遮挡类最终是靠多帧信息融合解决的——前几帧能看到遮挡区域就用时间维度的信息补全。8.4 一个实用的数据集组织规范如果你决定自己收集数据微调模型数据目录建议这样组织plate_dataset/ ├── train/ │ ├── images/ │ ├── labels/ │ └── annotations.json ├── val/ │ ├── images/ │ └── labels/ └── test/ ├── images/ └── labels/标注格式取决于你选的检测框架。YOLO系列用txt文件自带的格式每个标注是“类别 x_center y_center width height”坐标都归一化到0到1之间。注意透视畸变的车牌不要标成轴对齐矩形标得过大——框得多一点问题不大但框得偏了会把旁边背景带进来拉低识别效果。标完后抽查一遍很有必要我自己就遇到过标注文件里标签错位导致训练完全乱套的情况。9. 开源模型与工具链速查能直接上手的资源清单如果你不想从零训练模型这条路最省时间。我整理过一份常用资源选型表这里直接列出来环节推荐方案说明车牌检测YOLOv5 / YOLOv8 微调开源权重多社区成熟部署资料齐全文本检测替代方案PP-OCR DetPP-LCNet对不规则文本鲁棒车牌可当作文本处理车牌识别LPRNet轻量级、专为车牌设计支持不定长序列综合OCR引擎PaddleOCR检测识别一体可配置文本检测和识别模型做端到端车牌识别数据标注LabelImg / X-AnyLabelingLabelImg轻量X-AnyLabeling支持半自动标注数据增强imgaug / albumentations图像增强库支持多种变换组合推理部署ONNX Runtime / TensorRTONNX方便跨平台TensorRT GPU加速明显车牌数据集CCPD中国城市停车场数据集脚本丰富包含多种复杂场景规模和标注质量比较理想特别解释一下CCPD它是一个公开的中国车牌数据集标注包含四角点坐标可以直接用于训练透视校正模型或者检测模型数据量有几万张基本覆盖了各种天气和光照条件。不过在复杂场景的版本多样性上不同子集的难易程度差异比较大选型时记得看子集说明。还有一个细节用PaddleOCR做车牌识别时它默认的文本识别模型是针对中英文文本的字典里包含大量通用中文字符对省份汉字这种受限字符集会浪费模型容量。建议在车牌数据集上微调它的识别头或者把输出字典裁剪成车牌专用字符集推理速度和准确率都能提升。10. 关于算法选型的一些个人体会做了这么多车牌识别项目我最大的感受是计算机视觉工程问题方案选型永远比调参更重要。检测阶段深度学习是必须的传统颜色方法只配做辅助校正阶段透视几何仍然是性价比之王深度学习在这个环节反而不是最优解识别阶段CRNN/LPRNet这类免分割方案完胜传统分割方案后处理阶段规则引擎和字典校验是最不可忽视的兜底手段。如果只让我用一句话概括复杂环境下车牌识别的核心那就是让每个环节都做它擅长的事然后用系统级的策略去抵消单点的误差。单帧不准就多帧投票单模型误检就加规则校验单图模糊就多帧优选。这套系统思维比追各种新鲜模型框架重要得多。最后再分享一个我在实际项目中的小技巧方案交付时把识别结果和中间每一帧的车牌裁剪图一起存下来留作日志。后面一旦出现问题回溯分析会非常省事——看到一个失败的框马上就能判断是检测环节丢了、校正环节歪了还是识别环节认错了。这个习惯帮我省了很多现场排查的时间。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进