
简介这是一个基于Python与OpenCV实现的车牌识别系统毕业设计工程包面向计算机视觉、人工智能方向的高校毕业生也适合用作期末大作业或课程设计参考。项目完整覆盖车牌图像采集、预处理、定位、字符分割与识别等关键流程对RGB/HSV色彩空间转换、边缘检测、形态学处理和SVM分类均有工程实现已通过指导教师验收并获高分零基础学习者也能顺利运行。包内共81个文件以Python源码、OpenCV模型配置xml与dat、车牌样本图片jpg/png及可直接启动的exe程序为主整体大小约67.93MB目录结构清晰便于按模块阅读和二次开发。目前已有590人学习使用除了可直接运行的完整工程外还包含训练好的模型参数与测试图片既能用于毕业设计答辩演示也可作为智能交通场景的算法实践基础。1. 基于pythonOpenCV的车牌识别系统这套毕设源码能做什么拿到“基于pythonOpenCV的车牌识别系统”这个毕设项目先别急着跑 main.py。我拆过不少同类源码这套项目的结构是典型的“图像预处理 → 车牌定位 → 字符分割 → SVM 识别”四段式文件里既有 Python 源码main.py、img_recognition.py、RBG_HSV.py也带了一整套可直接运行的 exe 和两个关键模型文件 svm.dat、svmchinese.dat属于那种能直接交差、也能拿来二次开发的完整度比较高的毕设资源。它解决的痛点很明确给你一条从普通图片到输出车牌字符串的完整链路省掉你自己去调 OpenCV 算子和训练 SVM 的时间。适合正在做计算机视觉方向课程设计、毕业设计的人也适合想快速上手 OpenCV 车牌识别的初学者。下面我从定位、分割、识别这三段逐个拆。2. 车牌定位先想清楚靠边缘还是靠颜色车牌定位是整个识别系统里最影响成败的一步定位框画偏了后面分割和识别做得再漂亮都没用。这个项目里同时出现了边缘检测类和颜色转换类img_function.py、RBG_HSV.py其实就是给了两套定位思路一种靠车牌区域的纹理边缘特征一种靠车牌底色蓝牌为主的 HSV 颜色范围。实际跑的时候你会发现两套方法各有各的适用前提。2.1 图像预处理灰度、滤波、二值化的参数怎么定不管是走边缘还是走颜色第一步都是把原始图片转成适合 OpenCV 后续处理的格式。项目里 chuli.py 承担的就是这部分脏活读图、转灰度、降噪、二值化。常见的处理链路是这样的import cv2 import numpy as np def preprocess(src_path): # 读取 BGR 原图 img cv2.imread(src_path) # BGR 转灰度车牌识别不需要颜色信息时先用灰度降低计算量 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯滤波去噪ksize 越大越模糊5x5 是千万级像素下的折中值 blur cv2.GaussianBlur(gray, (5, 5), 0) # 用自适应阈值代替固定阈值能扛住光照不均 binary cv2.adaptiveThreshold( blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 19, # 邻域块大小奇数太大会丢失字符细节 9 # 常数 C越大越容易剔除噪点 ) return img, gray, binary这段代码里最值得关注的是adaptiveThreshold而不是threshold。固定阈值 127 这种写法在实验室灯光下没问题一旦图片里有阴影、反光固定阈值会直接把车牌区域切成黑白花斑后面定位就跟着崩。自适应阈值会根据每个像素邻域算出一个局部阈值抗干扰能力强得多。参数上块大小 19 和常数 C9 是针对 500~800 像素宽度的图片调出来的。如果你的测试图片分辨率更高块大小要跟着加大图片更小块大小要减小。否则要么字符糊成一团要么背景噪点全被当成前景。2.2 边缘检测与形态学操作候选车牌区域是怎么框出来的车牌区域有一个很稳定的纹理特征字符密集、边缘丰富、背景相对单调。所以用 Sobel 或者 Canny 先找边缘再用形态学闭运算把边缘连成一块连续的连通域最后按轮廓外接矩形的宽高比筛掉非车牌区域这是经典做法。项目里的 img_function.py 走的差不多就是这条路线。def locate_by_edge(binary): # 边缘检测Canny 双阈值低阈值用于连接高阈值用于抑制噪声 edges cv2.Canny(binary, 100, 200) # 闭运算先膨胀后腐蚀把断裂的字符边缘粘连成完整区域 kernel cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5)) closed cv2.morphologyEx(edges, 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 / h # 标准蓝牌宽高比约 3.14实际允许 2.5~4.0 的浮动 if 2.5 ratio 4.0 and 200 w * h 20000: candidates.append((x, y, w, h)) return candidates注意Canny(binary, 100, 200)的两个阈值一个是低阈值 100一个是高阈值 200。低于 100 的梯度直接丢弃高于 200 的肯定是边缘介于两者之间的看是否与高阈值边缘相连。这个比例一般是 1:2 到 1:3 之间。你在项目里如果发现车牌边缘断得太碎就把低阈值降到 60如果噪声太多就把高阈值拉到 250。形态学闭运算的 kernel 设成(17, 5)是有讲究的。车牌是横向的矩形字符横向排列所以你希望把同一行里的字符边缘横向连起来而不是把上下相邻的区域也粘到一起。17 的宽度对 500px 宽的图来说大约能覆盖 1.5~2 个字符间距5 的高度刚好容下字符高度。2.3 HSV 颜色筛选RBG_HSV.py 里到底做了什么边缘定位的弱点是背景里如果存在栅栏、树叶、车窗缝隙这类纹理丰富的区域会产生大量假候选框。这时候颜色信息就是更硬的条件。中国蓝牌的主色调非常稳定在 HSV 色彩空间里可以用一个窄范围把它圈出来。这也是 RBG_HSV.py 这个模块的核心职责。def locate_by_color(img): # 原图是 BGR先转成 HSV 再筛颜色 hsv cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 蓝色车牌典型范围H 100~124S 90~255V 90~255 lower_blue np.array([100, 90, 90]) 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) # 如果检测到的蓝色区域接近整张图说明阈值太宽直接跳过 if 2.0 w / h 4.5 and w img.shape[1] * 0.15: candidates.append((x, y, w, h)) return candidates这里有个最容易翻车的地方HSV 的下界和上界不是随便写的。OpenCV 里 H 通道范围是 0~179不是 0~360所以蓝牌的 H 值区间是 100~124不是 200~248。很多新手直接把别的语言例程里的 HSV 范围搬过来结果一张图都识别不出来就是因为没换算 OpenCV 的 H 范围。另外w img.shape[1] * 0.15这个条件是过滤“全屏变蓝”的情况。如果输入的图片里天空占比很大而且 HSV 阈值过宽整个天空都会被划成候选区域这个宽度比例条件能把这种明显不合理的候选过滤掉。颜色定位和边缘定位不冲突正常项目里是两者结合先用颜色粗定位再在候选区域里做边缘验证能同时过滤掉纹理噪声和颜色噪声。3. 字符分割与 SVM 识别svm.dat 和 svmchinese.dat 的分工定位到车牌区域之后下一步是把它统一校正成标准矩形然后切成单个字符逐个丢给识别模型。字符分割质量决定了识别准确率的上限而 SVM 模型的成熟度决定了这个上限能兑现多少。这个项目里最值钱的其实不是定位代码而是那两个训练好的模型文件svm.dat 和 svmchinese.dat。3.1 字符分割投影法是这套项目里最稳的方案分割的输入是一张已经裁出来的车牌图。第一步要把车牌转正因为摄像头拍摄角度不可能完全正面。项目里通常会用透视变换把任意四边形映射到固定尺寸比如 440x140 的标准车牌宽高比然后再做二值化和分割。def split_chars(binary_plate): # 去掉车牌上下边框先做一次水平投影统计每行白点数量 row_sum np.sum(binary_plate, axis1) # 找到字符区域的起始和结束行 row_idx np.where(row_sum 0)[0] if len(row_idx) 0: return [] top, bottom row_idx[0], row_idx[-1] plate_clean binary_plate[top:bottom 1, :] # 去掉左右边框后进行垂直投影 col_sum np.sum(plate_clean, axis0) col_idx np.where(col_sum 0)[0] left, right col_idx[0], col_idx[-1] plate_clean plate_clean[:, left:right 1] # 垂直投影每一列求和字符之间有明显的零值列 col_sum_clean np.sum(plate_clean, axis0) chars [] in_char False start 0 for i, val in enumerate(col_sum_clean): if val 0 and not in_char: # 进入一个字符 in_char True start i elif val 0 and in_char: # 字符结束切出这一段 w i - start if 10 w 80: # 过滤掉噪声点造成的过窄片段 char plate_clean[:, start:i] chars.append(char) in_char False return chars垂直投影的原理是统计每一列上的白点数量。字符笔画密集所以字符所在列的投影值很高字符之间的缝隙没有笔画投影值为 0。通过扫描投影值的“高→零→高”变化就能把每个字符切出来。这套代码在白色背景、黑色字符的干净车牌上效果很好但有两个前置条件一是二值化必须保证字符是白色、背景是黑色二是车牌边框必须提前去掉否则边框的投影会把所有字符连成一片。分割的时候10 w 80这个宽度过滤很关键。在 440 宽的车牌图上单个字符宽度差不多在 40~60 像素如果出现一个宽度只有十几像素的片段那基本是噪点或者螺丝钉。但也要小心过滤条件设得太死把掉漆严重的字符直接丢掉了。我一般会先打印出所有切片的宽度看看分布再调阈值。3.2 SVM 模型文件svm.dat 和 svmchinese.dat 各管哪一部分国内车牌由 7 个字符组成第 1 位是省份简称的汉字第 2 位是发牌机关字母后 5 位是字母和数字混合。新能源车牌是 8 位多了第 6 位和最后一位的区分规则。汉字和英文字母数字的形态差异很大所以项目里拆成了两个模型模型文件识别对象类别数加载方式svm.dat0~9 数字和 A~Z 字母不含 I、O34cv2.ml.SVM_load(svm.dat)svmchinese.dat省份简称汉字如 京、沪、苏、粤31cv2.ml.SVM_load(svmchinese.dat)这里要理解一个核心概念OpenCV 的 SVM 不能直接吃图片它只吃固定长度的特征向量。项目里字符图片一般是统一缩放成固定尺寸常见的是 20x20 或 32x32然后把像素值拉成一维数组作为特征。灰度图 20x20 拉平就是 400 维的特征向量32x32 就是 1024 维。SVM 本质上是在高维空间里找分类超平面特征向量越长能区分的细节越多但计算量也越大而且对小样本更容易过拟合。import cv2 import numpy as np def load_models(): # 路径尽量用绝对路径exe 场景下相对路径极易踩坑 svm_eng cv2.ml.SVM_load(svm.dat) svm_cn cv2.ml.SVM_load(svmchinese.dat) return svm_eng, svm_cn def predict_char(model, char_img, size(20, 20)): # 字符图统一缩放保持长宽比会引入黑边直接拉伸更常见 resized cv2.resize(char_img, size, interpolationcv2.INTER_AREA) # 归一化到 [0,1]SVM 对特征尺度敏感 resized resized.astype(np.float32) / 255.0 # 拉伸成一维特征向量reshape(-1) 自动计算长度 feature resized.reshape(1, -1) # OpenCV SVM 的 predict 返回 (结果, 置信度) _, result model.predict(feature) return int(result[0][0])注意resize用的INTER_AREA这是缩小图片时效果最好的插值方式放大时才用INTER_LINEAR或INTER_CUBIC。归一化到 0~1 而不是直接用 0~255 的像素值是 SVM 的一个常见调优手段。因为像素值范围越大某些特征的权重就越容易被大数值主导归一化能让所有维度的尺度一致。3.3 识别主流程img_recognition.py 里的调用链把定位、分割、识别串起来就是 img_recognition.py 的完整职责。这个模块的输入是一张完整图片输出是车牌字符串。它的调用顺序如下def recognize_plate(image_path): # 第一步读图和预处理 img, gray, binary preprocess(image_path) # 第二步定位候选车牌区域颜色 边缘双重验证 candidates locate_by_color(img) if not candidates: candidates locate_by_edge(binary) # 第三步对每个候选区域做透视校正和字符分割 svm_eng, svm_cn load_models() for x, y, w, h in candidates: plate img[y:y h, x:x w] # 透视校正省略这里直接假设正面拍摄 binary_plate binarize_plate(plate) chars split_chars(binary_plate) if len(chars) ! 7: # 不是 7 个字符跳过这个候选 continue # 第四步逐个字符用对应模型预测 result [] for i, char_img in enumerate(chars): if i 0: # 第一个字符一定是汉字 result.append(predict_char(svm_cn, char_img)) else: # 其余是字母或数字 result.append(predict_char(svm_eng, char_img)) return .join(result) return None这里面有一个很使用的小技巧利用车牌格式的固定结构来补强模型。第 1 位字符固定是省份汉字所以你直接用汉字模型第 2 位固定是字母不是数字后 5 位才是字母数字混合。哪怕后 5 位里混进了和字母相似的数字比如 0 和 O这种结构约束也能在拼接结果时帮你纠错。另一个值得注意的地方是len(chars) ! 7的过滤。现实中很多图片定位出来的候选框根本不是车牌或者车牌掉漆导致字符切不全这个判断能快速跳过一批假候选。但你也要意识到这个硬性条件对新能源 8 位车牌不友好后面我再说怎么改。4. 从源码到可运行环境准备与 main.py 运行流程源码结构清楚了接下来就是把它跑起来。这一步看似简单实际上环境问题比代码问题更折腾人。这个项目里既有纯 Python 源码又有打包好的 exe两条路的踩坑点完全不同。4.1 环境准备python、OpenCV 和 numpy 的版本组合先从 Python 环境说起。这个项目的代码风格是典型的毕业设计写法对 Python 版本不挑3.6 到 3.10 基本都能跑。但 OpenCV 版本很挑OpenCV 4.x 以后cv2.ml.SVM_load的调用方式基本没变但底层的 SVM 模型文件格式有兼容性风险。如果 svm.dat 是用旧版 OpenCV 训练出来的拿新版直接加载可能会报错。我一般建议先建一个虚拟环境不要直接往系统 Python 里装包毕设项目依赖经常互相打架。# 创建虚拟环境python 3.8 是比较稳的选择 python -m venv vlpr_env # Windows 下激活 vlpr_env\Scripts\activate # 安装依赖注意版本号 pip install opencv-python4.5.5.64 pip install numpy1.23.5opencv-python这个包名很奇怪但装上之后 Python 里的导入名是cv2不是opencv。很多新手报ModuleNotFoundError: No module named cv2就是因为用pip install opencv去装结果装了个不相关的包。至于版本我选 4.5.5 而不是最新版是因为它同时兼容旧的 SVM 模型文件格式和新的 Python API是这类毕设项目里最不容易出事的版本。numpy 的版本也值得注意。OpenCV 4.5.5 搭配 numpy 1.23.5 是比较经典的组合numpy 2.x 出来后有些 API 变了会导致cv2.findContours返回值的解析方式出问题。4.2 运行 main.py从加载图片到输出车牌的完整过程环境装好后先看看 main.py 的入口逻辑。这类项目的 main.py 通常做四件事解析命令行参数、读取图片、调用识别函数、打印结果。代码结构大概是这样的def main(): # 项目里 img.png 是自带的测试图片 image_path img.png # 调用识别主流程 plate recognize_plate(image_path) if plate: print(识别结果:, plate) else: print(未检测到车牌) if __name__ __main__: main()运行方式很简单cd 项目目录 python main.py第一次跑的时候我建议你把img.png换成自己拍的清晰车牌照片不要急着换复杂场景。先用一张干净的、正面拍摄、光线均匀的蓝牌图片验证整条链路能通。如果这张图都识别不出来优先检查定位环节而不是模型。main.py 里还有一个细节它可能会调用chuli.py里的函数做图像变换也可能会直接调img_recognition.py。这两个模块的边界在项目里不一定分得很清楚你要在 IDE 里用断点或者print调试的话先从recognize_plate这个函数往里看就行没必要一开始就纠结每个小函数是干什么的。4.3 直接跑 exe黑盒运行的边界在哪项目带的 exe 目录里有main.exe、svm.dat、svmchinese.dat和一个 pic 子目录这说明它打包时是把整个运行环境都塞进去了。好处是省去装 Python 的麻烦坏处是黑盒。exe 的运行有几个硬性条件exe 和两个 svm 模型文件必须放在同一个目录下。因为打包后的程序找模型文件用的是相对路径你在桌面双击这个 exe它会在当前工作目录找 svm.dat找不到就直接闪退。正确做法是打开 exe 所在的文件夹在里面双击运行或者用命令行cd exe .\main.exe用命令行跑的好处是即使闪退你能在终端里看到具体的报错信息而双击运行时窗口一关就什么都没了。另外exe 里集成的 Python 环境是打包那一刻的快照你没法给它升级 OpenCV也没法换模型文件。如果你想换一个自己训练的 svm.dat直接覆盖同名文件是能起作用的因为程序按相对路径加载这两个文件。换完如果效果变差再把原文件改回去这就是黑盒运行里保留的“后悔药”。5. 常见问题与避坑五个必踩的坑和对应解法跑完一轮之后你会发现这个项目 70% 的问题不出在算法而出在环境和路径。我把实际运行中最容易遇到的五个问题按“现象 → 原因 → 解决”列出来照着排查能省半天时间。5.1 ModuleNotFoundError: No module named cv2现象执行python main.py控制台直接报ModuleNotFoundError: No module named cv2。原因当前 Python 环境里没装 OpenCV或者装了不正确的包。还有人用pip install opencv这个包不是 OpenCV 本体装上后依然找不到cv2。解决用下方命令安装正确的包。安装完成后用python -c import cv2; print(cv2.__version__)验证当前环境能导入。pip uninstall opencv -y pip install opencv-python4.5.5.64 python -c import cv2; print(cv2.__version__)这里要特别提醒如果你开了一个 IDE比如 PyCharm它可能默认使用虚拟环境而你在系统终端里 pip 安装的包进不到 IDE 的解释器里。检查 PyCharm 右下角解释器路径确保和你装包的环境是同一个。5.2 加载 svm.dat 时报错或者返回空模型现象程序执行到cv2.ml.SVM_load(svm.dat)时没有异常但后面调用predict报错或者svm_eng直接是空对象。原因路径不对或者 OpenCV 版本与模型序列化版本不匹配。SVM 模型文件里有 OpenCV 的版本信息新版 OpenCV 加载旧格式文件时可能静默失败不报错但返回空模型。解决先确认当前工作目录再用绝对路径加载。程序里直接写绝对路径是最省事的方式import os # 把模型路径指向项目根目录避免 cmd 工作目录不同导致找不到文件 base_dir os.path.dirname(os.path.abspath(__file__)) svm_eng cv2.ml.SVM_load(os.path.join(base_dir, svm.dat)) svm_cn cv2.ml.SVM_load(os.path.join(base_dir, svmchinese.dat))如果确认路径没问题还是加载失败就换个 OpenCV 版本试试。优先试 4.5.x不行再降到 4.1.x总有一个能兼容。5.3 数字字母都能识别但汉字识别出来是乱码或空现象定位、分割都正常前 6 个字符识别正确第 7 位之前的位置也就是首位汉字识别结果明显不对或者输出一堆乱码。原因车牌字符串拼接时没有区分汉字模型和字母数字模型。如果代码里对整整 7 个字符都用svm.dat预测汉字就会被硬塞进字母数字的类别空间里因为svm.dat训练的时候根本没见过汉字样本。解决首位汉字强制走svmchinese.dat。如果你的分割结果总是 7 个字符第一位固定是汉字补齐这个判定逻辑# i 0 时必须用汉字模型字符只有 7 位第一位不能当字母预测 if i 0: label predict_char(svm_cn, char_img) else: label predict_char(svm_eng, char_img)还有一种情况是分割时第一个字符没切出来整个车牌只剩 6 个字符被前面的len(chars) ! 7过滤掉了。这种问题不在模型在分割参数的宽度阈值。5.4 自带测试图能识别换一张图就识别不出来现象跑项目自带的img.png一切正常换成自己用手机拍的图要么定位不到车牌要么定位到了但分割乱掉。原因这套项目的定位参数是围绕“正面、光照均匀、蓝牌”调出来的。手机照片可能有透视畸变、大面积反光、车牌占画面比例太小或者偏色严重这些都会让 HSV 阈值和形态学 kernel 失灵。解决按顺序调整三个地方。先把 HSV 蓝牌阈值放宽比如 H 下界从 100 降到 95再看定位框是否框住了整个车牌最后在分割前加一道透视校正。如果透视畸变大投影法会直接把倾斜的字符切成斜条# 用最小外接矩形得到倾斜角度再做仿射变换扶正 rect cv2.minAreaRect(cnt) angle rect[-1] if angle -45: angle 90 angle M cv2.getRotationMatrix2D(rect[0], angle, 1.0) rotated cv2.warpAffine(plate, M, (plate.shape[1], plate.shape[0]))这个旋转修正对手机拍出来的斜车牌效果非常明显是提升换图识别率的最有效手段。5.5 双击 exe 闪退命令行下能跑但看不到输出现象双击main.exe后窗口一闪而过啥也没看到。原因程序异常退出。常见原因有两类一类是 exe 的工作目录不对找不到svm.dat另一类是识别函数里某个 OpenCV 调用在特定图片上抛异常没有被捕获。解决先用命令行运行cd exe .\main.exe这样至少能保留异常信息。如果是模型路径问题把 exe、svm.dat、svmchinese.dat 放同一目录再试。如果是识别异常就要回到 Python 源码里给关键函数加 try-excepttry: plate recognize_plate(test_img) except Exception as e: print(f识别异常: {e})这种兜底代码在毕设答辩演示时特别有用至少不会在评委面前弹一个难看的崩溃窗口。6. 调试验证与二次开发把识别率从“偶尔对”调到“稳定对”6.1 准备一套自己的验证集项目自带的单张测试图只能证明链路通不能证明系统稳定。我的习惯是从网上下载或者自己拍摄 20~30 张不同场景的车牌图按难度分成三档清晰正面照、轻微倾斜照、逆光或反光照。每次改完参数就把这组图全跑一遍统计正确识别率。这个动作能让你知道当前改动到底是在改进还是在倒退而不是靠一张图“感觉这次好像行”。跑批量测试时别一张张肉眼盯写个简单的脚本把结果汇总起来import os test_dir test_imgs for name in os.listdir(test_dir): path os.path.join(test_dir, name) plate recognize_plate(path) print(f{name}: {plate})输出结果里如果某张图返回None单独拿去调试定位环节如果返回了但字符串里有错字单独调试分割和识别环节。定位问题、分割问题、识别问题三者的现象是完全不一样的。6.2 可视化调试把中间结果写到图片上不看中间结果就去改参数基本是玄学调试。我把每步中间结果都画到图上一眼就能看出是哪一环断了。在调试代码里加入可视化输出def debug_show(img, candidates, result_text): # 把候选框画到原图上确认定位是否准确 for x, y, w, h in candidates: cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), 2) # 在图片顶部叠加识别结果 cv2.putText(img, result_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 2) cv2.imwrite(debug_result.jpg, img)打开debug_result.jpg后你会快速形成判断绿色框没框住车牌是定位参数的问题框住了但识别错字是分割或模型的问题识别全错则要考虑是不是模型特征匹配不上。定位、分割、识别这三个模块完全可以独立调试各自调好了整体准确率自然上去。6.3 扩展成新能源车牌支持新能源车牌是多一位的 8 位字符而且第二位和最后一位之间有一条分割点。如果你想把这套毕设拿去做课程设计升级版改动点是分割时不要硬性要求len(chars) 7改成放行 8 个字符识别时前 7 位规则不变第 8 位加一个分类标记即可。核心的 SVM 模型不用重新训练因为新能源车牌第 8 位的字符集没有超出原来的字母数字范围。我的习惯是每拿到一套开源毕设源码先不追求跑通而是先把定位框和分割结果可视化出来确认这两步质量达标后再谈识别率。从那以后我每次跑同类 OpenCV 识别项目都强制先做一遍视觉化调试已经替我省掉了好几晚排查问题的时间。这套基于 python 和 OpenCV 的车牌识别系统思路清晰文件也不乱照着上面的流程走一遍基本能在一个晚上把整条链路跑通希望帮到你。本文还有配套的精品资源点击获取