ARTICLE DETAIL

资讯详情

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

基于YOLOv7的12种中文车牌识别系统:从训练到部署全流程

基于YOLOv7的12种中文车牌识别系统:从训练到部署全流程 简介本资源为基于YOLOv7的车牌识别算法系统面向人工智能、通信工程、自动化等专业的在校学生、教师及企业开发者可用于毕业设计、课程设计、项目立项演示或算法进阶学习。系统支持单行蓝牌、单行黄牌、新能源车牌、警用车牌、教练车牌、武警车牌、使馆车牌、港澳车牌、农用车牌、民航车牌等12种中文车牌类型覆盖常见与特殊场景。压缩包共100个文件约24.05MB包含38个Python源码、13个YAML配置、5个Shell脚本、4个C与1个CUDA文件以及2个pth与2个pt权重文件另附jpg、png效果图、md说明与docx、pptx文档便于理解模型训练、推理与部署流程。该项目为个人高分项目源码已通过导师指导与答辩评审代码经测试运行成功。已有123人学习下载适合在此基础上修改扩展实现其他识别功能或直接用于毕设、课设与作业。1. 12种中文车牌一把梭yolov7车牌识别系统到底能落地到什么程度停车场出入口的相机拍下一张图蓝牌、黄牌、绿牌、白牌混在一起甚至还有双层黄牌和港澳出入境车牌传统颜色分割加边缘检测的方案在这种场景下基本活不过三天。基于yolov7车牌识别算法系统支持12种中文车牌类型文档源码这个标题背后要解决的就是这个具体问题用单阶段目标检测网络把车牌位置框出来再走一遍字符识别最终输出结构化车牌号。它适合两类人——一类是想快速跑通一个能识别多类型中文车牌的demo的开发者另一类是想把车牌识别集成到自有业务系统里的工程团队。12种类型覆盖了蓝牌、黄牌单双层、绿牌新能源、白牌警用、黑牌使领馆、港澳牌等常见类别这意味着模型需要处理的类间差异比单一蓝牌识别大得多训练数据的标注策略和anchor设计都会直接影响最终精度。2. yolov7车牌检测的选型逻辑与数据准备2.1 为什么是yolov7而不是yolov5或v8yolov7在2022年发布时在5FPS到160FPS范围内COCO精度全面超过同级别检测器核心改进是ELAN高效层聚合网络和auxiliary head辅助训练策略。放到车牌检测这个具体任务上yolov7的优势体现在三个地方第一ELAN结构对多尺度特征的聚合能力更强车牌在图像中可能只占几十个像素也可能占据画面三分之一这种尺度跨度需要网络有足够宽的感受野第二yolov7的auxiliary head在训练阶段提供额外梯度对小目标检测的收敛速度有明显帮助车牌字符区域本身就是小目标第三yolov7的推理速度在Tesla T4上跑640×640输入可以稳定在30FPS以上满足视频流实时处理需求。yolov5的C3结构相对简单在12类车牌这种细粒度分类任务上类间区分度不如yolov7的ELAN。yolov8虽然精度更高但架构改动较大很多yolov7的预训练权重和调参经验无法直接迁移对于想快速复现的团队来说yolov7的生态成熟度更友好。我一般会建议如果手头有yolov7的预训练权重和标注好的车牌数据集直接上yolov7如果是从零开始且对精度有极致要求可以考虑yolov8但要接受更长的调试周期。2.2 12类车牌的数据标注规范12种中文车牌类型包括蓝牌单层、黄牌单层、黄牌双层、绿牌新能源、白牌警用、白牌军用、黑牌使领馆、黑牌港澳、港澳出入境、民航车牌、农用车牌、临时车牌。标注时需要注意几个关键点车牌框的标注要包含车牌完整边缘不要只框字符区域因为检测模型学的是车牌整体特征双层黄牌和双层农用车牌的标注框要覆盖上下两层不要拆成两个框绿牌新能源的渐变底色在标注时不需要特殊处理但训练时要确保有足够多的渐变样本港澳车牌的字体和大陆车牌差异大标注时不要混用同一套字符集数据量方面每类车牌至少需要500张标注样本12类合计6000张起步。如果某些类别样本少可以用数据增强补充但要注意增强后的车牌字符不能变形到无法辨认。常见做法是对蓝牌和黄牌做随机裁剪和亮度调整对绿牌做色相偏移对双层车牌做上下拼接增强。2.3 从标注文件到yolov7训练格式的转换yolov7使用YOLO格式的标注文件每行格式为class_id x_center y_center width height所有坐标归一化到0到1之间。假设你用LabelImg标注后得到的是VOC格式的XML文件转换脚本如下import xml.etree.ElementTree as ET import os # 12类车牌类别映射顺序必须与训练时的data.yaml一致 CLASS_MAP { blue: 0, yellow_single: 1, yellow_double: 2, green: 3, white_police: 4, white_military: 5, black_embassy: 6, black_hk_macau: 7, hk_macau_border: 8, civil_aviation: 9, agricultural: 10, temporary: 11 } def voc_to_yolo(xml_path, img_w, img_h, output_txt): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): cls_name obj.find(name).text if cls_name not in CLASS_MAP: continue cls_id CLASS_MAP[cls_name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并计算中心点和宽高 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h width (xmax - xmin) / img_w height (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(output_txt, w) as f: f.write(\n.join(lines))这段代码的逻辑是读取VOC格式的XML标注文件提取每个目标框的类别和坐标按YOLO格式归一化后写入txt文件。参数说明img_w和img_h是原图宽高必须与标注时使用的图像尺寸一致CLASS_MAP的类别顺序必须与训练时data.yaml中的names列表完全对应否则模型学到的类别会错位。转换完成后建议随机抽查10张图的标注框可视化结果确认框的位置和类别无误。3. yolov7车牌检测模型的训练与调参3.1 训练环境搭建与依赖安装yolov7的官方仓库依赖PyTorch 1.7以上版本建议使用CUDA 11.3或11.6。我一般会创建一个独立的conda环境避免与系统Python冲突conda create -n yolov7_plate python3.8 conda activate yolov7_plate pip install torch1.12.1cu113 torchvision0.13.1cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install -r requirements.txtrequirements.txt中需要关注的是matplotlib、numpy、opencv-python、tqdm、pyyaml这几个包。如果训练时用到wandb或tensorboard做可视化还需要额外安装。注意不要混用不同CUDA版本的PyTorch否则训练时会出现CUDA error: no kernel image is available for execution on the device这个坑我踩过不止一次。3.2 data.yaml与模型配置文件的修改yolov7训练需要两个配置文件data.yaml定义数据集路径和类别yolov7-plate.yaml定义网络结构。data.yaml内容如下train: /data/plate_dataset/images/train val: /data/plate_dataset/images/val nc: 12 names: [blue, yellow_single, yellow_double, green, white_police, white_military, black_embassy, black_hk_macau, hk_macau_border, civil_aviation, agricultural, temporary]nc必须等于12names的顺序必须与转换脚本中的CLASS_MAP一致。模型配置文件方面如果从yolov7.pt预训练权重开始训练需要修改yolov7.yaml中的nc为12同时调整anchor尺寸。车牌通常是宽扁矩形默认anchor可能偏大建议将anchor改为[10,6, 16,10, 24,14, 32,18, 48,26, 64,36, 96,52, 128,68, 192,100]这些数值是根据车牌宽高比统计出来的经验值。3.3 训练命令与关键参数设置启动训练的命令如下python train.py --workers 8 --device 0 --batch-size 16 --data data/plate.yaml \ --img 640 640 --cfg cfg/training/yolov7-plate.yaml --weights yolov7.pt \ --name yolov7_plate --hyp data/hyp.scratch.p5.yaml --epochs 300 --patience 50参数说明--batch-size 16在单卡24G显存下可以跑640输入如果显存不足降到8--img 640 640是训练输入尺寸车牌检测不需要太大分辨率640足够--patience 50表示50个epoch验证集精度不提升就早停避免过拟合--hyp指定超参数文件车牌检测建议用hyp.scratch.p5.yaml其中lr0设为0.01lrf设为0.2momentum设为0.937。训练过程中重点关注mAP0.5和mAP0.5:0.95两个指标12类车牌在300epoch内通常能收敛到mAP0.5在0.92以上。3.4 训练过程中的常见异常与排查训练时如果loss突然变成nan大概率是学习率过大或数据中有标注框宽高为0的脏数据。解决方法是先用脚本检查所有标注文件过滤掉宽高小于1像素的框然后将lr0降到0.001重新训练。如果验证集mAP震荡严重检查batch size是否太小16以下建议调大或使用梯度累积。如果某几类车牌mAP始终很低比如港澳车牌和临时车牌说明该类样本不足需要补充数据或使用类别权重平衡。4. 车牌字符识别与后处理逻辑4.1 检测框裁剪与透视矫正yolov7检测到车牌框后需要将车牌区域裁剪出来送入字符识别模块。但实际场景中车牌可能有倾斜直接裁剪会导致字符变形。常见做法是用OpenCV做透视矫正import cv2 import numpy as np def crop_plate(img, box): x1, y1, x2, y2 map(int, box) plate img[y1:y2, x1:x2] # 转灰度并二值化用于找车牌边缘 gray cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 查找轮廓取最大轮廓做最小外接矩形 contours, _ cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return plate max_cnt max(contours, keycv2.contourArea) rect cv2.minAreaRect(max_cnt) box_pts cv2.boxPoints(rect) # 透视变换到标准尺寸 dst np.array([[0, 0], [140, 0], [140, 44], [0, 44]], dtypenp.float32) M cv2.getPerspectiveTransform(box_pts.astype(np.float32), dst) warped cv2.warpPerspective(plate, M, (140, 44)) return warped这段代码的逻辑是先裁剪检测框区域然后通过二值化和轮廓查找找到车牌的最小外接矩形最后用透视变换将车牌矫正到140×44的标准尺寸。参数说明140×44是蓝牌的标准宽高比绿牌新能源是480×140双层黄牌是440×220实际使用时需要根据车牌类型选择不同的目标尺寸。如果轮廓查找失败直接返回原始裁剪图避免程序崩溃。4.2 字符识别方案选型CRNN还是CTC车牌字符识别主流方案有两种CRNNCTC和基于分割的字符分类。CRNNCTC的优势是端到端训练不需要字符级标注适合中文车牌这种字符集固定的场景。中文车牌字符集包括省份简称31个、字母A-Z去掉I和O、数字0-9、特殊字符港澳牌有额外字符合计约65个类别。CRNN的输入是矫正后的车牌图像输出是字符序列。如果自己训练CRNN需要准备字符级标注数据即每张车牌图对应的字符串。常见做法是用检测模型裁剪出车牌图人工标注车牌号然后训练CRNN。训练时CTC loss的输入长度建议设为18因为新能源车牌有8个字符双层车牌有14个字符18足够覆盖。如果不想自己训练也可以用PaddleOCR的识别模型做迁移学习但要注意PaddleOCR的默认字符集不包含港澳车牌的特殊字符需要微调。4.3 车牌颜色分类与类型后处理12类车牌的识别结果需要做后处理校验。比如蓝牌车牌号是7位绿牌是8位黄牌单层是7位双层是14位。如果CRNN输出的字符数与车牌类型不匹配说明识别有误需要触发重识别或人工复核。颜色分类可以用简单的HSV阈值判断蓝色H在100到124之间黄色H在26到34之间绿色H在35到77之间白色S低于30黑色V低于46。但要注意绿牌新能源是渐变底色单一阈值可能误判建议用检测模型输出的类别作为主判据颜色分类作为辅助校验。5. 部署落地与性能优化5.1 ONNX导出与TensorRT加速训练好的yolov7模型要部署到生产环境建议导出为ONNX再用TensorRT加速。导出命令python export.py --weights yolov7_plate.pt --grid --end2end --simplify \ --topk-all 100 --iou-thres 0.65 --conf-thres 0.35 --img-size 640 640 --max-wh 640参数说明--end2end表示导出端到端的模型包含NMS后处理--topk-all 100保留最多100个检测框--conf-thres 0.35是置信度阈值车牌检测建议设低一些避免漏检--iou-thres 0.65是NMS的IoU阈值。导出ONNX后用TensorRT的trtexec工具转换trtexec --onnxyolov7_plate.onnx --saveEngineyolov7_plate.engine \ --fp16 --workspace4096 --minShapesimages:1x3x640x640 \ --optShapesimages:4x3x640x640 --maxShapesimages:8x3x640x640--fp16开启半精度推理在T4上可以提升约1.8倍速度--workspace4096是显存工作空间单位MB动态shape设置允许batch size在1到8之间变化。实测在T4上yolov7车牌检测CRNN识别的端到端延迟可以控制在25ms以内满足25FPS视频流处理。5.2 多路视频流并发处理实际停车场场景通常有4到8路摄像头需要并发处理。建议用多进程架构每个进程负责一路视频流的解码和推理主进程负责结果汇总和业务逻辑。进程间通信用共享内存或消息队列避免频繁的数据拷贝。如果GPU显存有限可以用CUDA MPSMulti-Process Service让多个进程共享GPU上下文减少上下文切换开销。注意TensorRT引擎不是线程安全的每个进程需要独立加载引擎文件。5.3 模型剪枝与量化对车牌识别的影响如果部署到边缘设备如Jetson Nano需要对模型做剪枝和量化。剪枝方面yolov7的ELAN结构中有大量冗余通道用L1范数对BN层权重排序剪掉最小的30%通道精度损失通常在1%以内。量化方面INT8量化可以将模型体积压缩到FP32的四分之一推理速度提升2到3倍但要注意校准集的选取——必须包含12类车牌的典型样本否则量化后的模型对某些类别的识别率会大幅下降。我一般会保留10%的验证集作为校准集确保量化后mAP0.5下降不超过2个百分点。6. 避坑与排查12类车牌识别中那些血泪教训6.1 双层黄牌被拆成两个单层框现象训练时双层黄牌的标注框被拆成上下两个独立框模型学到的是两个小框而不是一个完整车牌。原因标注人员对双层车牌理解不一致或者标注工具默认按字符行分割。解决在标注规范中明确双层车牌必须用一个框覆盖上下两层标注完成后用脚本检查所有黄牌标注框的宽高比宽高比小于2的框需要人工复核。6.2 绿牌新能源在夜间场景漏检严重现象白天绿牌识别正常夜间或地下车库绿牌漏检率超过30%。原因绿牌底色在低照度下饱和度下降与黑色车身的对比度降低模型学到的颜色特征失效。解决训练数据中补充夜间绿牌样本至少占总绿牌样本的20%同时在推理阶段对低照度图像做直方图均衡化预处理提升车牌区域对比度。6.3 港澳车牌字符识别乱码现象港澳车牌的字母和数字识别结果出现乱码比如把“HK”识别成“HX”。原因CRNN训练时港澳车牌样本不足字符集覆盖不全。解决单独收集港澳车牌样本至少500张加入CRNN训练集同时在字符集中显式加入港澳车牌的特殊字符不要依赖模型自己泛化。6.4 模型导出ONNX后精度下降现象PyTorch模型mAP0.5为0.93导出ONNX后降到0.89。原因导出时--end2end模式下的NMS参数与训练时不一致或者--simplify操作改变了某些算子。解决导出时先用--end2end导出带NMS的版本对比ONNX和PyTorch在相同测试集上的输出逐层排查差异如果精度下降超过2个百分点尝试不用--simplify重新导出。6.5 多路视频流推理时GPU显存溢出现象单路推理正常开到4路时GPU显存溢出。原因每个进程独立加载TensorRT引擎显存占用翻倍。解决使用CUDA MPS共享GPU上下文或者将batch size从4降到2同时用队列缓冲视频帧避免瞬时并发过高。7. 用测试集反推模型短板一个可复用的验证脚本训练完模型后不要只看mAP就完事。我习惯写一个按类别统计精度的脚本把12类车牌的检测和识别结果分开看这样能快速定位是哪几类拖了后腿。脚本核心逻辑如下import json from collections import defaultdict # 加载验证集标注和模型预测结果 with open(val_gt.json, r) as f: gt_data json.load(f) with open(val_pred.json, r) as f: pred_data json.load(f) # 按类别统计TP、FP、FN stats defaultdict(lambda: {tp: 0, fp: 0, fn: 0}) for img_id in gt_data: gt_boxes gt_data[img_id] pred_boxes pred_data.get(img_id, []) matched_gt set() for pred in pred_boxes: best_iou 0 best_gt_idx -1 for idx, gt in enumerate(gt_boxes): if idx in matched_gt: continue iou compute_iou(pred[bbox], gt[bbox]) if iou best_iou: best_iou iou best_gt_idx idx if best_iou 0.5 and pred[class] gt_boxes[best_gt_idx][class]: stats[pred[class]][tp] 1 matched_gt.add(best_gt_idx) else: stats[pred[class]][fp] 1 for idx, gt in enumerate(gt_boxes): if idx not in matched_gt: stats[gt[class]][fn] 1 # 输出每类的精确率和召回率 for cls_name, s in stats.items(): precision s[tp] / (s[tp] s[fp]) if (s[tp] s[fp]) 0 else 0 recall s[tp] / (s[tp] s[fn]) if (s[tp] s[fn]) 0 else 0 print(f{cls_name}: P{precision:.3f}, R{recall:.3f})这个脚本的关键在于按类别分别统计而不是只看整体mAP。参数说明compute_iou是计算两个框IoU的函数0.5是匹配阈值可以根据业务需求调整。跑完这个脚本如果发现某类召回率低于0.8就针对性地补充该类样本或调整anchor。我一般会在每次模型迭代后跑一遍把结果记在表格里对比不同版本的变化。车牌类型常见短板优先优化手段双层黄牌框合并错误检查标注规范绿牌新能源夜间漏检补充夜间样本港澳车牌字符乱码扩充字符集临时车牌样本不足数据增强民航车牌字体特殊单独训练识别模型最后说一个我自己的习惯每次训练新模型前先拿100张测试图跑一遍旧模型把失败案例截图存下来训练完新模型后再跑同一批图对比哪些问题解决了、哪些是新引入的。这个习惯帮我省了很多次“以为模型变好了其实只是测试集换了”的后悔药。希望帮到你。本文还有配套的精品资源点击获取
返回列表