ARTICLE DETAIL

资讯详情

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

Python车牌识别源码解析:SVM模型与OpenCV图像处理

Python车牌识别源码解析:SVM模型与OpenCV图像处理 简介面向计算机相关专业学生与需要项目实战的开发者这份基于Python的深度学习车牌识别系统源码是一套可直接用于课程设计、期末大作业的完整项目也可作为毕业设计或算法学习的基础工程。系统包含图像预处理、车牌定位、字符分割与识别等核心模块相关脚本与资源组织清晰从图像输入到结果输出都有对应模块承接便于理解算法流程并进行二次扩展。压缩包共18个文件以Python源码为主辅以车牌样例图片、SVM训练数据文件、演示文稿及说明文档整体大小仅4.73MB轻量易部署几乎开箱即用可基于自带图片快速验证识别效果。已有483人学习下载适合希望快速搭建毕设或大作业演示环境的学习者。通过源码可掌握从图像采集到车牌输出的完整实现思路借助附带PPT与README还能快速梳理项目亮点为答辩或报告提供有力支撑。1. 为什么这套“深度学习”车牌识别源码核心其实是一堆SVM模型把“基于Python车牌识别系统深度学习项目源码.zip”解压之后我第一反应是找网络模型文件结果翻遍了也不行——真正的识别主力是svm.dat和svmchinese.dat这两个支持向量机模型Pytorch和TensorFlow的权重一个都没看到。这不是卖家挂羊头而是课程设计场景下非常合理的选型数据量小、答辩要能讲清楚原理、环境搭建不能太折腾SVM在几百张车牌字符样本上的表现依然能打。这套源码能做的事情很直观输入一张带车牌的车辆图片输出一串车牌号字符串中间完整走完定位、分割、识别三步。适合三类人正在赶计算机大作业、需要课程设计源码参考、以及想完整跑通一套图像识别链路而不是只调API的学习者。2. 先拆项目结构两个SVM模型和三段图像管线2.1 文件里谁在干活、谁在辅助、谁可以不用管下载包解开后是一堆文件和文件夹很多人一上来就懵。我按职责把文件分成三类第一轮只需要盯住带.py的文件和两个.dat模型。核心代码是这几个main.py是总入口负责读图、调度、输出结果img_function.py和chuli.py负责图像预处理和车牌定位img_recognition.py负责字符分割和识别img_math.py提供一些数学工具函数。模型文件就两个svm.dat存的是字母和数字分类器svmchinese.dat存的是中文省份简称分类器这是整个识别链路的终点。辅助资料这边车牌识别.pptx通常是答辩用的演示文稿README.md里一般会写运行说明和依赖清单。pic目录里的01.png、02.jpg、car4.jpg、ganzou5.png这些是测试图片我建议你全部保留第一次跑通就用它们别一上来就换自己的图。注意这两个.dat是训练好的模型序列化文件不是配置表。它们决定了“能识别哪些字符”如果你想识别一个模型里没见过的汉字光改代码没用得重训模型。2.2 为什么课程设计选SVM而不是深度卷积网络我见过不少同学拿到源码第一句话是“怎么没有CNN”然后急着想换成ResNet这是没搞清楚作业的评分点在哪里。课程设计和工程项目的区别在于老师更在意你能不能把“数据怎么处理、特征怎么提取、分类器为什么选它”讲清楚而不是你堆了多少层网络。SVM在这个场景里其实很合适。车牌字符是一个典型的“小样本、高区分度”问题字符种类有限汉字31个字母数字34个去掉I和O每个字符的结构差异明显SVM用少量支持向量就能把类别边界划得很干净。换成深度学习卷积网络反而麻烦几百张样本很容易过拟合训练时间又长笔记本跑起来风扇狂转最后答辩时老师问一句“你的网络为什么是这样设计的”你答不上来。另一个实际原因是运行时依赖。这个项目用sklearn就能完成分类器加载和预测不需要装动不动几个G的深度学习框架。你拿去交作业的机器不一定装得了CUDA。2.3 整条识别链路先过一遍从图片到车牌字符串拿到项目之后别急着改代码先按下面的顺序把链路跑通一次确认模型和数据没损坏把整个项目解压到一个纯英文路径下比如D:\PlateRecognition不要让路径里出现中文或者空格。确认svm.dat和svmchinese.dat和main.py在同一个目录下脚本里加载模型用的基本上是相对路径。直接在项目根目录打开命令行执行python main.py观察控制台输出和弹出的窗口。如果报缺少依赖按第3章的依赖清单安装装完再跑。跑通之后换pic目录里另一张图看识别结果是否跟着变这一步能验证代码不是硬编码结果。我第一次拿到这类源码的时候很莽直接把main.py里图片路径改成自己桌面上一张随手拍的照片结果定位框直接框到前挡风玻璃上。后面才意识到车牌识别是有入场条件的图片里的车牌不能被严重遮挡、不能是极端俯仰角、宽度最好在整张图的三分之一以上。跑通别人的测试图是第一步能在合理条件下跑通自己的图才是真的会用。3. 把项目跑起来环境、依赖、路径三个坑一次填平3.1 环境选型Python 3.8 OpenCV 4.x的兼容组合这个项目对Python版本不算挑剔但我建议用3.8到3.10之间的版本太老的新库装不上太新的版本个别编译包可能没轮子。虚拟环境是必须的别直接装进系统解释器后面你还要做别的作业环境冲突了哭都来不及。依赖的核心就五个numpy负责数组运算opencv-python负责所有图像读写和轮廓操作scikit-learn负责加载SVM模型Pillow是辅助图像处理的matplotlib用来显示中间过程。装的时候建议用清华镜像别用默认源硬等。conda create -n plate python3.8 -y conda activate plate pip install numpy1.24.3 opencv-python4.8.1.78 scikit-learn1.3.2 matplotlib3.7.3 Pillow10.1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple固定版本号是我吃过亏后的习惯。opencv-python如果直接装最新版有时候会和numpy的版本冲突报一堆莫名其妙的二进制错误。sklearn的1.3.2系列对SVM的接口支持稳定不会出现predict方法签名变化的问题。Pillow在这套项目里主要做格式兼容比如带透明通道的PNG图片读取。装完之后在命令行里执行python -c import cv2, sklearn, numpy; print(cv2.version, sklearn.version, numpy.version)能正常打印版本号就说明环境干净了。3.2 首次运行main.py把测试图片喂进去环境装好之后第一次运行我建议你先别改任何代码直接用项目自带的测试图跑。在项目根目录打开终端激活虚拟环境后执行conda activate plate cd D:\PlateRecognition python main.py这里有一个很多人会翻车的点如果在IDE里面直接点运行工作目录可能不是项目根目录导致程序找不到svm.dat。所以我一般都会先在终端里cd到项目目录再跑。如果程序正常运行控制台会输出类似“识别结果京A12345”的字符串同时会弹出几个窗口展示中间过程——透视变换后的车牌图、分割后的字符图、最终识别结果。弹出的窗口如果在你屏幕上闪一下就没了说明代码里用了cv2.waitKey(0)按下任意键继续。如果直接报错找不到main.py先检查你是不是在项目根目录——这种低级错误我在实验室见过不下十次都是文件放在桌面命令行还停在C盘用户目录。3.3 换自己的图片注意光照和角度这两个硬条件跑通测试图之后第二步一定是换自己的图。把你要识别的图片放到pic目录下然后在main.py里找到图片路径相关的变量改成纯英文文件名。这一步的重点不在代码而在选图标准。我把这个项目里对输入图片的基本要求整理成了一张表你可以作为换图前的自查清单检查项合格标准不合格表现光照条件均匀自然光或白天拍摄逆光/阴影导致定位框偏移车牌尺寸车牌宽度占图宽30%以上车牌太小导致字符分割失败拍摄角度俯仰角不超过30度车牌透视变形严重字符粘连文件格式JPG、PNG均可推荐JPG带透明通道PNG可能出现颜色通道错乱车牌类型蓝底白字优先黄底黑字需要调整HSV参数换图后如果识别结果不对先把图片路径改回去确认代码本身没问题再去排查图片。这一条排查顺序很重要不然你会把代码改得面目全非最后发现是图片本身就不合格。4. 拆核心代码定位、分割、SVM识别三步连环调用4.1 车牌定位HSV颜色范围筛选蓝色区域整个识别流程第一步是定位。这个项目里比较常见的做法是用HSV颜色空间筛出蓝色像素再用形态学操作把散点连成区域最后按外接矩形的宽高比锁定车牌位置。之所以不用深度学习目标检测是因为车牌颜色特征太稳定了蓝底白字是国标用颜色筛选又快又稳不用训练目标检测模型。import cv2 import numpy as np def locate_plate(img): hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # OpenCV中H通道范围是0-180蓝车牌的H约在100-124 lower_blue np.array([100, 80, 80]) upper_blue np.array([124, 255, 255]) mask cv2.inRange(hsv, lower_blue, upper_blue) kernel np.ones((5, 5), np.uint8) mask cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations2) contours, _ cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) plate_rect None for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 车牌宽高比通常在2.5到4.5之间 if 2.5 w / h 4.5 and w * h 3000: plate_rect (x, y, w, h) break return plate_rect这段代码的要点在于HSV.lower_blue里给的三个数字不是随便填的OpenCV把H通道压缩到0到180所以蓝色区域大约从100到124。S和V设置成80起步是为了过滤掉暗处的蓝色噪声。形态学闭运算的作用是把文字造成的空洞填上让车牌变成一个完整的连通区域。轮廓筛选里我用宽高比2.5到4.5过滤是因为国标车牌的宽高比大约是3:1如果框出来的区域比例明显不对那大概率不是车牌。4.2 字符分割按x坐标排序用宽高比过滤噪声定位到车牌区域之后接下来要把“京A12345”这七个字符一个个切出来。字符分割的思路是先把车牌区域转成灰度图做二值化让文字变白、背景变黑然后通过findContours找到每个字符的外轮廓再按轮廓的x坐标从左到右排序。def segment_characters(plate_img): gray cv2.cvtColor(plate_img, 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) chars [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 过滤掉铆钉、小噪声和贴边轮廓 if w 5 and h 15 and 0.3 w / h 0.9: char_img plate_img[y:yh, x:xw] chars.append((x, char_img)) chars.sort(keylambda item: item[0]) # 按x坐标排序保证字符顺序 return [char_img for _, char_img in chars]这里的参数是血泪经验堆出来的w 5是过滤掉小数点、铆钉这类小物件h 15保证字符高度足够避免把边框噪声当字符宽高比0.3到0.9是字符的典型比例数字1显得瘦一点但不会小于0.3。字符分割后必须按x排序因为findContours返回的顺序和图像位置没有几何关系不排序的话“京A12345”可能会变成“A23451京”这种乱序。4.3 两个SVM接力预测汉字一个模型字母数字一个模型字符分割出来之后最后一步就是让SVM判断每个字符图片是什么。这个项目里有两个模型是有原因的svmchinese.dat识别的是第一个汉字svm.dat识别的是后面的字母和数字。因为省份简称是中文而字母数字是拉丁字符两类字符的形态差异太大了放进同一个模型效果会很差。from sklearn import svm import joblib def recognize_chars(char_imgs): # 加载训练好的两个SVM模型 clf_chinese joblib.load(svmchinese.dat) clf_en_num joblib.load(svm.dat) result [] for i, char_img in enumerate(char_imgs): # 统一缩放到20x20转成灰度拉成一维特征向量 resized cv2.resize(char_img, (20, 20), interpolationcv2.INTER_AREA) gray cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY) features gray.flatten().reshape(1, -1).astype(float32) / 255.0 if i 0: pred clf_chinese.predict(features)[0] else: pred clf_en_num.predict(features)[0] result.append(pred) return .join(result)这里有一个细节值得注意features必须先归一化再预测把像素值从0到255缩放到0到1之间。训练SVM的时候如果用了归一化预测时不归一化结果会偏移得很离谱。我见过有人直接用原始像素值预测识别率直接从90%掉到50%还以为是模型坏了。另一方面predict方法返回的是数组取[0]才能拿到字符值。4.4 main.py怎么把三块串成一条流水线整个识别流程在main.py里的编排逻辑很清晰读图 → 定位 → 透视矫正 → 分割 → 识别 → 输出。透视矫正是个容易被忽略的环节拍摄角度如果不是完全正对车牌切出来的字符会是斜的直接送进SVM识别会出错所以拿到定位矩形后多半会做一个透视变换把倾斜的车牌拉正。img cv2.imread(pic/01.jpg) plate_rect locate_plate(img) x, y, w, h plate_rect # 适度外扩把边框也包进去避免把边缘字符切断 x1 max(0, x - 5); y1 max(0, y - 5) x2 min(img.shape[1], x w 5); y2 min(img.shape[0], y h 5) plate_img img[y1:y2, x1:x2] chars segment_characters(plate_img) if len(chars) 7: # 标准车牌7个字符 plate_text recognize_chars(chars[:7]) print(识别结果:, plate_text)注意这里有一个隐藏逻辑先判断chars长度是否大于等于7再取前7个。这是为了防止铆钉噪声混进来导致字符数量超过7宁可丢掉尾部多余字符也不能乱序。如果你下载的版本里没有透视矫正环节识别率会差一截可以自己补一段cv2.getPerspectiveTransform。5. 避坑与常见问题六个翻车现场和对应的下一步5.1 报错FileNotFoundError: svm.dat文件找不到现象运行main.py时崩溃提示找不到svm.dat或svmchinese.dat。原因99%是工作目录不对。IDE运行的时候默认目录不在项目根目录程序用相对路径去找dat文件自然找不到。还有少部分情况是文件被解压时放到了子目录里。解决在main.py里改成绝对路径定位这样不管在哪运行都不会错。比较稳的写法是取当前文件所在目录再拼接import os base_dir os.path.dirname(os.path.abspath(__file__)) model_en os.path.join(base_dir, svm.dat) model_ch os.path.join(base_dir, svmchinese.dat)5.2 定位框框到了挡风玻璃或车身现象程序跑起来没有报错但是画出的定位框不在车牌上识别结果是一串乱码。原因HSV颜色阈值太宽或者图片里车身颜色和车牌底色接近。蓝色阈值设得宽车身蓝色区域就会和车牌连成一片。解决收窄HSV范围然后提高面积门槛。先调lower_blue的S和V值从80提到100以上能过滤掉大部分浅色噪声。如果还是框错打印所有候选轮廓的宽高比看看筛出来的除了车牌还有什么东西再针对性加过滤条件。5.3 分割出来的字符数量远多于7个现象识别结果不是车牌号而是一长串字符或者程序在print的时候输出乱码。原因车牌上的铆钉、边框碎片、脏点被当成了字符轮廓。字符分割的过滤条件太松。解决把w 5和h 15这两个过滤阈值提高观察你的测试图里真实字符的像素尺寸按实际值设定。还有一招比较实用直接限制最大字符数超过7个就按面积排序取最大的7个轮廓因为这7个一定要是文字而不是噪声if len(chars) 7: chars sorted(chars, keylambda item: cv2.contourArea(cv2.convexHull(item[1])), reverseTrue)[:7]5.4 图像显示颜色偏红偏蓝越看越不对劲现象用matplotlib显示图像时原本蓝色的车牌变成了橙色红色文字变成了蓝色。原因OpenCV读图默认是BGR通道顺序matplotlib的imshow默认按RGB显示通道没有转换。解决显示之前做一步通道转换。import matplotlib.pyplot as plt img_bgr cv2.imread(pic/01.jpg) img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) plt.imshow(img_rgb)这个坑特别隐蔽因为它不影响最终的识别结果——SVM吃的是灰度图通道顺序不影响灰度转换——但如果你要在答辩PPT里展示中间过程图片颜色是反的老师一眼就看出来了。5.5 sklearn版本太新导致predict方法行为变化现象程序运行到predict时报警告或者返回的结果维度不对无法拼成字符串。原因新版本sklearn里SVC.predict对单条样本返回的shape和旧版本不完全一致有的项目代码是按照一维数组写的。解决给特征向量显式reshape(1, -1)这一步本来就应该做。如果报警告检查sklearn版本是否高于1.3高版本建议固定到requirements.txt里写的版本。5.6 换了一张光线很强的图片定位直接失败现象换图后连定位框都画不出来程序侧直接返回None。原因强光下蓝色车牌的饱和度下降HSV里S分量变低低于了阈值下限。解决把定位函数里的HSV阈值做成可调参数或者对图片先做一次直方图均衡化提升对比度。均衡化之后再走HSV筛选通常能把浅色车牌重新拉回来。gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray_eq cv2.equalizeHist(gray) img_eq cv2.cvtColor(gray_eq, cv2.COLOR_GRAY2BGR)6. 识别率还能往上顶量化评估、特征升级和答辩话术项目跑通只是及格线拿高分还得往下挖一层。我建议你写一个批量测试脚本把自己拍的或者网上下载的几十张车牌图跑一遍算一个准确率数字。注意这里的准确率不是“整个车牌识别完全正确”的比例而是区分成“定位成功率”和“字符识别率”两个指标——因为作业里这两个环节是分开评估的。import os correct_char 0 total_char 0 correct_plate 0 total_plate 0 for img_file in os.listdir(test_imgs): plate_text, expected run_plate_recognition(os.path.join(test_imgs, img_file)) total_char len(expected) total_plate 1 for c1, c2 in zip(plate_text, expected): if c1 c2: correct_char 1 if plate_text expected: correct_plate 1 print(字符级准确率:, correct_char / total_char) print(车牌级准确率:, correct_plate / total_plate)拿到这个数字之后提升方向有三个。第一个是加数据重训SVM把不同光照、不同字体的车牌字符截图收集起来扩充训练集这是效果最明显的操作。第二个是调SVM超参数重点看C和gamma两个值在sklearn里可以用GridSearchCV暴力搜一遍资源耗得不多但分数能涨一点。第三个是换特征当前项目用的是原始像素拉平你可以改成HOG特征对边缘和纹理的表达能力强很多字符识别的鲁棒性会明显改善。答辩的时候如果老师问“为什么不用深度学习”你可以这样回应车牌字符是标准化印刷体类别之间差异大、类内差异小SVM在少量样本上就能学到稳定边界而且支持向量和决策边界都可以可视化便于解释和验证。这套说辞既尊重了原项目设计也展示了你的工程判断力比硬吹深度学习更经得住追问。我自己拆过太多课程设计源码最大的教训就是拿到项目先跑通、再改图、最后才读代码——顺序反了你会在状态里陷一天。从那以后我每次拿到新的源码都会先把main.py跑一遍把自己的一张图换进去确认链路真实可用再开始看核心算法。希望帮到你。本文还有配套的精品资源点击获取
返回列表