ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

YOLO车牌识别实战:解决小目标漏检与字符粘连

YOLO车牌识别实战:解决小目标漏检与字符粘连 简介本资源是一套基于YOLO目标检测算法实现的完整车牌识别系统面向人工智能与计算机视觉方向的本科生、毕业设计及课程设计学习者解决智能交通场景中车牌实时定位与OCR识别的核心问题。压缩包共97个文件含25个Python源码如detect_train.py、read_plate.py、ocr_test.py等核心模块、50个pyc编译文件、5张JPG/PNG测试图像、3份Markdown文档含README说明、2个Dockerfile支持CPU/ARM64多平台部署及requirements.txt等配置文件整体体积仅5.12MB轻量易部署。已有140人学习下载资源结构清晰覆盖数据预处理、YOLO车牌检测、字符分割、CNN-OCR识别及后处理全流程并提供export.py模型导出、gradio/flask REST API接口脚本与docker一键运行方案兼顾工程落地与教学复现需求。1. 为什么用 YOLO 做车牌识别不是“能跑就行”而是要解决夜间模糊、小目标漏检、字符粘连这三座大山你手头有个.zip文件名字叫基于YOLO的车牌识别.zip——它不是教学Demo也不是PPT配套素材而是一套面向真实路口、停车场、高速卡口场景打磨过的轻量级落地方案。我去年在三个地市的智慧停车项目里反复迭代过同类结构YOLOv5s/v8n 为主干但真正起作用的从来不是模型本身而是车牌这个特殊目标带来的三重硬约束第一车牌区域只占整图0.3%~1.2%YOLO 默认的anchor尺寸如64×64根本框不住60×180的蓝牌第二夜间补光不均导致字符边缘发虚传统OCR直接失效必须靠检测识别联合优化第三两车并行时车牌紧贴、角度倾斜超±25°YOLO 的回归头容易把“粤B·A1234”和“粤B·A1235”框成一个大box。这套方案没用Transformer、没上多模态就靠YOLO系列里最稳的v5/v8分支配合车牌专属anchor聚类、字符级分割掩码引导、以及端到端可微分的字符对齐模块在RK3399嵌入式设备上做到92.7% mAP0.5测试集含雨雾/低照度/运动模糊样本。适合想快速验证算法可行性、又不愿被工业级SDK绑定的工程师——你不需要买臻识一体机也能在树莓派4B上跑通全流程。2. 从解压到推理用最小依赖跑通车牌检测字符识别双阶段流程2.1 解压后目录结构与核心文件功能定位拿到基于YOLO的车牌识别.zip后先解压观察结构别急着运行很多翻车源于没看清文件分工├── data/ # 数据相关含sample_img/测试图、classes.txt车牌类别、trainval/训练集划分 ├── models/ # 模型定义yolov5s_plate.yaml定制化backbonehead、yolov8n_plate.yamlv8适配版 ├── weights/ # 预训练权重yolov5s_plate.pt检测模型、crnn_lite.pth字符识别模型 ├── utils/ # 工具链plate_utils.py车牌矫正函数、char_recognizer.pyCRNN推理封装 ├── detect_plate.py # 主检测脚本调用YOLO检测框 裁剪 字符识别 ├── train.py # 训练入口支持v5/v8双后端 └── requirements.txt # 依赖清单torch1.13.1cu117, opencv-python4.8.0, easyocr2.7.0仅作对比基准注意weights/下的.pt文件是检测模型不是完整识别模型字符识别由独立的crnn_lite.pth承担。很多新手误以为YOLO能直接输出“粤B·A1234”其实YOLO只负责定位车牌区域xyxy坐标后续字符识别是另一套轻量CRNN网络完成的——这是该方案能兼顾速度与精度的关键设计。2.2 本地环境配置避开CUDA版本、OpenCV编译、PyTorch兼容性三连坑不要直接pip install -r requirements.txt按以下顺序执行实测在Ubuntu 20.04 RTX3060 / Windows10 GTX1660 环境下100%复现# 步骤1创建干净虚拟环境避免conda/pip混装冲突 python -m venv plate_env source plate_env/bin/activate # Linux/Mac # plate_env\Scripts\activate # Windows # 步骤2安装指定CUDA版本的PyTorchYOLOv5/v8对torch版本极其敏感 # 若显卡驱动470.82 → 用CUDA11.7 pip install torch1.13.1cu117 torchvision0.14.1cu117 --extra-index-url https://download.pytorch.org/whl/cu117 # 步骤3安装OpenCV必须用预编译版源码编译易触发ffmpeg冲突 pip install opencv-python4.8.0.76 # 步骤4安装YOLO官方依赖跳过ultralytics自动装torch我们已手动装好 pip install numpy1.23.5 pandas1.5.3 matplotlib3.7.1 # 步骤5安装字符识别依赖CRNN需torchvision但版本必须匹配 pip install torchtext0.14.1 # 注意torchtext 0.15会报错no module named torchtext.data.utils验证是否成功import torch print(torch.__version__, torch.cuda.is_available()) # 应输出 1.13.1cu117 True import cv2 print(cv2.__version__) # 应输出 4.8.02.3 单张图片推理用detect_plate.py跑通端到端流程进入项目根目录执行python detect_plate.py \ --source data/sample_img/001.jpg \ --weights weights/yolov5s_plate.pt \ --img-size 640 \ --conf 0.4 \ --iou 0.5 \ --rec-weights weights/crnn_lite.pth参数说明--source输入路径支持单图/文件夹/视频如--source data/sample_img/会批量处理整个文件夹--weights检测模型权重必须用yolov5s_plate.pt而非通用yolov5s.pt因前者anchor已针对车牌尺寸重聚类--img-sizeYOLO输入分辨率640是平衡速度与精度的甜点值实测512易漏小车牌768显存溢出--conf检测置信度阈值0.4是经验值低于0.3误检飙升高于0.5漏检率12%--iouNMS阈值0.5防止相邻车牌合并如并行车道--rec-weights字符识别模型路径不可省略否则只输出框坐标不输出文字成功运行后会在runs/detect/exp/下生成带标注框和识别结果的图片右下角会显示粤B·A1234:0.92——0.92是CRNN输出的字符序列置信度非YOLO的框置信度该值由CTC Loss解码后取平均logit计算得出。3. 训练自己的车牌数据集从VOC转YOLO格式、anchor重聚类到loss权重调优3.1 数据准备为什么必须用VOC格式原始标注而不是直接标YOLO txt该方案要求训练数据为Pascal VOC 格式XML标注而非YOLO原生txt。原因有三XML中bndbox包含精确像素坐标可无损转换为YOLO格式而人工标txt易出现四舍五入误差导致小车牌框偏移超3像素VOC的name字段支持多类别如“blue_plate”、“green_plate”、“yellow_plate”方便后续扩展新能源车牌utils/voc2yolo.py脚本内置车牌长宽比校验逻辑——若XML中width/height 2.5自动触发警告正常蓝牌长宽比≈3.3低于2.5大概率是标注错误或遮挡。转换命令python utils/voc2yolo.py \ --voc-root data/VOCdevkit/ \ --year 2023 \ --output-dir data/plate_yolo/生成目录结构data/plate_yolo/ ├── images/ # 所有jpg重命名规则000001.jpg, 000002.jpg... ├── labels/ # 对应txt每行格式class_id center_x center_y width height归一化 └── trainval.txt # 列出所有训练验证图片名不含扩展名3.2 Anchor重聚类用k-means算出最适合车牌的3组尺寸YOLO默认anchor如v5的[10,13, 16,30, 33,23]是为COCO通用目标设计的车牌宽高比集中于2.8~3.5必须重聚类。执行python utils/anchor_kmeans.py \ --label-dir data/plate_yolo/labels/ \ --cluster-num 3 \ --size 640输出示例KMeans for 3 clusters: Anchor 1: [28, 82] # 对应小车牌远距离/倾斜 Anchor 2: [42, 115] # 对应标准蓝牌主检测区 Anchor 3: [61, 172] # 对应大车牌新能源绿牌/黄牌将结果填入models/yolov5s_plate.yaml的anchors:字段anchors: - [28,82, 42,115, 61,172]血泪经验聚类前务必确认label尺寸单位是像素非归一化值否则k-means结果全乱。anchor_kmeans.py内部会自动读取txt中第4、5列width/height并乘以640还原为像素尺寸。3.3 训练启动与关键参数调优为什么batch_size16是树莓派4B的极限使用train.py启动训练支持v5/v8双后端python train.py \ --data data/plate_yolo/plate.yaml \ --cfg models/yolov5s_plate.yaml \ --weights weights/yolov5s.pt \ # 加载COCO预训练权重作为起点 --epochs 150 \ --batch-size 16 \ --img 640 \ --name yolov5s_plate_custom \ --cache # 启用内存缓存加速读取首次运行稍慢后续快3倍plate.yaml内容必须包含train: ../plate_yolo/trainval.txt val: ../plate_yolo/trainval.txt # 实际应拆分此处为简化示意 nc: 1 names: [plate]关键参数说明--batch-size 16在RTX3060上稳定树莓派4B需降为4改--batch-size 4 --workers 2--cache强制启用缓存否则SD卡IO成为瓶颈实测加载速度从12fps→38fps--epochs 150车牌数据量少通常5k图早停策略设为patience30避免过拟合--weights必须用COCO预训练权重从零训练收敛极慢且mAP难超70%。训练过程监控重点Box Loss应在50epoch后稳定在0.05以下高于0.08说明anchor不匹配或标注噪声大Obj Loss在0.15~0.25区间波动属正常车牌属于稀疏目标obj loss天然偏高Class Loss必须趋近于0单类别任务不为0说明类别混淆。4. 避坑指南YOLO车牌识别项目里最常踩的5个深坑及现场急救方案4.1 现象检测框严重偏移框在车牌上方10像素但confidence显示0.95原因YOLO的xywh回归目标未对车牌做中心点偏移校正。原始YOLO预测的是anchor中心到真实框中心的偏移量而车牌顶部常有铆钉/反光条导致标注框top-y偏小网络学会“向上偏移”来补偿。解决在models/common.py的Detect层后插入偏移补偿# 在forward()函数末尾添加 pred[..., 1] 8.0 / self.stride[i] # y方向强制下移8像素按stride缩放实测对蓝牌偏移修正率达92%且不影响其他目标检测。4.2 现象字符识别结果全是“川A·12345”中的“川”字重复5次如“川川川川川”原因CRNN的CTC解码器在低质量图像上无法区分字符间隔将连续灰度区域误判为同一字符。解决在utils/char_recognizer.py中增强预处理def preprocess(img): img cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) img cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) # 新增垂直投影切分强制分离字符 proj np.sum(img, axis0) peaks find_peaks(proj, height200)[0] # 找到字符中心位置 if len(peaks) 7: # 7字符车牌含· img crop_by_peaks(img, peaks) # 按峰值切分为7块再送入CRNN return img4.3 现象训练时Loss突然爆炸Box Loss从0.05跳到12.5GPU显存瞬间占满原因--batch-size设置超过显存承载极限PyTorch梯度累积失败触发NaN梯度传播。解决立即中断训练修改train.py中梯度裁剪# 在optimizer.step()前添加 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm10.0)并重启训练同时降低--batch-sizeRTX3060建议≤16GTX1060建议≤8。4.4 现象导出ONNX模型后推理结果全黑输出tensor全为0原因YOLOv5的models/yolo.py中存在torch.where()操作ONNX导出时未正确处理bool类型张量。解决替换models/yolo.py中所有torch.where(cond, a, b)为# 原代码 x torch.where(condition, a, b) # 改为 x condition.float() * a (1 - condition.float()) * b此写法兼容ONNX 1.10实测导出成功率100%。4.5 现象树莓派部署后FPS从12暴跌至2.3top显示CPU占用98%原因OpenCV默认使用libjpeg-turbo解码但在ARM平台未启用硬件加速纯CPU解码4K图耗时230ms。解决编译OpenCV时启用-D WITH_V4LON -D WITH_LIBV4LON或改用更轻量的解码方案# 替换cv2.imread为 def fast_imread(path): with open(path, rb) as f: arr np.frombuffer(f.read(), np.uint8) return cv2.imdecode(arr, cv2.IMREAD_COLOR)实测树莓派4B上解码耗时从230ms→32msFPS恢复至10.5。5. 进阶技巧用混淆矩阵反向定位漏检根源以及如何让YOLO在雨雾天保持90%召回5.1 构建车牌专用混淆矩阵不只是看mAP更要揪出“谁在漏检”YOLO原生val.py输出的混淆矩阵confusion matrix是针对COCO 80类的对单类别车牌毫无意义。必须自定义统计逻辑# 在val.py中新增函数 def plate_confusion_matrix(preds, targets, iou_thres0.5): tp, fp, fn 0, 0, 0 for i, pred in enumerate(preds): gt targets[i] if len(gt) 0: # 无真实框所有pred都是fp fp len(pred) continue if len(pred) 0: # 无预测框所有gt都是fn fn len(gt) continue # 计算IoU矩阵 iou_mat box_iou(pred[:, :4], gt[:, :4]) # pred x gt matched torch.zeros(len(gt), dtypetorch.bool) for p in range(len(pred)): if iou_mat[p].max() iou_thres: gt_idx iou_mat[p].argmax() if not matched[gt_idx]: tp 1 matched[gt_idx] True else: fp 1 # 重复匹配同一gt else: fp 1 fn (~matched).sum().item() return tp, fp, fn运行验证后得到三元组(tp842, fp93, fn58)计算得召回率 TP/(TPFN) 842/(84258) 93.5%精确率 TP/(TPFP) 842/(84293) 90.1%关键洞察fn58中41个来自雨雾天气样本占70.7%——说明模型对低对比度场景鲁棒性不足需针对性增强。5.2 雨雾天增强策略不用GAN用物理模型驱动的数据合成与其用CycleGAN生成雾图易引入伪影不如用大气散射模型Atmospheric Scattering Model合成def add_fog(img, t0.7, A0.95): # t: 透射率A: 大气光值晴天0.95浓雾0.75 dark np.min(img, axis2) kernel np.ones((15,15), np.uint8) dark cv2.erode(dark, kernel) dark cv2.GaussianBlur(dark, (15,15), 0) transmission 1 - t * dark.astype(np.float32) / 255.0 fogged img * transmission[..., None] A * (1 - transmission[..., None]) return np.clip(fogged, 0, 255).astype(np.uint8) # 在dataloader中随机应用 if random.random() 0.3: # 30%概率加雾 img add_fog(img, trandom.uniform(0.5, 0.85))实测在雨雾测试集上召回率从72.3%提升至91.6%且不增加推理耗时合成在CPU完成GPU只负责前向。5.3 字符识别置信度过滤为什么直接删掉0.7的结果反而降低准确率CRNN输出的字符置信度如粤B·A1234:0.68不是整体序列置信度而是每个字符logit的softmax均值。粗暴过滤会导致“粤B·A1234”中若“B”置信度0.65被删剩下“粤·A1234”无法解析实际应采用动态阈值对7字符序列允许最多2个字符0.7但整体字符串需满足len(valid_chars) 5。在detect_plate.py中修改识别后处理def filter_recognition(text, confs): valid_chars [] for c, conf in zip(text, confs): if conf 0.6 or c in ·: # 允许符号·无条件保留 valid_chars.append(c) if len(valid_chars) 5: # 强制至少5字符才接受 return None return .join(valid_chars)上线后端到端准确率车牌字符串完全匹配从83.2%提升至89.7%漏检率下降4.1个百分点。我坚持在每个新项目里做三件事用plate_confusion_matrix查漏而不是只看mAP雨雾增强必用物理模型拒绝GAN幻觉字符过滤永远留“后悔药”——宁可多返回一个模糊结果也不让系统沉默。这套方法跑通了17个地市的路侧设备最老的一台还在用树莓派3B跑v5s。希望帮到你。本文还有配套的精品资源点击获取
返回列表