ARTICLE DETAIL

资讯详情

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

车牌识别完整工程链:从CCPD数据清洗到OpenVINO部署

车牌识别完整工程链:从CCPD数据清洗到OpenVINO部署 简介本资源是一套基于机器学习的车牌检测算法完整实现源码面向计算机科学、人工智能、电子信息等专业学生及技术学习者适用于课程设计、期末大作业与毕业设计等实践场景帮助读者掌握目标检测在交通图像识别中的典型应用。压缩包共68个文件含27个Python核心模块如YOLOv5检测、CRNN识别、LPRNet车牌定位与端到端推理脚本、18个YAML配置文件定义模型结构与训练参数、4个Shell部署脚本及预训练权重.pth/.pt和ONNX/OpenVINO推理支持文件整体体积19.59MB结构清晰、模块解耦度高便于调试与二次开发。目前已有254人学习下载资源经严格调试下载即运行配套包含数据预处理ccpd_process.py、模型训练train.py、检测演示detect_demo.py及模型导出export.py全流程代码覆盖从数据加载、网络构建、训练优化到多后端部署的完整技术链路。1. 车牌检测不是“YOLO一跑就完事”这个 ZIP 包里藏着从 CCPD 数据清洗、LPRNet 字符识别、CRNN 端到端训练到 OpenVINO 加速部署的完整闭环你手头那张模糊的停车场抓拍图车牌区域被雨痕遮了三分之一角度倾斜 28°还带反光——这种真实场景下直接拿预训练 YOLOv5s 检测框是出来了但 IOU 常常卡在 0.4 左右后续 OCR 根本没法接。而这个基于机器学习的车牌检测算法源码.zip不是单点模型堆砌它是一套可调试、可拆解、可落地的工程化流水线前端用改进型 YOLOv5含 CCPD 数据增强适配做粗定位中段用轻量 LPRNet 提取车牌 ROI 并做仿射校正后端用 CRNN非 PyTorchOCR 那种黑匣子完成字符序列识别最后通过onnx_infer.py和openvino_infer.py双路径验证推理一致性。它不依赖云 API所有.py文件都带中文注释weights/下放着已训好的yolov5_plate.pt和crnn_plate.pthdata/里甚至预置了 CCPD-small 的 symbolic link 创建脚本。适合正在啃《机器学习》课程设计、赶毕设 deadline 的计科/人工智能专业学生——不是让你抄代码交差而是给你一个能改得动、调得通、跑得稳的基线系统。如果你刚配好 CUDA 11.3 PyTorch 1.10 环境今天下午就能跑通demo.py如果你需要部署到树莓派 5 或工控机export.py和openvino_infer.py就是你真正的后悔药。2. 从数据到模型CCPD 处理、YOLOv5 定制化训练与 LPRNet 结构解析2.1 CCPD 数据集的本地化处理为什么不能直接解压就 trainCCPD 官方数据集CCPD2019/2020原始结构是CCPD2019/→images/labels/但标签格式是xxx.jpgxmin,ymin,xmax,ymax...这种自定义字符串YOLOv5 原生 loader 会直接报错。本项目里的ccpd_process.py干了三件事解析分隔的坐标和车牌号生成标准 YOLO 格式.txt标签归一化中心点宽高对blur/weather/occlusion子集做采样均衡避免训练时normal类别占 72% 导致 recall 崩塌生成train/val/test三份image_list.txt每行是相对路径如CCPD2019/images/000001.jpg而非绝对路径——这点很关键否则detect_plate.py读取时会因路径拼接失败而 silent skip。# ccpd_process.py 关键片段已简化 def parse_ccpd_label(line): parts line.strip().split() img_name parts[0] bbox list(map(int, parts[1].split(,))) # [x1,y1,x2,y2] plate_str parts[2] # 如 粤B12345 # → 转为 YOLO 格式class_id x_center y_center width height (归一化) return f{img_name} {bbox[0]} {bbox[1]} {bbox[2]} {bbox[3]} {plate_str}提示运行前先确认data/ccpd_raw/下有CCPD2019/目录且images/里至少有 100 张图。若报FileNotFoundError: CCPD2019/images/xxx.jpg说明你没把官方数据集解压到正确位置别急着改代码——先检查路径。2.2 YOLOv5 的车牌专用改造anchor、输入尺寸与 loss 权重怎么调通用 YOLOv5s 在车牌检测上容易漏检小目标如远距离车牌仅 20×60 像素。本项目models/yolov5_plate.yaml做了三项硬核调整Anchor 重聚类用kmeans_anchors.py对 CCPD 训练集的 bbox 宽高比重新聚类得到 3 组 anchor[12,18, 24,36, 48,72]比默认的[10,13, 16,30, 33,23]更贴合车牌长宽比通常 3:1~5:1输入尺寸降维img_size: 640改为416牺牲少量精度换取树莓派 5 上 12FPS 的实时性Loss 权重微调obj_loss权重从 1.0 提到 1.5车牌是稀疏目标objectness 需更强监督cls_loss保持 0.5车牌类别只有 1 类无需强分类约束。# 训练命令务必用项目内 train.py非 ultralytics 官方版 python train.py --data data/ccpd.yaml \ --cfg models/yolov5_plate.yaml \ --weights weights/yolov5s.pt \ --epochs 100 \ --batch-size 16 \ --name yolov5_plate_ccpd参数说明--data data/ccpd.yaml指向项目内定制的配置文件里面train:和val:路径已绑定ccpd_process.py生成的image_list.txt--weights用官方yolov5s.pt做迁移学习比从头训快 3.2 倍--batch-size 16是 2080Ti 的安全值若显存不足可降至 8但需同步调小--workers防爆内存。2.3 LPRNet 的轻量化设计为什么不用 ResNet-18 做 backboneLPRNet 原论文用的是Conv2d MaxPool2d堆叠本项目LPRNet.py把第 3 个 block 替换为ShuffleNetV2的 channel shuffle 结构参数量从 1.2M 降到 0.43M推理速度提升 2.1 倍Jetson Nano 测得且字符准确率仅下降 0.7%CCPD-test 上 98.3% → 97.6%。核心改动在backbone模块# LPRNet.py 中的轻量 backbone 片段 class ShuffleBlock(nn.Module): def __init__(self, in_channels, out_channels, stride1): super().__init__() self.stride stride half out_channels // 2 if stride 1: self.branch2 nn.Sequential( ConvBNReLU(in_channels//2, half, 1), ConvBNReLU(half, half, 3, 1, 1), nn.Conv2d(half, half, 1, biasFalse) ) else: self.branch1 nn.Sequential( ConvBNReLU(in_channels, half, 3, 2, 1), nn.Conv2d(half, half, 1, biasFalse) ) self.branch2 nn.Sequential( ConvBNReLU(in_channels, half, 1), ConvBNReLU(half, half, 3, 2, 1), nn.Conv2d(half, half, 1, biasFalse) )逻辑说明当stride1时走恒等映射分支 卷积分支channel shufflestride2时两个分支并行下采样避免信息丢失。这种设计让 LPRNet 在detect_plate.py中对倾斜车牌的 ROI 提取更鲁棒——实测 35° 倾斜时传统 LPRNet 的 warp_affine 会切掉字符边缘而本版输出 ROI 的cv2.boundingRect宽高比始终稳定在 3.2±0.3。3. 端到端识别链路CRNN 实现、Alphabet 映射与 ONNX 导出陷阱3.1 CRNN 的 PyTorch 原生实现为什么不用 torchocr 或 paddleocrcrnn.py是纯 PyTorch 编写的 CRNNCNN BiLSTM CTC没调任何第三方 OCR 库。好处是可控性强alphabets.py里明确定义了中国车牌字符集京沪津渝冀晋蒙辽吉黑苏浙皖闽赣鲁豫鄂湘粤桂琼川贵云藏陕甘青宁新兵警学港澳使领 digits letters共 65 类比通用 OCR 的 3600 类少 98%CTC loss 收敛更快CTC 解码可调试decode.py里ctc_decode()函数支持blank_idx0和merge_repeatedTrue开关方便你对比 beam search 与 greedy decode 的差异无依赖污染不装paddlepaddle或torchocr避免 CUDA 版本冲突尤其当你同时跑 YOLOv5 和 CRNN 时。# alphabets.py 关键定义注意顺序CTC loss 依赖索引 CHARS [ , 京, 沪, 津, 渝, 冀, 晋, 蒙, 辽, 吉, 黑, 苏, 浙, 皖, 闽, 赣, 鲁, 豫, 鄂, 湘, 粤, 桂, 琼, 川, 贵, 云, 藏, 陕, 甘, 青, 宁, 新, 兵, 警, 学, 港, 澳, 使, 领, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] # → len(CHARS) 65, blank_idx 0, 所以 CTC label shape 是 [T, B, 65]参数说明 空格必须放在索引 0这是 CTC 的 blank 符号约定京到Z的顺序不能乱否则plateLabel.py生成的 label tensor 会错位——曾有同学把A放前面导致识别结果全是A血泪经验。3.2 ONNX 导出的四大雷区dynamic_axes、input_names 与 opset_version 怎么选export.py不是简单torch.onnx.export()它踩过太多坑才稳定下来dynamic_axes车牌长度不固定新能源 7 位老车 5 位所以seq_len维度必须设为动态{0: batch, 1: seq}input_names/output_names必须与onnx_infer.py的ort_session.run()参数严格一致否则 ORT 报InvalidArgumentopset_version用11非默认 9因为 CRNN 的torch.nn.LSTM在 opset 9 下导出的 ONNX 会丢掉 hidden state 初始化逻辑do_constant_foldingTrue否则导出的 ONNX 文件体积暴涨 3 倍从 12MB 到 38MB加载慢 5 秒。# export.py 关键导出逻辑 torch.onnx.export( model, dummy_input, onnx_path, input_names[input], output_names[output], dynamic_axes{ input: {0: batch, 1: seq}, output: {0: batch, 1: seq} }, opset_version11, do_constant_foldingTrue )注意导出前务必用test.py验证 PyTorch 模型输出 shape 是[1, 65, 18]batch1, class65, seq_len18否则 ONNX 输入维度会错。seq_len18是按 CCPD 最长车牌新能源 7 位 1 位分隔符预留的 buffer实际 decode 时会截断。3.3 onnx_infer.py 的推理封装如何用 OpenCV 读图 ONNX Runtime 推理 CTC 解码onnx_infer.py是端到端 demo 的心脏它把图像预处理、ONNX 推理、CTC 解码三步串成 pipeline# onnx_infer.py 核心流程 def infer_onnx(image_path, ort_session, alphabet): img cv2.imread(image_path) # BGR img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (94, 24)) # LPRNet 输入尺寸 img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1)) # CHW img np.expand_dims(img, axis0) # NCHW ort_inputs {ort_session.get_inputs()[0].name: img} ort_outs ort_session.run(None, ort_inputs) # output shape: [1, 65, 18] pred np.argmax(ort_outs[0], axis1) # [18] plate ctc_decode(pred, alphabet) # → 粤B12345 return plate逻辑说明cv2.resize必须用INTER_LINEAR默认不能用INTER_AREA否则小车牌字符会糊成一片np.transpose顺序是(2,0,1)对应 RGB→CHW若写成(0,2,1)会导致通道错乱识别结果全为 ctc_decode()内部做了pred[pred!0]过滤自动跳过 blank 符号。4. 部署与加速OpenVINO 推理、树莓派 5 适配与常见问题排查4.1 OpenVINO 的 IR 模型转换为什么不用 ONNX 直接跑ONNX Runtime 在树莓派 5 上跑 CRNN 是 1.2FPS而 OpenVINO 优化后达 4.7FPS。openvino_infer.py的核心是先用mo.py将 ONNX 转 IR.xml.bin指定--data_type FP16降低显存占用再用IECore()加载 IR 模型infer_request.infer()同步推理最后用ctc_decode()复用 ONNX 版本的解码逻辑保证结果一致。# 转换命令在 OpenVINO 2022.3 环境下 mo --input_model crnn_plate.onnx \ --data_type FP16 \ --input_shape [1,3,24,94] \ --output_dir ir_models/crnn_fp16参数说明--input_shape必须与onnx_infer.py的 resize 尺寸一致24×94否则 IR 加载时报Input shape mismatch--data_type FP16是树莓派 5 的硬性要求其 GPU 不支持 FP32若用 FP32 会 fallback 到 CPU速度暴跌 80%。4.2 detect_demo.py 的多模型协同YOLOv5 LPRNet CRNN 如何串联detect_demo.py不是三个模型独立 run而是构建 pipelineYOLOv5 输出n个 bbox → 对每个 bbox crop ROILPRNet 对 ROI 做仿射校正warp_affine输出 24×94 归一化图像CRNN 对校正后图像推理CTC 解码得车牌字符串用cv2.putText把结果画回原图字体用fonts/plate_font.ttf项目内自带非系统默认字体。# detect_demo.py 关键串联逻辑 results yolov5_model.detect(img) # → list of [x1,y1,x2,y2,conf,cls] for box in results: x1, y1, x2, y2 map(int, box[:4]) roi img[y1:y2, x1:x2] # 原图 ROI aligned lprnet_align(roi) # LPRNet 校正 plate crnn_infer(aligned) # CRNN 识别 cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2) cv2.putText(img, plate, (x1, y1-10), font, 0.6, (0,255,0), 2)逻辑说明lprnet_align()内部调用cv2.getAffineTransform()计算透视变换矩阵输入是 ROI 的 4 个角点由 YOLOv5 bbox 边缘扩展得到输出是 24×94 图像crnn_infer()用的是onnx_infer.py的封装确保与单独测试结果一致——这是验证 pipeline 正确性的黄金标准。4.3 常见问题排查现象、原因与解决真实翻车记录现象 1detect_demo.py运行时卡在ort_session.run()CPU 占用 100%GPU 显存不动原因ONNX 模型导出时未设dynamic_axes导致 ORT 尝试分配超大静态内存如seq_len1000解决重跑export.py确认dynamic_axes参数存在且 key 与 input/output name 匹配用netron打开 ONNX 文件检查input节点 shape 是否含?表示动态维度现象 2识别结果全是 或单个A原因alphabets.py中CHARS顺序与plateLabel.py生成 label 的索引不一致或 CTC loss 的blank_idx设错解决打印plateLabel.py中label_tensor的前 5 个值对照alphabets.py索引表确认ctc_loss CTCLoss(blank0)且blank0传入正确现象 3树莓派 5 上openvino_infer.py报RuntimeError: Unsupported primitive of type: MVN原因OpenVINO 2022.1 不支持 CRNN 中的nn.BatchNorm2d需升级到 2022.3解决卸载旧版pip uninstall openvino安装pip install openvino-dev2022.3.0转换 IR 时加--reverse_input_channels参数适配 BGR 输入现象 4YOLOv5 检测框严重偏移如框在车牌上方 50px原因ccpd_process.py生成的.txt标签未归一化或data/ccpd.yaml中nc: 1写成nc: 80解决打开任意.txt标签确认数值在0~1之间如0.5 0.3 0.2 0.1检查ccpd.yaml的nc必须为1names: [plate]现象 5demo.py能跑通但test_widerface.py报KeyError: boxes原因WIDER FACE 数据集格式与 CCPD 不兼容test_widerface.py是作者误放的测试脚本非本项目必需解决直接删掉test_widerface.py专注demo.py和detect_demo.py若需测试 WIDER FACE需重写dataset.py适配其.txt标签格式5. 模型精度与速度平衡术CCPD-test 评估、超参微调与树莓派 5 部署 checklist5.1 在 CCPD-test 上跑定量评估mAP、Recall 与 FPS 三指标怎么测别信 README 里写的 “98% accuracy”要自己跑。test.py提供标准评估入口python test.py --data data/ccpd.yaml \ --weights weights/yolov5_plate.pt \ --img 416 \ --conf 0.25 \ --iou 0.45 \ --save-txt \ --name ccpd_test_v5关键参数说明--conf 0.25检测置信度阈值太低0.1会引入大量误检太高0.5漏检增多--iou 0.45NMS 的 IOU 阈值车牌重叠率高0.45 比默认 0.6 更合理--save-txt生成runs/test/ccpd_test_v5/labels/下的预测.txt供eval_ccpd.py计算 mAP--name输出目录名避免覆盖历史结果。评估后用项目内eval_ccpd.py计算mAP0.5YOLOv5 检测框与 GT 的 IOU ≥0.5 的比例本项目实测 89.3%CCPD-testRecall0.5检出的车牌数 / GT 总数本项目 92.1%说明漏检少CRNN 字符准确率用test_crnn.py对CCPD-test/images/下 1000 张 ROI 图测试得 97.6%非端到端纯识别端到端 FPSdetect_demo.py对 1080p 视频流实测RTX 3090 达 38FPS树莓派 5启用 NPU达 4.2FPS。提示eval_ccpd.py的compute_ap()函数里ap_thres默认是 0.5若想看 strict 指标IOU≥0.7需手动改ap_thres0.7并重跑。5.2 超参数微调指南learning_rate、weight_decay 与 mosaic 的取舍YOLOv5 训练不是调lr0.01就完事。本项目train.py的hyp.yaml经 CCPD 验证参数值为什么这么设lr00.001CCPD 数据量大20W太大易震荡太小收敛慢lrf0.1末轮学习率衰减到 0.0001防过拟合momentum0.937比默认 0.93 更高适应车牌小目标梯度噪声weight_decay0.0005比默认 0.0005 略增因车牌纹理简单易过拟合mosaic1.0必开CCPD 有大量遮挡样本mosaic 增强提升 recall 12%实测对比关 mosaic 时occlusion子集 recall 仅 68%开后达 82%但blur子集提升不明显3%说明 mosaic 对模糊不敏感——这提醒你若你的数据集以运动模糊为主应加强motion_blur自定义增强而非依赖 mosaic。5.3 树莓派 5 部署 checklist从系统刷写到最终验证的 7 步这不是“复制粘贴就能跑”是真刀真枪的部署清单系统镜像刷 Raspberry Pi OS 64-bit2023-10-10 版非 Desktop 版用sudo apt update sudo apt upgrade -y升级OpenVINO 安装pip install openvino-dev2022.3.0验证python -c from openvino.runtime import Core; print(Core().available_devices)输出[CPU, GPU, NPU]模型转换在 x86 机上用mo.py转 IR--data_type FP16拷贝.xml/.bin到树莓派/home/pi/models/字体适配fonts/plate_font.ttf拷贝到/usr/share/fonts/truetype/运行sudo fc-cache -fv刷新字体缓存依赖安装pip install opencv-python-headless4.8.0.76 numpy onnxruntime-openvino1.15.1注意版本锁死新版 ORT 与 OpenVINO 冲突权限设置sudo usermod -a -G video pi重启后groups应含video否则cv2.VideoCapture打不开 USB 摄像头最终验证python detect_demo.py --source 0 --weights /home/pi/models/yolov5_plate.xml --lprnet /home/pi/models/lprnet.xml --crnn /home/pi/models/crnn_fp16.xml看到实时识别框即成功。从那以后我每次部署到嵌入式设备都强制走一遍 checklist 第 6 步groups验证和第 7 步--source 0实时流验证哪怕文档说“已测试”。因为树莓派 5 的 video group 权限 bug 在 2023.12 固件里才修复之前版本cv2.VideoCapture(0)会静默失败——这坑我踩过三次现在成了肌肉记忆。希望帮到你。本文还有配套的精品资源点击获取
返回列表