ARTICLE DETAIL

资讯详情

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

Python+CNN停车场车牌识别系统源码实战解析

Python+CNN停车场车牌识别系统源码实战解析 简介基于Python与CNN模型识别的停车场智能车牌识别系统源码是一套面向计算机视觉课程设计、期末大作业及智能停车场项目开发者的完整工程。项目以CenterNet完成车牌目标检测结合最优CNN模型对字符进行识别并通过Pygame模块搭建图形交互界面从图像预处理、车牌定位到字符输出均有对应实现代码结构完整、可直接运行适合作为毕设或课设的参考基座。压缩包总计2000个文件约124.68MB其中999个py文件覆盖算法核心与业务逻辑h/json/xml等文件用于模型配置与数据组织md/txt文档提供使用说明与设计思路另含部分Java、C、JavaScript等扩展辅助文件目录划分清晰便于按模块研读、调试与二次开发。该资源已有475人学习读者可从中获取目标检测与车牌识别的完整融合思路、界面交互设计方案及工程化组织方式也能直接复用其中模型与脚本快速搭建可演示的智能停车场识别原型支撑项目答辩与学习报告撰写。1. 基于Python和CNN的停车场车牌识别这套源码到底能落地多少停车场出入口的车牌抓拍看着是个简单任务实际跑过才知道白天强光、夜间反光、雨雪泥污、弯道来车任何一项都能让传统OCR闹脾气。用基于Python和CNN模型识别的停车场智能车牌识别系统源码就是把“定位、分割、识别”这条流水线用卷积神经网络串起来对一个固定场景做到接近可用的识别率。这套东西适合两类人一是做毕业设计需要可跑通demo的在校生二是想给自助洗车、园区道闸加一个轻量识别模块的嵌入式开发者。它解决的不是“识别一个车牌”而是“在停车场这种非理想光照下怎么稳定输出车牌号码”。这篇文章从模型选型、数据集准备、训练推理命令、核心模块再到部署踩坑把整条链路拆开讲清楚。2. 系统架构与选型为什么停车场场景首选CNN而不是传统OCR2.1 CNN在车牌识别中的角色从检测到分类的流水线一套完整的车牌识别系统通常拆成三步第一步在画面里找到车牌位置第二步把车牌区域里的字符一个个切开第三步把每个字符识别成汉字、字母或数字。传统做法用颜色阈值、边缘检测、模板匹配在固定卡口上能跑但一遇倾斜、模糊、逆光就崩。CNN卷积神经网络的好处在于它用一堆可训练的卷积核自己学习“像车牌”的特征对光照和形变的容忍度高得多。在停车场场景里我一般会把定位用目标检测网络或OpenCV传统定位字符识别用轻量CNN。这套源码里重点就在“识别”这一步设计一个输入固定尺寸、输出字符类别的CNN模型让它在已经切好的字符图片上做分类。如果只是靠CNN做端到端检测和识别对训练数据量和算力的要求会高很多作为一套源码包反而不好维护。2.2 模型选型轻量CNN和YOLO系列的取舍很多人拿到源码第一反应是“我要端到端检测车牌”其实没必要。停车场摄像头位置固定车牌宽高比、字体分布相对稳定把问题拆开反而好调。字符识别模型可以做到很小三个卷积层加两个全连接层参数量几十万CPU就能实时。如果用YOLO直接回归车牌的四个角点和类别训练需要更多标注数据且在小目标上容易漏检。我见过不少源码包只给一个字符分类模型自己不带定位这时候需要配合一个前置定位模块。方案训练数据量推理速度适用场景YOLO端到端检测车牌需要几千张带框标注GPU实时CPU较慢车牌位置不固定的移动巡检传统定位 轻量CNN识别只要字符图几百张CPU也能实时固定相机停车场出入口建议如果源码里自带YOLO权重直接用如果只有字符识别模型自己用OpenCV做定位先灰度化、边缘检测、找轮廓再切字符。这样即使没有高端显卡也能在树莓派上跑。2.3 数据集准备与标注格式别小看字符类别定义和转换无论是训练还是二次训练都需要先吃透数据集结构。停车场车牌识别源码常见的数据组织是一张车牌字符图片一个文件标签是字符的类别序号。中文省份简称算一类字母和数字各算一类。注意字符类别数不是10加24因为省份简称有31个汉字和字母、数字混在一起要统一做成分类索引。如果源码给的是VOC XML标注需要先转成YOLO训练用的txt格式。常见做法是用脚本把xml里的xmin ymin xmax ymax归一化到图片宽高再写成一行“类别编号 cx cy w h”。下面是一个转写片段import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, out_path, class_map): tree ET.parse(xml_path) root tree.getroot() w_img int(root.find(size/width).text) h_img int(root.find(size/height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in class_map: continue 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) cx (xmin xmax) / 2 / w_img cy (ymin ymax) / 2 / h_img w (xmax - xmin) / w_img h (ymax - ymin) / h_img lines.append(f{class_map[cls]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))参数说明class_map是一个字符串到数字的字典比如{blue_plate: 0, green_plate: 1}。归一化坐标是YOLO训练的必要输入直接喂像素坐标会训练发散。注意有些车牌还有新能源绿色牌照长度从7位变成8位类别定义里要留出位置否则后处理切字符时索引会错位。我之前处理过一个数据集里面有一千张“苏”字车牌但“藏”字只有几张这种不均衡会让模型对低频字符几乎不识别。解决思路在后文避坑章节专门说。总之先把数据集准备好训练才不玄学。3. 从源码跑通Python环境、依赖安装与最小复现命令拿到一个“停车场智能车牌识别系统源码.zip”别急着双击train.py。先理清里面的文件结构一般会有README、requirements.txt、weights目录、data目录、train.py、detect.py。缺依赖是最常见的卡点所以下面按最小顺序复现。3.1 环境准备Python版本与依赖清单建议用Python 3.8到3.10太新的版本容易遇到OpenCV和PyTorch版本兼容问题。先创建一个虚拟环境python -m venv venv source venv/bin/activate # Windows 下改为 venv\Scripts\activate pip install --upgrade pip pip install -r requirements.txt如果源码里没有requirements.txt手动安装核心依赖pip install opencv-python numpy torch torchvision matplotlib注意只做CPU推理的机器可以装CPU版torch去PyTorch官网复制对应命令要训练的机器有N卡再装CUDA版。依赖装完后用python -c import cv2; print(cv2.__version__)验证。很多源码包里的OpenCV代码用了较新的API版本太旧会直接报模块找不到。这里的关键不是版本号越新越好而是和你系统的CUDA、Python三者兼容。3.2 运行训练脚本关键参数与默认值假设train.py有CLI参数。常见参数包括data、epochs、batch-size、img-size、device。最小训练命令python train.py --data data/plate.yaml --epochs 100 --batch-size 8 --img-size 416 --device cpu参数含义--data指向一个yaml文件里面写入train、val图片路径和类别数量、类别名列表。--epochs100是常用起点真实停车场数据量小几千张的情况下50到100轮足够收敛。--batch-size由显存决定8比较保守如果OOM就降到4。--img-size是输入到网络的图像边长416对车牌检测合适太大训练慢。--device cpu强制用CPU训练跑起来慢但能验证流程。训练过程中观察loss曲线如果直线不降后文有排错。如果你只想先看效果跳过训练直接用源码自带的weights/best.pt或model.h5跑推理。3.3 运行推理脚本检测单张图片和视频流训练完或直接用源码自带权重推理命令一般是python detect.py --source test.jpg --weights weights/best.pt --conf-thres 0.5 --save-txt参数说明--source可以是图片路径也可以是摄像头编号0。停车场部署时用这个连USB摄像头。--conf-thres是置信度阈值建议先设0.5观察如果漏检很多降到0.3但会引入误检。--save-txt把识别结果包括车牌号写到txt文件方便对接道闸逻辑。跑通这一步你就能看到一帧图里画出车牌框并输出识别文本。此时再去调内部算法才是有效率的路径。3.4 拿到源码后先做三步能省一晚上第一确认依赖清单是否完整缺什么补什么。第二跑一次推理哪怕用测试图片确认模型权重能加载。第三看输出结果里是否带置信度和坐标只有文本没有框的源码往往少了定位模块。我见过不少下载的免费python源码包直接打印“车牌号”不画框看起来很省事但一旦画面里有多辆车就分不清。所以拿到源码先别追求端到端先确认它能稳定读出一张单车牌图。另一个容易忽略的点是路径分隔符。Windows下源码里写的是正斜杠放在Linux上没问题反过来如果代码用了\拼接路径Windows上跑得通部署到Linux就开场爆错。我一般会先全局搜一遍os.path.join确认没有硬编码。4. 核心模块拆解车牌定位、字符分割与CNN识别4.1 预处理灰度化、去噪与边缘检测在车牌识别源码里第一步通常不是直接丢给CNN而是先做一个传统视觉预处理。好处是能缩小候选区域降低CNN工作量。下面是我常用的定位预处理段import cv2 import numpy as np def preprocess(img): gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯滤波核大小5抑制噪声但保留边缘 blur cv2.GaussianBlur(gray, (5, 5), 0) # Sobel边缘检测只取x方向因为车牌字符边缘竖直方向多 sobel cv2.Sobel(blur, cv2.CV_16S, 1, 0, ksize3) abs_sobel cv2.convertScaleAbs(sobel) # 二值化高于阈值设为255 _, binary cv2.threshold(abs_sobel, 150, 255, cv2.THRESH_BINARY) # 形态学闭操作把字符之间的空隙连接成块 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 3)) closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return closed说明Sobel取x方向是因为车牌字符的竖直笔画多但中国车牌有汉字横竖都有实际可以两个方向都试。闭操作的核宽度17、高度3是为了把单个字符连成一个车牌整体。如果车牌子被光线截断可以把核宽度加大到25。注意这段代码产生的是二值轮廓图后面还要找轮廓筛选。很多开源源码在这个地方只用颜色阈值遇到绿色车牌或黄色车牌就得改参数。用边缘检测的好处是颜色无关鲁棒性更高。不好的地方是会引入车灯、保险杠边缘等噪声后面必须按宽高比过滤。4.2 字符分割投影法与连通域分析拿到车牌定位框后需要把“京A12345”切成单独字符。最常见的做法是垂直投影把二值图像每一列的白色像素数统计出来投影值为零的列就是字符间隔。示例def split_chars(plate_gray): # 先二值化文字为白色 _, binary cv2.threshold(plate_gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 统计每列白色像素数 h, w binary.shape col_sum np.sum(binary 0, axis0) # 找到连续非零区间宽度大于阈值的视为字符 chars [] in_char False start 0 for i in range(w): if col_sum[i] 0 and not in_char: start i in_char True elif col_sum[i] 0 and in_char: end i if end - start 10: # 过滤掉噪声点 chars.append((start, end)) in_char False return chars这个方法的坑在于车牌中间的圆点或分隔符也会产生投影缝隙需要在切片时过滤掉宽度过短或位置异常的片段。有些源码会直接用固定比例把车牌切成7份省去投影但遇到新能源8位车牌就出错所以还是投影法更通用。因为中国车牌字符间距不是完全均匀的固定比例只适合标准蓝牌。4.3 CNN识别构建模型与训练配置字符图片通常归一化到相同尺寸比如32x64。下面是一个轻量CNN分类模型用PyTorch实现这就是标题里“CNN模型识别”的核心部分import torch import torch.nn as nn class PlateCharCNN(nn.Module): def __init__(self, num_classes65): super().__init__() self.features nn.Sequential( # 输入: 3x64x32RGB字符图 nn.Conv2d(3, 32, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), # 32x32x16 nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(2), # 64x16x8 nn.Conv2d(64, 128, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.AdaptiveAvgPool2d((1, 1)) # 128x1x1 ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(128, num_classes) ) def forward(self, x): x self.features(x) x torch.flatten(x, 1) return self.classifier(x)参数说明输入尺寸选了64高、32宽因为车牌字符高大于宽。第一个卷积层输出32通道第二个输出64第三个输出128。全连接前用了AdaptiveAvgPool2d这样无论输入图片稍微变形最后都能变成128维特征。分类数按你的数据集设定比如中国蓝牌常用65类其中31个汉字省份简称、24个字母、10个数字。训练时用交叉熵损失、Adam优化器、学习率0.001是稳定起点。要注意类别不均衡比如“O”和“0”容易混淆可以在损失函数里给难分类别加权重。这个CNN模型很小字符图片单张推理在普通CPU上只要几毫秒。如果不想自己训练也可以用源码里训练好的char_model.pth。我建议拿到源码先看这个模型输入尺寸很多坑都出在预处理尺寸和模型不匹配上。5. 避坑与常见问题排查从数据集标注到模型收敛的5个典型坑这一章不直接贴代码写的是调试经验。车牌识别的难点普遍不在模型结构而在工程边界。5.1 坑1训练loss不下降准确率卡在60%左右现象训练几十轮后loss在1.0附近波动验证集准确率上不去。原因最常见的是学习率过大优化器震荡其次是数据类别不均衡低频字符主导梯度方向。我见过一个源码自带数据集里“皖”字样本占了一半模型就变得只认“皖”。解决把学习率从0.01降到0.001加一个余弦退火调度器。统计每类样本数量对低频类用加权采样器。一个简单做法是在损失函数里给每个类别权重频率越低的类别权重越高。如果时序上loss下降很慢先跑10轮看趋势不降就先调学习率别急着加数据。5.2 坑2识别结果“0”和“O”、“1”和“I”混淆严重现象识别“京A01O12”这种车牌输出“京A0O12”置信度还不低。原因这两类字符在归一化后形状几乎一样尤其是中文字体下模型天然难以区分。纯靠CNN模型很难从像素层面找到稳定差异。解决从后处理下手。因为中国车牌的车牌号中字母“I”和“O”不允许出现可以在输出层直接把这两个类别屏蔽或设置规则如果预测为“O”且概率略低于某个阈值强制映射为“0”。在训练时也可以做字符级数据增强例如随机细微旋转、仿射变换让模型更关注笔画局部差异。5.3 坑3车牌定位把车灯、广告牌误检成车牌现象在停车场夜间场景推理输出一堆红色框框住车灯或反光物。原因训练数据里负样本太少模型只知道“像车牌”的纹理不知道“不是车牌”的边界。灯的边缘和车牌边缘在Sobel输出上非常像。解决收集一批不含车牌的夜间停车场截图打上空白标签加入训练。同时调整NMS的IoU阈值到0.4并限制检测框宽高比在2到5之间。这个坑在停车场俯拍角度特别常见因为灯光和车牌在视觉上都接近矩形高亮块。5.4 坑4摄像头角度导致倾斜车牌识别率下降现象道闸摄像头安装在侧面拍出来的车牌是斜的字符分割后边界不齐CNN识别错字。原因字符分割依赖垂直投影倾斜让每列白色像素叠加分割线错位。解决在车牌定位后增加一个透视变换校正步骤根据定位的四个角点做仿射变换把车牌转正。或者在训练数据增强里加入随机旋转正负15度让CNN能适应斜字符。这个坑在现场施工时特别明显因为安装高度和角度不标准理想中的水平车牌很少见。5.5 坑5夜间反光、曝光过度导致漏检现象晚上强光灯直射车牌区域一片白定位直接找不到。原因摄像头自动曝光把车牌过曝了边缘信息丢失。解决硬件端调整曝光时间或启用宽动态。算法端在推理前做直方图均衡化把过亮区域拉伸回正常范围。训练时也要对训练集做亮度扰动乘以随机系数0.6到1.4让模型对曝光变化不那么敏感。这里有个玄学点很多人只调算法参数忽略摄像头本身的曝光设置导致同样的模型在另一个停车场就失效。6. 进阶落地从单张图片到停车场出入口实时识别系统的关键一步单张图片跑通只是起点停车场出入口要的是实时。如果你的源码只有PyTorch推理脚本我建议先把它导出成ONNX再用ONNX Runtime做CPU/GPU推理去掉PyTorch框架的开销。常见做法是在源码里加一个导出脚本import torch from model import PlateCharCNN model PlateCharCNN(num_classes65) model.load_state_dict(torch.load(weights/best.pth, map_locationcpu)) model.eval() dummy torch.randn(1, 3, 64, 32) torch.onnx.export(model, dummy, plate_char.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}})导出后再用Flask封装一个HTTP识别接口供门禁系统调用。代码示意import flask import cv2 import numpy as np import onnxruntime as ort app flask.Flask(__name__) sess ort.InferenceSession(plate_char.onnx) app.route(/recognize, methods[POST]) def recognize(): file flask.request.files[file] img cv2.imdecode(np.frombuffer(file.read(), np.uint8), cv2.IMREAD_COLOR) # 这里接车牌定位和字符分割结果把字符图list送入模型 # 输出为各类别置信度取argmax后再做规则过滤 ...验证方法准备白天、夜间、雨天各200张停车场真实截图计算“整串车牌完全正确率”而不是只算字符级准确率。我自己的习惯是每天夜里检查一次运行日志把识别失败的车牌截图汇总成新训练集隔一周微调一次模型。最早做这个项目我也翻过车以为CNN模型越大越准结果在停车场上被一张侧停的黑色SUV教做人。后来明白了与其盲目换大模型不如先把定位和分割调稳再谈优化。希望帮到你。本文还有配套的精品资源点击获取
返回列表