Python车牌识别系统实战:SVM+HOG传统CV方案调参与避坑指南

📅 发布时间:2026/10/1 5:01:02
Python车牌识别系统实战:SVM+HOG传统CV方案调参与避坑指南
简介这是一套面向计算机视觉初学者与课程设计者的车牌检测识别实战项目基于Python实现覆盖图像预处理、车牌定位、字符分割与字符识别四大核心环节帮助读者理解从传统图像处理到深度学习识别的完整技术链路。资源包共48个文件约22.11MB包含7个py脚本、17个png与8个jpg图像素材、2个dat数据文件、1个ui界面文件及1个mp4演示视频另附requirement.txt、run.bat与README.md等配置说明便于快速搭建运行环境。项目内含SVM训练脚本、字符分割模块与主界面程序配合测试图片与训练数据可直观复现车牌定位与识别流程。目前已有150人学习下载适合希望掌握OpenCV图像处理、SVM分类及界面集成开发的读者参考实践。1. 从一张糊到看不清的卡口图说起这套 Python 车牌系统到底能干什么前阵子帮朋友处理一批停车场出入口的抓拍图两千多张光照忽明忽暗车牌有的歪着有的被泥糊了一半。他问我能不能用现成方案跑一遍我第一反应是找开源项目翻了一圈发现大部分要么只给模型不给训练脚本要么依赖一堆跑不起来的旧版本。后来拆到这套vehicle-license-plate-recognition-master结构相对完整从 UI 到训练脚本到分割识别都有才决定认真跑一遍。这套东西本质是一个基于 Python 的车牌检测和识别系统走的是传统计算机视觉加机器学习的路线图像预处理、车牌定位、字符分割、字符识别四步。它不依赖深度学习大模型核心识别环节用的是 SVM 配合 HOG 特征训练脚本svm_train.py和训练数据目录train_dat都在包里意味着你可以拿自己的车牌样本重新训练而不是只能用它预置的那点数据。适合谁做停车场、门禁、卡口这类小场景的开发者想快速搭一个能跑通、能改、能自己喂数据的原型也适合正在学 OpenCV 和机器学习、想找一个完整项目练手的人。它不适合追求工业级高准确率、要处理极端天气和超高速场景的团队那是另一条技术路线的事。2. 拆开压缩包先看结构哪些文件是核心哪些可以不动2.1 目录里每个文件到底管什么拿到基于Python的车牌检测和识别系统.zip解压后第一眼看到一堆文件容易懵。我按职责把它们分成四类这样你改代码时知道该动哪里。文件/目录职责是否需要改car_id_detect.py车牌检测与识别主逻辑包含定位、分割、识别调用核心按需改card_seg.py字符分割模块把定位到的车牌切成单个字符分割效果差时改svm_train.pySVM 训练脚本用train_dat里的样本训练字符分类器换数据时改train_dat/训练样本目录按字符分类存放要自己补样本my_main_ui.py/my_main_ui.uiPyQt 界面逻辑与设计文件一般不动Ui_my_main_ui.py由.ui生成的界面代码不要手改my_pic_rc.py/my_pic.qrc资源文件图标等不动config.js配置文件路径和参数集中在这里按环境改requirement.txt依赖清单装环境时用run.batWindows 下一键启动脚本看情况改test_img//pic/测试图片和素材换成自己的图car.e4p/_eric6projectEric6 工程文件不用管这里有个血泪经验Ui_my_main_ui.py是从my_main_ui.ui自动生成的你直接改它下次用 Qt Designer 重新生成就全没了。要改界面就改.ui文件再重新生成或者把自定义逻辑写在my_main_ui.py里继承。2.2 环境怎么搭才不翻车requirement.txt里通常列了 opencv-python、numpy、scikit-learn、PyQt5 这些。我一般不用pip install -r一把梭因为版本冲突概率高尤其是 OpenCV 和 PyQt5 的版本搭配。稳妥做法是建虚拟环境后逐个装先装数值和图像基础库再装界面库。# 建虚拟环境Python 版本建议 3.7 到 3.9太新的版本 PyQt5 和旧 OpenCV 容易出问题 python -m venv venv # Windows 激活 venv\Scripts\activate # Linux / macOS 激活 source venv/bin/activate # 先装基础库指定较稳的版本区间 pip install numpy1.21.6 pip install opencv-python4.5.5.64 pip install scikit-learn1.0.2 # 再装界面库 pip install PyQt55.15.4 # 最后装剩余依赖 pip install -r requirement.txt逻辑说明先固定 numpy 和 opencv 的版本是因为 SVM 训练和图像处理对这两个库的 API 变化敏感新版 OpenCV 某些函数返回值有调整旧代码可能报错。参数上opencv-python4.5.5.64是我实测和这套代码兼容性较好的版本如果你装最新版遇到cv2.findContours返回值数量不对就是版本问题。装完用python -c import cv2; print(cv2.__version__)验证一下。提示如果你用的是 Python 3.10 以上PyQt5 可能装不上或运行时报DLL load failed直接降到 3.9 最省事。3. 车牌定位与字符分割传统 CV 路线怎么调参才稳3.1 预处理和定位的完整链路车牌定位这一步代码里走的是颜色特征加边缘检测的组合。常见做法是先把图转 HSV 空间因为车牌底色蓝牌、黄牌、绿牌在 HSV 里比 RGB 更容易分离。然后做形态学闭运算把车牌区域连成块再用轮廓筛选出候选区域最后用长宽比和面积过滤掉明显不是车牌的框。import cv2 import numpy as np def locate_plate(img_path): # 读图注意路径不要有中文OpenCV 在 Windows 下读中文路径会返回 None img cv2.imread(img_path) if img is None: raise ValueError(图片读取失败检查路径是否含中文或文件是否存在) # 转 HSV蓝牌在 H 通道约 100-124 区间 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 蓝色车牌掩膜参数按实际底色调整 lower_blue np.array([100, 80, 80]) upper_blue np.array([124, 255, 255]) mask cv2.inRange(hsv, lower_blue, upper_blue) # 形态学闭运算把断裂的车牌区域连起来 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 找轮廓 contours, _ cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) candidates [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) ratio w / float(h) area w * h # 车牌长宽比一般在 2.5 到 5.5 之间面积太小的直接丢 if 2.5 ratio 5.5 and area 2000: candidates.append((x, y, w, h)) return img, candidates逻辑说明HSV 阈值是这套方法最关键的参数。lower_blue和upper_blue决定了能不能把车牌从背景里抠出来光照强的时候 S 和 V 的下限要调低否则车牌会被当成背景丢掉。形态学核(17, 5)是横向长条因为车牌是扁的横向闭运算能把字符之间的缝隙填上。长宽比范围2.5到5.5是经验值新能源绿牌比例略有不同遇到绿牌要把范围放宽到2.2到6.0。3.2 字符分割的投影法怎么用定位到车牌后card_seg.py里做的是字符分割。常见做法是灰度化加二值化然后做垂直投影投影曲线上的波谷就是字符之间的间隙。这个方法对清晰、端正的车牌效果好遇到倾斜或粘连就会翻车。def segment_chars(plate_img): # 转灰度并二值化Otsu 自动找阈值 gray cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 垂直投影统计每一列白色像素个数 projection np.sum(binary 255, axis0) # 找波谷作为分割点阈值取投影均值的 0.15 倍 threshold projection.mean() * 0.15 splits [] in_char False start 0 for i, val in enumerate(projection): if val threshold and not in_char: in_char True start i elif val threshold and in_char: in_char False # 宽度太小的当噪声丢掉 if i - start 5: splits.append((start, i)) chars [binary[:, s:e] for s, e in splits] return chars逻辑说明threshold这个系数是调参重点取大了会把字符切断取小了会把相邻字符粘在一起。0.15是这套代码里的默认思路实际用的时候我一般会打印投影曲线看一眼再定。宽度过滤i - start 5是为了去掉车牌上的螺丝孔和边框产生的假字符。分割出来的字符要统一缩放到固定尺寸再送进 SVM否则特征维度对不上。注意如果车牌有倾斜垂直投影会失效字符会粘成一片。这种情况要么先做倾斜校正要么换连通域分析的方法投影法救不回来。4. SVM 训练与识别样本怎么准备模型怎么调4.1 训练数据目录的组织方式train_dat目录是这套系统的训练数据根目录里面按字符分类建子文件夹每个文件夹放对应字符的样本图。SVM 训练脚本会遍历这些文件夹把图片转成 HOG 特征标签就是文件夹名。这个组织方式很直白但有几个坑。第一样本尺寸要统一。HOG 特征提取要求输入图尺寸一致常见做法是统一缩放到20x40或28x28。如果train_dat里的图尺寸乱七八糟训练脚本要么报错要么特征维度混乱。第二每个字符的样本数量要尽量均衡某个字符只有两三张训练出来的分类器对它基本没识别能力。第三中文车牌第一个字符是汉字汉字分类比数字字母难得多样本不够的话第一个字经常识别错这是传统方法的通病。import os import cv2 import numpy as np from skimage.feature import hog from sklearn.svm import SVC from sklearn.model_selection import train_test_split import joblib def load_dataset(data_dir, img_size(20, 40)): X, y [], [] for label in os.listdir(data_dir): label_dir os.path.join(data_dir, label) if not os.path.isdir(label_dir): continue for fname in os.listdir(label_dir): fpath os.path.join(label_dir, fname) img cv2.imread(fpath, cv2.IMREAD_GRAYSCALE) if img is None: continue img cv2.resize(img, img_size) # HOG 特征orientations 和 pixels_per_cell 按字符大小调 feat hog(img, orientations9, pixels_per_cell(8, 8), cells_per_block(2, 2), block_normL2-Hys) X.append(feat) y.append(label) return np.array(X), np.array(y) def train_svm(data_dir, model_pathsvm_model.pkl): X, y load_dataset(data_dir) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) # RBF 核适合这种中小规模特征C 和 gamma 用网格搜索调更好 clf SVC(kernelrbf, C10, gammascale, probabilityTrue) clf.fit(X_train, y_train) acc clf.score(X_test, y_test) print(f验证集准确率: {acc:.4f}) joblib.dump(clf, model_path) return clf逻辑说明hog的orientations9表示把梯度方向分成 9 个 binpixels_per_cell(8,8)是每个 cell 的大小cells_per_block(2,2)是归一化块。这三个参数决定了特征维度字符图小的时候 cell 要相应调小否则一个 cell 覆盖太多像素细节丢失。SVC的C10是惩罚系数越大越容易过拟合gammascale是自适应核宽度。stratifyy保证训练集和验证集里每个字符的比例一致样本不均衡时这个参数很重要。4.2 识别流程怎么串起来训练好模型后识别流程就是把分割出来的每个字符图缩放到训练时的尺寸提 HOG 特征送进 SVM 预测最后把预测结果按顺序拼成车牌号。这里有个容易忽略的点训练时的预处理要和识别时完全一致训练用了灰度化加 resize识别时也得一样少一步或者顺序不同准确率就掉。def recognize_plate(chars, model, img_size(20, 40)): result [] for ch_img in chars: ch_img cv2.resize(ch_img, img_size) feat hog(ch_img, orientations9, pixels_per_cell(8, 8), cells_per_block(2, 2), block_normL2-Hys) pred model.predict([feat])[0] result.append(pred) return .join(result)逻辑说明model.predict([feat])返回的是列表取第一个元素。拼接顺序就是字符从左到右的顺序所以分割那一步的顺序不能乱。如果识别结果里出现明显不合理的字符组合比如车牌第二位出现汉字说明分割或识别有误可以加一层后处理规则过滤。5. 避坑与排查这套系统最容易翻车的五个地方5.1 图片读取返回 None现象cv2.imread返回None后面所有操作报NoneType错误。原因路径含中文或者文件根本不存在或者 OpenCV 版本对某些格式支持不好。解决把图片路径改成全英文或者用cv2.imdecode(np.fromfile(path, dtypenp.uint8), -1)读中文路径。先打印路径确认文件在。5.2 定位框一堆但没一个是车牌现象候选框几十个全是背景噪声。原因HSV 阈值太宽把背景里颜色相近的区域也框进来了。解决把 S 和 V 的下限提高比如从80提到120缩小颜色范围同时把面积下限从2000提到5000过滤小噪点。调完打印掩膜图看一眼掩膜里车牌区域应该是干净的一块。5.3 字符分割把两个字粘一起现象分割出来的字符宽度明显偏大识别结果少一位。原因车牌倾斜或者字符间距太小垂直投影的波谷不明显。解决先做倾斜校正用cv2.minAreaRect拿到车牌角度再旋转或者改用连通域分析cv2.connectedComponentsWithStats按面积和位置筛选字符区域。5.4 SVM 训练报特征维度不一致现象ValueError: Found array with dim X but expected Y。原因train_dat里图片尺寸不统一HOG 特征维度对不上。解决在load_dataset里强制cv2.resize到统一尺寸并且检查有没有读入失败的图被跳过导致标签和特征数量不匹配。训练前打印X.shape和len(y)确认一致。5.5 界面启动报缺少 DLL 或模块现象运行run.bat或python my_main_ui.py时报ImportError或DLL load failed。原因PyQt5 版本和 Python 版本不匹配或者虚拟环境没激活装到了全局。解决确认虚拟环境已激活pip list看 PyQt5 在不在Python 3.10 以上换 3.9Windows 上如果报platform plugin相关错误把 PyQt5 的plugins/platforms目录加到环境变量QT_QPA_PLATFORM_PLUGIN_PATH。6. 拿自己的数据重训一遍验证这套系统到底行不行的唯一方法预置模型只能证明代码能跑通证明不了它对你的场景有用。我一般拿到这类项目第一件事就是用自己场景的图重训一遍看准确率能到多少心里才有底。具体做法从你的抓拍图里裁出至少 200 张车牌按字符切好每个字符至少 30 张样本数字和字母可以少一点汉字要多补。放进train_dat对应文件夹跑svm_train.py看验证集准确率。这里有个技巧不要一次性把所有样本丢进去训先拿一半训另一半测看哪些字符错得多。错得多的字符单独补样本再训。我实测下来数字和字母在样本够的情况下能到 95% 以上汉字第一个字通常只有 70% 到 85%这是 HOG 加 SVM 的天花板想再高就得上 CNN。验证的时候别只看整体准确率要按字符类别看混淆矩阵。sklearn.metrics.confusion_matrix跑一下你会清楚看到哪个字符老是被认成另一个。比如0和D、8和B、1和I这些形近字符是传统方法的重灾区补样本的时候重点补这些。from sklearn.metrics import confusion_matrix, classification_report # 在 train_svm 里加一段训练完打印详细报告 y_pred clf.predict(X_test) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred))逻辑说明classification_report给出每个字符的精确率、召回率和 F1confusion_matrix给出具体的错分情况。看报告的时候重点看召回率低的字符那就是样本不够或者特征区分度不够的。补样本优先补这些。从那以后我每次拿到这类传统 CV 项目都强制先用自己的数据跑一遍混淆矩阵不看这个数不敢说它能用。希望帮到你。本文还有配套的精品资源点击获取