尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python+OpenCV车牌识别源码实战:从跑通到优化

Python+OpenCV车牌识别源码实战:从跑通到优化 简介这是一套面向计算机视觉初学者与课程设计/毕业设计需求者的车牌识别实战源码包基于Python与OpenCV实现覆盖车牌定位、字符分割与识别等核心流程适合想动手理解传统图像处理方案、或需要可运行项目参考的开发者。压缩包共30个文件、约14.02MB包含2个Python主脚本predict.py、surface.py负责推理与界面交互配套svm.dat、svmchinese.dat等训练模型数据以及chars2.7z、charsChinese.7z字符集压缩包另有16张jpg测试图片、4个xml配置文件和pyc、js等辅助文件目录结构清晰便于直接运行与二次调试。目前已有399人学习下载。读者可据此获得一套完整的车牌识别实现思路包括SVM分类模型调用、字符样本组织方式与多张实拍车牌图的测试用例既能用于快速验证效果也方便在此基础上替换数据集、调整参数或迁移到其他OCR场景是入门OpenCV项目实践的实用参考。1. 车牌识别这套 PythonOpenCV 源码为什么值得先跑通再谈优化很多人第一次接触车牌识别是从一段几十行的 OpenCV 灰度加边缘检测代码开始的跑完发现框都框不准更别说识别字符。这套基于 PythonOpenCV 的车牌识别系统源码加数据价值不在于算法多先进而在于它把「定位 → 分割 → 字符识别」整条链路和训练数据一起给全了predict.py负责推理surface.py负责界面svm.dat和svmchinese.dat是训练好的 SVM 模型chars2.7z、charsChinese.7z是字符训练集train目录里还有一批真实车牌图。它适合两类人一类是想搞懂车牌识别完整流程、拿现成数据练手的 Python 和 OpenCV 学习者另一类是课程设计、嵌入式视觉项目里需要快速搭一个可演示原型的从业者。下面按「资源是什么 → 怎么跑起来 → 坑在哪 → 怎么调」的顺序拆开讲每一步都尽量给到能直接抄的命令和参数。2. 拆开压缩包先看结构predict.py、surface.py 与两个 SVM 模型怎么配合2.1 目录里每个文件到底干什么拿到SYS.zip解压后核心目录是LicensePlateRecognition-master。先别急着运行把关键文件认全后面排错会快很多。文件/目录作用是否必须predict.py单张图片推理入口加载模型并输出车牌字符必须surface.py图形界面调用推理逻辑做交互演示可选svm.dat英文数字字符的 SVM 训练模型必须svmchinese.dat中文省份简称的 SVM 训练模型必须chars2.7z英文数字字符训练集训练时需要charsChinese.7z中文省份字符训练集训练时需要train/一批真实车牌测试图如wA87271.jpg、car3.jpg测试用config.js界面相关配置界面用.idea/PyCharm 工程配置可忽略否predict.py和surface.py是两条入口前者适合命令行批量验证后者适合演示。两个.dat是已经训练好的模型意味着你不训练也能直接推理这是这套资源最省事的地方。2.2 环境依赖与 OpenCV 安装的常见坑这套代码基于 Python 3.7 左右的环境目录里有predict.cpython-37.pyc依赖主要是 OpenCV 和 NumPy。安装时最容易翻车的是cv2装不上或版本不匹配。# 建议用虚拟环境隔离避免污染全局 python -m venv lp_env # Windows 激活 lp_env\Scripts\activate # Linux / macOS 激活 source lp_env/bin/activate # 安装核心依赖opencv-python 不要装太新的版本 pip install opencv-python4.5.5.64 pip install numpy1.21.6逻辑说明opencv-python提供cv2模块车牌识别里的灰度化、高斯模糊、Sobel 边缘、形态学闭运算全靠它numpy负责矩阵运算。参数上4.5.5.64是较稳的版本新版 OpenCV 某些 API 行为有变化容易让老代码报错。如果pip install opencv-python后import cv2报ModuleNotFoundError: No module named opencv八成是装到了别的解释器里用python -c import sys; print(sys.executable)确认当前解释器路径再pip install一次。提示Anaconda 用户如果conda环境里没有 cv2优先用pip install opencv-python而不是conda install opencv后者包名和版本经常对不上。2.3 用 predict.py 跑通第一张图环境好了先拿train目录里的图做单张推理确认模型能加载。# 进入项目根目录 cd LicensePlateRecognition-master # 直接运行推理脚本按脚本内约定传入图片路径 python predict.py如果predict.py里图片路径是写死的打开文件找到类似cv2.imread(...)的行改成你要测的图import cv2 from predict import predict # 假设推理函数名为 predict # 读取一张测试图路径按实际调整 img cv2.imread(train/wA87271.jpg) # 调用推理返回识别出的车牌字符串 result predict(img) print(识别结果:, result)逻辑说明cv2.imread读入 BGR 图像predict内部一般会做灰度化、边缘检测、轮廓筛选、透视校正、字符分割再分别用svm.dat和svmchinese.dat分类。参数上图片路径别带中文和空格OpenCV 在部分平台对中文路径支持不好读进来是None后面全崩。跑通这一步说明模型和依赖都没问题再去看界面。3. 车牌定位与字符分割OpenCV 形态学参数怎么调才不飘3.1 定位流程背后的原理车牌识别的第一步是从整张图里把车牌区域抠出来。常见做法是灰度化 → 高斯模糊去噪 → Sobel 或 Canny 提取垂直边缘车牌字符垂直边缘密集→ 形态学闭运算把字符连成块 → 按长宽比和面积筛选轮廓 → 透视变换校正。这套源码的定位逻辑基本沿这条线走理解它才能知道参数该动哪里。import cv2 import numpy as np # 读图并转灰度 img cv2.imread(train/car3.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊核大小取奇数5x5 适合多数车牌图 blur cv2.GaussianBlur(gray, (5, 5), 0) # Sobel 提取垂直边缘ksize3 是常用值 sobel cv2.Sobel(blur, cv2.CV_8U, 1, 0, ksize3) # 二值化阈值 0 配合 OTSU 自动选取 _, binary cv2.threshold(sobel, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 形态学闭运算把断裂的字符边缘连成矩形块 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)逻辑说明GaussianBlur的核越大越平滑但太大会把字符边缘也糊掉5x5 是平衡点。Sobel的ksize3对细边缘敏感车牌字符间距小用 3 合适。morphologyEx的闭运算核(17, 5)是关键参数宽度 17 是为了把同一行字符横向连起来高度 5 控制纵向连接核太宽会把相邻车牌或背景连进来太窄则字符连不成块。这一步调不好后面轮廓筛选全是噪声。3.2 轮廓筛选与透视校正闭运算后找轮廓按车牌的长宽比国内蓝牌约 440:140接近 3:1和面积过滤。# 找外轮廓 contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: x, y, w, h cv2.boundingRect(cnt) ratio w / float(h) # 长宽比在 2.5 到 4 之间且面积够大才当候选车牌 if 2.5 ratio 4 and w 100: plate img[y:yh, x:xw] cv2.imwrite(plate_crop.jpg, plate)逻辑说明RETR_EXTERNAL只取最外层轮廓减少内部字符轮廓干扰。ratio范围 2.5 到 4 覆盖了蓝牌和部分新能源牌的比例w 100过滤掉小噪点。实际图里如果车牌倾斜boundingRect得到的是外接正矩形会带进背景这时需要cv2.minAreaRect加透视变换校正。参数上长宽比范围要根据你的图源调整卡太死会漏检放太宽会误检。3.3 字符分割的投影法定位到车牌后把字符一个个切开。常用垂直投影对二值车牌图按列求和字符处投影值高字符间隙投影值低据此切分。# 车牌区域转灰度并二值化 plate_gray cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY) _, plate_bin cv2.threshold(plate_gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 垂直投影按列统计白色像素 projection np.sum(plate_bin, axis0) / 255 # 找字符起止列阈值取投影最大值的 0.1 倍左右 threshold projection.max() * 0.1 in_char False for i, v in enumerate(projection): if v threshold and not in_char: start i in_char True elif v threshold and in_char: cv2.imwrite(fchar_{start}.jpg, plate_bin[:, start:i]) in_char False逻辑说明np.sum(axis0)把每列白色像素数累加字符笔画多的列值大。阈值取最大值的 0.1 倍是经验值太低会把噪点当字符太高会漏掉笔画细的字符。分割出的每个char_*.jpg再送进 SVM 分类。中文省份字符和英文数字用不同模型别搞混。4. 避坑与排查跑这套源码最容易翻车的五个地方4.1 现象cv2.imread返回 None后面全报错原因图片路径含中文、空格或路径根本不对。OpenCV 在 Windows 上对非 ASCII 路径支持差。 解决把图片放到纯英文路径下或改用cv2.imdecode(np.fromfile(path, dtypenp.uint8), cv2.IMREAD_COLOR)读中文路径。4.2 现象模型加载报UnpicklingError或维度不匹配原因svm.dat是用特定版本 sklearn/OpenCV 训练的你环境里的库版本差异导致反序列化失败。 解决确认svm.dat的加载方式如果是 sklearn 的 SVM用对应版本的joblib.load版本对不上就重训用chars2.7z解压后的数据跑一遍训练脚本。4.3 现象定位框把整个背景都框进来原因形态学闭运算核太大或轮廓筛选的长宽比范围太宽。 解决把getStructuringElement的核从(17, 5)调小到(13, 5)试长宽比范围收紧到 2.8 到 3.5。4.4 现象字符分割多切或少切原因垂直投影阈值不合适或车牌二值化时反了白底黑字 vs 黑底白字。 解决先cv2.imshow看一眼plate_bin确认字符是白色阈值从 0.1 倍调到 0.15 倍试多切就调高少切就调低。4.5 现象中文省份识别成英文或乱码原因中文和英文用了同一个模型或svmchinese.dat没加载。 解决确认代码里第一个字符走svmchinese.dat其余走svm.dat中文训练集charsChinese.7z解压后类别标签要和模型输出索引对齐。注意.idea/和__pycache__/是工程和缓存文件换机器跑之前删掉__pycache__避免旧字节码干扰。5. 从能跑到好用用 train 目录做批量验证与模型微调5.1 批量跑 train 目录统计识别率单张跑通不代表稳定用train里那批图做批量验证才能看出真实水平。import os import cv2 from predict import predict correct 0 total 0 # 遍历 train 目录下所有 jpg for fname in os.listdir(train): if not fname.lower().endswith(.jpg): continue img cv2.imread(os.path.join(train, fname)) if img is None: continue result predict(img) total 1 # 文件名里若含真实车牌可对比这里只打印 print(fname, -, result) print(共处理, total, 张)逻辑说明os.listdir遍历目录endswith(.jpg)过滤非图片。真实项目里文件名往往不含真值要人工核对或另建标注表。跑完看哪些图识别错错的那几张就是调参和补数据的依据。参数上批量跑时注意内存图多就分批。5.2 用 chars2.7z 和 charsChinese.7z 重训 SVM如果现成模型在你的场景下识别率低用自带训练集重训是最直接的提升手段。# 解压训练集 7z x chars2.7z -o chars2 7z x charsChinese.7z -o charsChinese解压后每个字符一个子目录目录名就是标签。训练时读取所有图统一缩放到固定尺寸如 20x20展平成特征向量喂给sklearn.svm.SVC。import os import cv2 import numpy as np from sklearn.svm import SVC import joblib X, y [], [] for label in os.listdir(chars2): d os.path.join(chars2, label) if not os.path.isdir(d): continue for f in os.listdir(d): img cv2.imread(os.path.join(d, f), cv2.IMREAD_GRAYSCALE) if img is None: continue img cv2.resize(img, (20, 20)) X.append(img.flatten()) y.append(label) # 训练 SVM核函数用线性或 RBF clf SVC(kernellinear) clf.fit(np.array(X), np.array(y)) joblib.dump(clf, svm_retrained.dat)逻辑说明resize到 20x20 是为了统一特征维度flatten把二维图变一维向量。SVC(kernellinear)对字符这种小图够用样本多可换 RBF。训练完joblib.dump保存替换原来的svm.dat。参数上C值默认 1.0识别率低可调大但太大容易过拟合。5.3 一个具体技巧先做透视校正再分割很多识别错误根源在车牌倾斜。定位到候选区域后用cv2.minAreaRect拿到最小外接旋转矩形再做透视变换拉正字符分割会稳很多。rect cv2.minAreaRect(cnt) box cv2.boxPoints(rect) box np.int0(box) # 取宽高构造目标矩形 w, h int(rect[1][0]), int(rect[1][1]) dst np.array([[0, 0], [w-1, 0], [w-1, h-1], [0, h-1]], dtypefloat32) M cv2.getPerspectiveTransform(box.astype(float32), dst) warped cv2.warpPerspective(img, M, (w, h))逻辑说明minAreaRect返回带角度的矩形boxPoints拿到四个角点getPerspectiveTransform算变换矩阵warpPerspective拉正。参数上目标宽高直接用旋转矩形的宽高别硬编码。拉正后再走投影分割字符切分准确率明显提升。我自己的习惯是每次拿到这类车牌识别源码先不碰算法先把train目录批量跑一遍把错例截图存下来再决定是调形态学参数还是重训模型。这套资源的好处是数据和模型都齐省了找训练集的功夫但 OpenCV 版本和路径这两个坑几乎每次都要踩一遍提前按上面的排查清单过一遍能少走很多弯路。希望帮到你。本文还有配套的精品资源点击获取
返回列表