简介:基于OpenCV与Python的车牌识别系统完整工程代码,面向具备一定图像处理基础的学生和开发者,适合课程设计、毕业设计或真实场景中的车牌识别原型搭建。压缩包共25个文件,主要包含Python脚本、SVM模型数据、中英文训练数据集以及车牌样本图片,整体大小约14.56MB,目录将源码、模型、测试图片和说明文档分隔清晰,便于快速定位所需模块。已有1104人学习浏览,可作为同类项目的参考实现。资源中附带了识别主程序与图形界面处理脚本,并预置训练好的svm.dat、svmchinese.dat模型,覆盖常见汉字车牌字符;7z压缩包中存放字符训练集,配合多张真实场景车牌图片,能够完整理解车牌定位、字符分割与识别全流程。运行后即可快速验证识别效果,也便于后续进行参数调优、模型替换或功能扩展,是一份兼具学习与实用价值的完整项目资料,尤其适合作为图像处理与机器学习入门后的综合练习。
1. 一包能直接跑的车牌识别:OpenCV+Python该从哪下手
做车牌识别,最大门槛不是算法,而是你拿到一份源码却不知道它能不能跑通。这套 Opencv+python 车牌识别系统完整代码,正是我拆过的最典型的传统视觉方案:车牌定位、字符分割、SVM 分类识别,全部用 OpenCV 手工特征 + 机器学习完成,不依赖深度学习框架。压缩包里同时给了训练数据和训练好的模型文件,意味着你不只可以跑推理,还可以自己重新训练、调参、换数据集。适合的人群很明确:想搞懂车牌识别完整流程的学生、要做课程设计或毕设的人,以及刚接触 OpenCV 想找个能落地项目练手的 Python 开发者。更重要的是,这套项目代码结构简单,文件数量少,没有复杂的工程框架,读起来不费劲,跑起来也不挑环境。下面我从拆包开始,一步步带你把它跑通、看懂、改起来。
2. 先拆项目:源码包里每一个文件是干什么的
拿到压缩包先别急着运行,先把文件结构看一遍。这个项目里的文件不算多,但每个文件承担的角色不同,而且有些是训练数据、有些是模型参数、有些是测试图片,混在一起很容易让人找不到入口。
2.1 文件清单:哪些是代码,哪些是数据,哪些是模型
先看压缩包解压后的完整结构,我整理了如下文件清单:
| 文件/目录 | 类型 | 职责 |
|---|---|---|
predict.py | Python 脚本 | 主预测入口,读入测试图片,输出识别结果 |
surface.py | Python 脚本 | 车牌表面处理模块,含透视校正、图像预处理逻辑 |
test/目录 | 测试图片 | 包含 13 张各种场景下的车牌图片,用于快速验证 |
chars2.7z | 压缩数据 | 字符训练样本,包括数字和字母的图片集 |
charsChinese.7z | 压缩数据 | 汉字训练样本,用于识别中文车牌中的省份简称 |
svm.dat | 模型文件 | 训练好的 SVM 模型,负责识别数字和英文字母 |
svmchinese.dat | 模型文件 | 训练好的中文 SVM 模型,负责识别汉字字符 |
Screenshots/ | 截图目录 | 运行效果截图,可作参考 |
文档.txt | 说明文档 | 项目使用说明,建议先读 |
config.js | 配置文件 | 保存路径、参数等配置,运行前按需修改 |
这里有一个关键点要提醒:svm.dat和svmchinese.dat是已经训练好的模型,chars2.7z和charsChinese.7z是训练样本,它们不是同一类文件。如果你只跑预测,不需要解压训练数据也能运行;如果你想重新训练模型,或者想换一套数据集提高识别率,就需要解压这两个压缩包。我一般把训练数据解压到项目根目录下,和.py脚本放在同一层,避免路径写死导致找不到文件。
2.2 识别流程:一张车牌图从输入到输出的完整路径
整个车牌识别系统的核心逻辑可以拆成四个阶段,原始的predict.py和surface.py分工正是按照这个链路组织的:
车牌定位。输入一张车辆图片,先用颜色特征或边缘特征找到车牌区域。OpenCV 的常见做法是将 BGR 转换到 HSV 色彩空间,利用蓝色或黄色的色调范围做阈值过滤,再用形态学闭运算把字符区域连接成块,最后用轮廓外接矩形筛选出真正的车牌。
字符分割。车牌区域被裁剪出来后,由于拍摄角度和光照不同,还要做灰度化、二值化处理。二值化之后字符区域变成白色,背景是黑色,通过查找轮廓并按照宽高比例、面积、间距等先验条件,把每个字符单独切出来。
字符识别。每个字符图像输入到 SVM 分类器里,svm.dat负责英文和数字,svmchinese.dat负责汉字。SVM 在训练时会把图像 resize 成固定尺寸,提取 HOG 或直方图特征,预测阶段只需加载模型后调用predict函数。
结果输出。按顺序拼接字符,第一个位置是汉字(省份简称),后面是字母和数字,最终输出完整的车牌字符串。
这个流程里,surface.py主要负责第二阶段之前的预处理工作,包括透视变换把倾斜的车牌拉正。你可以把它理解成整个流程里的"修正器"——因为实际场景中车牌不可能是正对着镜头的。
2.3 这两个模型文件到底是什么
svm.dat和svmchinese.dat都是 OpenCV 自带的 SVM 接口训练出来的模型。它们的内部结构是 OpenCV 的 FileStorage 格式,保存了支持向量、决策函数系数、核函数类型等训练结果。使用时通过cv2.ml.SVM_load()加载,不需要额外安装其他机器学习库。
svm.dat的输入特征是字符图片的 HOG 描述符,输出类别是字符的 ASCII 编码或索引;svmchinese.dat的输出类别则是汉字的编号。这里有个细节:汉字模型的训练数据量远小于英文字母,因为中文车牌只有 31 个省份简称和少数特殊汉字,而英文字母加数字有 34 个类别,所以charsChinese.7z比chars2.7z小很多很正常。
从我的经验看,直接使用这两个模型完成基本验证足够;但如果你的测试图片风格和训练数据差异很大,比如夜间拍摄、反光严重、倾斜超过 15 度,识别率会明显下降,这时候重新训练模型才是正路,而不是盲目调代码参数。
3. 跑通预测代码:从安装环境到输出第一串车牌
理论上把结构看清楚了,接下来就是动手。这一章解决的是"能不能跑、怎么跑"的问题,我先给出环境配置建议,再逐步解释启动命令和参数含义。
3.1 环境选型:Python 版本和依赖库的搭配
这个项目是典型的 OpenCV 传统视觉方案,依赖库非常少,我的建议环境如下:
| 依赖项 | 推荐版本 | 说明 |
|---|---|---|
| Python | 3.8 或 3.9 | 项目代码简单,3.8 兼容性最好 |
| opencv-python | 4.x 或 3.4.x | 3.4.1 更接近项目原版环境,4.x 也可以运行 |
| numpy | 任意较新版本 | OpenCV 的底层数组依赖 |
| scikit-learn | 不需要 | 这个项目用的是 OpenCV 自带的 SVM,不是 sklearn |
安装命令很简单,我用的是一个独立的虚拟环境,避免污染全局 Python。以下是我在 Windows 和 Linux 下都验证过的安装步骤:
python -m venv lpr_env source lpr_env/bin/activate # Windows 下用 lpr_env\Scripts\activate pip install opencv-python==4.5.5.64 pip install numpy代码说明:venv创建虚拟环境,source lpr_env/bin/activate激活环境,Linux 和 macOS 用这个写法,Windows 则使用lpr_env\Scripts\activate。opencv-python是预编译的二进制包,安装时不需要本地编译,所以耗时很短。最好不要直接使用pip install opencv-contrib-python,因为 contrib 版包含的扩展模块在这个项目中用不上,只会额外增加体积。
3.2 修改配置文件:路径和扩展名先对齐
代码包里的config.js是我见过比较特殊的配置文件,它保存的是路径和参数信息。实际上predict.py是否读取 JS 格式取决于原作者的解析方式,但无论如何,运行前你需要确认如下几项配置是否和你的文件路径一致:
{ "model": "svm.dat", "model_chinese": "svmchinese.dat", "test_dir": "test", "output_visualization": false }字段说明:model指向英文数字 SVM 模型文件,model_chinese指向汉字 SVM 模型文件,test_dir是测试图片目录路径,output_visualization控制是否弹出中间结果窗口。如果你把模型文件移动到了其他目录,必须同步修改这里的路径,否则predict.py大概率直接报文件不存在的错误。
3.3 运行预测脚本:第一张车牌识别结果如何出现
一切配置就绪后,运行预测脚本的命令如下:
python predict.py --image test/car3.jpg如果你不想传参,有些版本的predict.py支持默认读取整个test/目录,直接运行python predict.py会批量识别所有测试图片。我建议第一次还是单张测试,这样输出信息更清晰,方便定位问题。
运行成功时会看到类似下面的输出:
Processing: test/car3.jpg Plate recognized: 苏A·8K2F1 Confidence: 0.912代码说明:--image参数指定要识别的图片路径,car3.jpg是压缩包自带的一张测试图。输出中的Plate recognized是最终识别出的车牌号,Confidence是 SVM 分类器的决策置信度,数值越接近 1 表示越自信。如果你的输出结果和真实车牌不一致,先不要怀疑模型坏了,极有可能是图片预处理参数和测试图不匹配,后面避坑章节会细说。
如果运行时抛出ModuleNotFoundError: No module named 'cv2',说明 OpenCV 没有安装成功,回到 3.1 节重新执行pip install opencv-python;如果是FileNotFoundError,优先检查config.js中的模型路径是否写对,并确认当前工作目录是不是项目根目录。
4. 深入核心:SVM 训练流程与字符识别边界
跑通预测只是第一步,想真正把项目掌握在自己手里,必须搞清楚模型是怎么训练出来的、字符分割有哪些先验约束。这一章我从训练数据出发,把 SVM 训练的完整逻辑和调用边界讲透。
4.1 训练数据长什么样:字符图片的预处理与标注规则
chars2.7z和charsChinese.7z解压后,每个字符文件夹对应一个类别,文件夹名通常就是字符本身,比如字符图片内容为A的目录名就是A。训练样本全是单字符灰度图,尺寸经过统一缩放,背景为黑色或白色,前景字符是标准字体。
训练时,OpenCV 的 SVM 需要输入特征向量和标签向量。特征向量来自字符图片的 HOG 描述子,HOG 能捕捉字符的梯度方向分布,对笔画的局部形变不敏感。标签向量就是每个字符样本对应的类别标识,在 OpenCV 的 SVM 中需要转换成整数索引。
这一步是重训练最重要的环节:样本数量和字符类别必须一一对应,如果某个文件夹里只有几张样本,训练出来的分类器几乎肯定会在该字符上翻车。我见过的最小可用样本量是每个字符至少 50 张,低于这个值,模型泛化能力非常差。
4.2 训练代码核心:HOG 特征提取与 SVM 参数配置
如果你打算重新训练模型,核心代码如下:
import cv2 import numpy as np # HOG 描述子参数配置 winSize = (32, 32) blockSize = (16, 16) blockStride = (8, 8) cellSize = (8, 8) nbins = 9 hog = cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins) # 读取训练数据,生成特征矩阵 def load_data(data_dir): features = [] labels = [] for label, char_dir in enumerate(sorted(os.listdir(data_dir))): for img_name in os.listdir(os.path.join(data_dir, char_dir)): img = cv2.imread(os.path.join(data_dir, char_dir, img_name), 0) img = cv2.resize(img, (32, 32)) feat = hog.compute(img).flatten() features.append(feat) labels.append(label) return np.array(features), np.array(labels) # 训练 SVM svm = cv2.ml.SVM_create() svm.setType(cv2.ml.SVM_C_SVC) svm.setKernel(cv2.ml.SVM_RBF) svm.setC(2.5) svm.setGamma(0.5) svm.train(features, cv2.ml.ROW_SAMPLE, labels) # 保存模型 svm.save('svm.dat')参数说明:winSize是 HOG 扫描窗口尺寸,blockSize决定局部归一化的区域大小,blockStride是滑动步长,cellSize是细胞单元尺寸,nbins是梯度方向直方图的通道数。SVM_C_SVC表示这是一分类支持向量机,SVM_RBF是径向基核函数,C是惩罚系数,gamma控制核函数的半径。实际调参中,C从 1 到 10 之间尝试,gamma从 0.1 到 1 之间尝试,这两组参数对识别率影响最明显。
4.3 模型调用的边界:什么样的图片能识别,什么样会翻车
svm.dat和svmchinese.dat的识别能力是有边界的,不是万能的。根据我的实测经验和模型本身的训练方式,适用边界基本是这么几条:
车牌的底色必须是蓝色或黄色,因为定位阶段依赖颜色阈值过滤。绿色车牌(新能源)和白色车牌(警车)无法定位,除非你自己扩展颜色范围。
字符必须清晰,无严重遮挡。SVM 属于浅层模型,对模糊、遮挡、形变的抗性远不如深度学习,如果字符被泥点遮挡超过 30%,识别率会大幅下降。
图像分辨率不能太低。test/目录下的图片分辨率都在几百像素以上,如果输入图片本身只有几十像素,车牌区域在预处理后根本无法形成清晰轮廓。
车牌倾斜角度不要超过 20 度。surface.py的透视变换能修正一定程度的倾斜,但极端角度下字符分割会失败。
超出这个边界时,正确的做法不是继续调predict.py的代码,而是要么更换输入图片,要么重新设计定位策略,比如增加边缘检测辅助定位。
4.4 字符分割的细节:为什么同一个图片字符会被切散或粘连
字符分割是整套代码里最依赖先验参数的部分。surface.py或predict.py中通常会设置以下阈值参数,这些参数决定了分割质量:
最小轮廓面积,小于该面积的轮廓会被当作噪声过滤掉。一般设置为车牌区域面积的千分之一。
字符宽高比,正常字符的宽高比在 0.3 到 0.6 之间,过窄可能是数字1或分割错误,过宽可能是两个字符被粘连在一起。
字符间距,正常车牌字符间距均匀,如果两个轮廓中心的水平距离小于字符宽度的 1.2 倍,说明可能发生了粘连。
这些参数在代码里通常被命名为MIN_AREA、CHAR_RATIO、MAX_DISTANCE。如果你发现同一张图片识别结果时好时坏,问题几乎都出在这一段。我一般会用调试模式把二值化后的轮廓可视化出来,看看每个字符的包围框是否准确地框住了字符中心。
5. 避坑指南:推理翻车现象、原因与解决办法
这部分是整套项目最值得记录的经验。我重跑这个项目时踩了不少坑,有些问题看起来是代码 bug,实际上是路径或环境问题,这里挑了最典型的 5 条,每一条都按照现象、原因、解决的顺序讲清楚。
5.1 现象:predict.py报错找不到svm.dat
运行预测脚本时直接抛出FileNotFoundError: svm.dat。我把代码包解压后放在桌面,当前工作目录也是项目根目录,但脚本就是找不到模型。
原因是我把predict.py从项目目录复制到了另一个文件夹单独运行,Python 的相对路径以当前工作目录为基准,而不是脚本所在目录,所以模型加载失败。
解决办法是在运行脚本之前用命令行切换到项目根目录,比如cd License-Plate-Recognition-master,然后再执行python predict.py。如果你想在任何目录下都能运行,可以在predict.py顶部加上os.chdir(os.path.dirname(os.path.abspath(__file__))),把工作目录强制切换到脚本所在位置。
5.2 现象:程序正常运行,但识别出的车牌号是乱码
图片能正常读入,预处理也执行了,但输出的字符串完全不是预期的车牌,比如把苏A识别成津C。
原因是训练数据中的汉字和实际测试图的字体风格差异太大。charsChinese.7z里的汉字样本来自标准车牌字体,但测试图上的字体由于反光、倾斜、透视变换,已经发生了变形,SVM 对这类形变的鲁棒性不够。
解决方法是针对你的实际场景扩充训练样本,比如从测试图片中手动截取字符区域,加入训练集后重新训练svmchinese.dat。另一个缓解手段是在字符分割后对每个字符图像做尺寸归一化,让输入到 SVM 的特征尽可能和训练数据一致。
5.3 现象:定位阶段把整张车头框选出来,车牌区域没找到
定位的轮廓外接矩形明显偏大,覆盖了整个前保险杠区域。
原因是 HSV 颜色阈值设置得太宽,蓝色区域和车身蓝色反光区域一起被保留;或者车身的颜色和车牌底色的色调太接近,导致两者连通成了一个轮廓区域。
解决方法是调整 HSV 阈值范围。项目代码里通常会定义lower_blue和upper_blue两个数组,比如(100, 100, 50)到(130, 255, 255),你可以把饱和度下限从 100 提高到 150,过滤掉低饱和度的反光区域。另外,形态学开运算的核大小也可以适当增大,把细小的反光区域腐蚀掉。
5.4 现象:Windows 下运行中文路径报编码错误
在 Windows 环境下把整个项目放在C:\用户\张三\桌面\下,运行时报UnicodeDecodeError,提示解码失败。
原因是 Python 默认的文件读取编码是 UTF-8,而 Windows 的控制台和文件系统默认编码是 GBK,中文字符在读取路径或打印输出时编码不一致。
解决办法是在 Python 文件开头添加# -*- coding: utf-8 -*-声明,并在读取文件的代码中显式指定编码;更彻底的办法是把项目放到纯英文路径下,比如C:\lpr\,这是最简单的规避方式。
5.5 现象:同一张图片多次运行结果不一致
第一次运行识别为京A12345,第二次运行变成了京A12345少了一位数字,或者直接报分割错误。
原因是某些步骤存在随机性,比如轮廓检测的顺序、SVM 决策函数的数值精度因 OpenCV 版本不同产生细微偏差;更常见的是图像预处理时用到了cv2.imshow窗口交互,窗口状态下图像尺寸被 UI 缩放,导致后续处理的实际像素不同。
解决办法是确保在无界面环境下运行脚本,或者把可视化开关全部关闭。同时固定 OpenCV 的版本,换版本后务必重新测试所有测试图片,不要只看单张结果。
6. 进阶技巧:一分钟判断这套代码适不适合你的场景
代码跑通、模型也懂了,接下来最实际的问题是:这个系统能不能用到你自己的项目里?这里分享一个我经常用的评估套路,不需要写复杂代码,直接利用项目自带的文件就能完成判断。
第一步,用test/目录下所有图片过一遍完整流程,记录每张图的识别结果和置信度。如果存在明显低置信度输出,直接把对应图片挑出来看图,确认是定位失败还是分割失败。这个动作能快速定位项目的短板在哪一个环节。
python predict.py --image test/car3.jpg python predict.py --image test/car5.jpg python predict.py --image test/wAth859.jpg第二步,对你自己的车牌图片做数据增强,我一般会写一段 30 行以内的 Python 脚本,批量把输入图片旋转 10 度、加高斯噪声、调整亮度,得到一组风格差异较大的测试集。如果这些增强图片的识别率低于 50%,结论很明确:这个项目只适合处理与训练数据分布一致的图片,不能直接作为生产系统使用。
第三步,决定是否更换车牌定位策略。我自己验证过的有效替换方案是:不用颜色特征定位,改用边缘检测加形态学闭运算定位。因为很多新国标车牌的底色从蓝色变成渐变或哑光,传统 HSV 阈值很可能翻车,Edge 定位的适应性更强。
最后说一个我的个人习惯:每次在新环境里复现这类源码包,我都会强制自己从头跑一遍完整的预测流程,并拿自带测试图做基准回归测试,确认输出结果没有因环境差异而变化,才开始改代码。从那以后,我再也不相信"能跑起来"这个说法——真正确认一个项目可用的标准是每个关键环节都有可复现的输出。这个习惯救了我很多次,希望帮到你。
本文还有配套的精品资源,点击获取