ARTICLE DETAIL

资讯详情

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

OpenCV+SVM车牌识别系统:定位、分割与字符识别全流程解析

OpenCV+SVM车牌识别系统:定位、分割与字符识别全流程解析 简介一套基于OpenCV与SVM的车牌识别系统毕业设计资源面向计算机视觉、机器学习方向的本科生及开发者适用于课程设计、毕业设计或技术入门。压缩包为7z格式大小15.4MB文件总数暂未标注主要文件类型包括项目源码、论文文档和答辩PPT源码可直接运行或二次开发论文文档有助于撰写设计报告答辩PPT便于汇报演示。系统实现涵盖车辆图像灰度化、二值化、去噪等预处理利用OpenCV进行边缘检测与轮廓提取以定位车牌区域再通过SVM对分割后的字符进行特征分类与识别最终组合输出完整车牌号码整个流程封装为函数便于复用。此外还包括字符分割与单独识别等提升准确率的辅助技术并给出训练数据集学习字符特征关系的完整思路。已有921人学习对需要快速搭建车牌识别原型、理解SVM分类原理或完成毕业设计答辩的读者具有实用价值。1. 车牌识别不是黑匣子机器学习、OpenCV 和 SVM 各管哪一段拿到“机器学习 OpenCV SVM 的车牌识别系统”项目包的人基本逃不过两种情况准备毕业论文或者课程大作业需要现场演示。包里源码、论文、答辩 PPT 三件套齐了依赖装好一跑第一张图就翻车的情况一点也不少见——蓝牌定位框贴到保险杠上识别 7 个字符错 3 个你还不知道从哪改起。其实这套组合拆开看每个词管一段OpenCV 管图像处理负责把车牌区域从复杂背景里抠出来再把字符一个个切开机器学习定义整个方案的组织方式样本标注、特征工程、训练验证都得按它的规矩来SVM 是最终做字符分类的算法吃进特征向量吐出“这是京、这是沪、这是 A 还是 5”。它适合刚做完图像处理入门、想完整走一遍传统视觉流程的学生也适合需要在答辩现场把每个中间步骤都讲清楚的场合。下面按定位、分割、特征、分类的顺序拆开讲。2. 车牌识别系统怎么拆五个环节一条链路2.1 为什么是“OpenCV SVM”而不是直接端到端 CNN一部分人上来就问车牌识别都成熟到能端到端部署了怎么还用 SVM 这种“老算法”这个问题答辩老师十有八九会提你得事先把回答想清楚。车牌识别是一个高度标准化的视觉任务相机拍下来的字符字体固定、结构固定、颜色固定多数就是蓝底白字、7 个编码位。这种任务不需要模型从头学习“车牌长什么样”先做好定位和分割把高不确定性的检测问题降维成低不确定性的字符分类问题再把分类交给 SVM数据量小也能训出可用模型。SVM 在这种几十到几百维的特征空间上训练很快推理也用不上 GPU一台普通笔记本就能跑完整个系统。对答辩来说SVM 的决策边界、软间隔、核函数、惩罚参数 C 这些知识点都是课程学过、能对应上的讲原理比对着黑盒模型念参数轻松得多。深度学习当然能做得更好但那是另一套工作量目标检测做定位、CNN 做字符分类、大样本标注、调参成本全部上台阶。做课设和毕设先跑通传统链路再谈增强性价比最高。如果你后续想升级SVM 这一环换成 CNN 分类器、定位换成 YOLO管线接口都不用改这正是分环节设计的红利。2.2 车牌结构先定死7 位编码和类别空间决定你怎么切下车牌识别项目一大半时间可以省下来只要你先把普通号牌的组织规则钉死。普通蓝牌的结构是“省份汉字 发牌机关字母 五位字母/数字”共 7 个有效字符中间有一个分隔圆点但不参与识别。省份汉字常见 31 个京、津、冀、晋、蒙、辽、吉、黑、沪、苏、浙、皖、闽、赣、鲁、豫、鄂、湘、粤、桂、琼、渝、川、贵、云、藏、陕、甘、青、宁、新发牌机关字母约 24 个避开 I 和 O防止与 1 和 0 混淆后五位字母数字混合同样避开 I 和 O。类别空间粗略一算就是 31 24 10 65 个类别这是后面 SVM 训练时的标签表。相当多项目源码的 train 文件夹就是这样组织的每个类别一个子文件夹文件夹名就是字符本身。这样训练代码不用维护单独的标签映射文件目录名直接对标签省掉一个很容易写错的地方。另一个要提前定的是颜色。蓝底白字最常见新能源车牌是渐变绿底黑字黄牌是货车和教练车。多数毕业设计只做蓝牌因为蓝底在 HSV 色彩空间里最稳定、阈值最好写。你下载的源码包只要定位环节写的是蓝色阈值把测试图换成黄牌或绿牌效果崩塌很正常不是代码坏了是颜色阈值没有覆盖这些场景。2.3 五个环节的数据流输入输出先定好再写代码施工之前我建议你先把数据流图画出来原始图像 → 车牌定位 → 车牌校正裁剪 → 字符分割 → 特征提取 → SVM 分类 → 字符序列输出。不同环节对图像格式要求不一样接口先定好后面换实现不换接口不容易连环返工。环节输入输出典型的坑车牌定位摄像头抓拍图BGR一个或多个车牌矩形坐标颜色阈值不覆盖阴影/反光漏检或误检校正裁剪车牌矩形区域规整的车牌图块抓拍倾斜时直接把斜图块送去切字符字符分割规整车牌图块7 张字符切片汉字内部笔画断开被切碎圆点被当成字符特征提取字符切片定长特征向量特征维度不一致SVM 训练直接报维度错误字符分类特征向量7 个字符标签O/0、1/I 相似字符分错需要规则后处理定位环节输出的是一个矩形框坐标不是抠出来的图。字符分割环节接收一个车牌图块输出若干小图。特征提取环节输出一维 float 向量比如 HOG 算出来的 324 维数组。SVM 训练和推理吃的就是这种向量。这五个环节想成五个函数函数签名提前定好后面单独换任何一环的实现都不影响其他环节。这是传统视觉管线相对端到端模型最大的优点中间过程全部可观测、可调试、可定位。我自己做的时候会在每个环节后强制存一版中间图到 debug_imgs 目录跑完一批测试图再统一翻看。不少下载的源码跑出来效果差不是算法不行是中间结果没有一个可观测的抓手改参数全靠猜越调越乱。你先保证“看得见每一环”再谈“调好每一环”。3. 用 OpenCV 实现车牌定位与字符分割可直接套用的 Python 流程3.1 车牌定位HSV 颜色阈值 形态学闭运算 轮廓筛选车牌定位的常规做法是结合两种信号颜色信号和边缘信号。蓝牌在 HSV 色彩空间里色相 H 集中在蓝到蓝紫区间饱和度 S 和明度 V 明显高于周围环境在梯度空间里车牌字符密集边缘响应强。常见做法是先用 HSV 阈值生成候选 mask再做形态学闭运算把字符之间的空隙填平让车牌连成一块最后用轮廓外接矩形的宽高比过滤噪点。import cv2 import numpy as np def locate_plate(frame): hsv cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) # 蓝色车牌在 HSV 下的经典阈值区间 lower_blue np.array([90, 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) # 国内车牌宽高比约 3:1留出倾斜容忍区间 if w 120 and h 30 and 4.0 w / h 2.0: candidates.append((x, y, w, h)) candidates.sort(keylambda r: r[2] * r[3], reverseTrue) return candidates这段代码里四个关键参数要说明白。lower_blue 和 upper_blue 决定哪些像素算“蓝”H 的 90~124 覆盖蓝到蓝紫区域S 和 V 的 80 下限过滤暗部和灰部白天阳光强时把 S 下限抬到 90能明显减少误检。kernel 尺寸 (17, 5) 不是随手写的车牌字符横向排列横向跨度远大于纵向长条形的核才能把字符间隙填平而不会把上下边缘粘在一起。宽高比 2.0~4.0 对应标准车牌的约 3:1 比例区间留宽是为了容忍相机俯仰角带来的透视形变。最后按面积从大到小排序因为画面里最大块的蓝色矩形大概率是车牌如果同一帧出现多辆车就把 candidates 逐个送去分割识别。这套定位对光照和角度特别敏感。正午强光下车牌的 S 值被压低下午阴影里 V 值掉得厉害单一阈值很难同时覆盖。把 S、V 下限调低会带来大量误检调高又容易漏检。工程上更稳妥的折中是用一组宽松阈值把候选区域全捞出来宁可多也不要漏再用后续字符分割和 SVM 识别结果做二次过滤。识别环节连续输出合理字符序列的才是真车牌。这个“粗定位 精确认”的思路比你把 HSV 阈值调到完美省力得多也更抗现场光照变化。3.2 字符分割垂直投影法与连通域分析的取舍拿到定位后的车牌图块先转灰度再做二值化然后切字符。主流做法有两种。垂直投影法统计每列前景像素个数列和从 0 跳到非 0 记为左边界从非 0 跳回 0 记为右边界连通域法直接找字符轮廓取外接矩形。我一般结合用先跑投影看整体分布再用连通域处理汉字这种内部结构复杂的区域。def segment_chars(plate_bgr): gray cv2.cvtColor(plate_bgr, cv2.COLOR_BGR2GRAY) _, binary cv2.threshold( gray, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU ) # 统计每一列的前景像素数前景为白值为 255 col_hist np.sum(binary // 255, axis0) char_bounds [] in_char False start 0 for x, val in enumerate(col_hist): if val 3 and not in_char: # 出现明显前景进入字符 in_char True start x elif val 3 and in_char: # 前景消失结束一个字符 in_char False width x - start if 20 width 120: # 过滤噪点碎块与粘连大块 char_bounds.append((start, x)) return char_bounds, binarythreshold 里用了 THRESH_BINARY_INV把字符统一变成白色前景、黑色背景避免不同车牌底色导致极性不一致。val 3 允许这一列存在少量噪点不会把三个像素宽的噪声当成字符边界。宽度 20~120 的下限过滤碎片上限过滤可能粘连整片的大块。这套参数只适合同一相机、差不多距离的输入换成远距离小图宽度阈值要按车牌图块的实际像素宽度缩放不要写死。这里有个经典问题第一位是汉字内部左右结构之间有明显空隙比如“川”字垂直投影在中间会掉到 0被切成两段反过来笔画密集的字母和数字之间间隔只有几个像素闭运算做多了还会黏成一整块。稳妥的方案是先用连通域分析把所有轮廓跑一遍筛出高度在车牌高度 40%~90%、宽高比在 0.2~1.5 之间的块按 x 坐标排序得到 8~10 个矩形含圆点再做后处理去掉面积特别小的圆点把汉字拆出来的碎片按 x 距离合并。字符分割是整个系统里最需要耐心调的部分。我调过一版江苏车牌“苏”字用连通域永远切成三个碎块最后用了一个简单规则第一位汉字区域宽度大约是第二位字母的 1.6~2.2 倍直接按比例从图块左侧切出汉字区再对剩余六位做垂直投影。这个规则来自车牌排版规范比纯图像算法更贴标准实际操作也最稳。你拿到源码后先跑一批真实车牌看分割切图再决定要不要加上“按宽度比例预切第一位”这种基于规则的补救。3.3 字符归一化为什么要统一到 32×32分割出来的字符图宽高比例不一汉字偏方字母数字偏长直接送进特征提取会得到长度不一致的向量SVM 根本没法训练。统一做法是把每张字符图等比缩放后居中贴到 32×32 画布。注意是等比缩放后居中不是直接把图拉伸成满幅方形拉伸会把字体比例扭曲0 和 O 这类靠比例区分的字符特征被抹掉。def normalize_char(char_img, size32): h, w char_img.shape scale min(size / h, size / w) new_w, new_h max(1, int(w * scale)), max(1, int(h * scale)) resized cv2.resize(char_img, (new_w, new_h), interpolationcv2.INTER_AREA) canvas np.zeros((size, size), dtypenp.uint8) y_off (size - new_h) // 2 x_off (size - new_w) // 2 canvas[y_off:y_off new_h, x_off:x_off new_w] resized return canvasscale 取 min 而不是 max是为了保证原始字符完整落进画布不裁掉笔画。INTER_AREA 在缩小图像时抗锯齿效果更好如果字符放大时边缘出现锯齿可换 INTER_CUBIC 试一下。居中偏移的整数除法可能让细笔画贴到一边训练样本增强时我会把偏移随机挪一两个像素给模型加一点平移扰动识别时保持严格居中这样模型对轻微偏移的鲁棒性会好不少。还有一个经常被忽略的细节二值化极性必须全局一致。有些源码在反色二值化和正色二值化之间摇摆同一批字符图随机出现白底黑字和黑底白字送到 SVM 特征差异极大准确率直接往下掉。我的习惯是全程用 THRESH_BINARY_INV前景字符统一为白色背景为黑色特征提取前再确认一遍所有切图极性一致。4. SVM 车牌字符识别模型训练数据组织、核函数参数与推理代码4.1 训练样本怎么准备目录即标签增强要克制字符识别模型的训练数据主要有三个来源一是公开字符数据集二是从真实车牌图手工切图三是用合成车牌图渲染生成。合成数据成本低也最受毕业设计欢迎用固定字体在蓝色底图上渲染字符加一点椒盐噪声、模糊、透视变换批量出图。但合成数据有个明显坑抓拍图里字符边缘有反光、污渍、磨损这些域差异是渲染路径覆盖不了的只在合成样本上训练的模型到真图上准确率往往掉 10 到 15 个点。我会采用“合成做兜底、真实切图为辅”的比例先跑一批真实抓拍图把分割结果全部存下来洗掉切坏的再并入训练集。真实样本占到 30% 以上时整体效果才稳得住。训练集的目录组织直接影响代码复杂度。推荐这种结构plate_dataset/ train/ 京/ # 文件夹名即为类别标签 0001.jpg 0002.jpg A/ 0001.jpg 0/ 0001.jpg 5/ 0001.jpg文件夹名直接映射到字符标签读取时遍历子目录把目录名转成整数标签避免手工维护 label.txt 对不齐。注意 Windows 下遍历含中文路径偶尔会编码报错训练机上尽量把目录名存成英文别名额外维护一个 char_map.json 做字符与索引的对应。4.2 提取 HOG 特征参数怎么设为什么这么设HOG方向梯度直方图是传统视觉里做字符识别的稳妥选择它对字符笔画的走向、粗细变化有一定容忍度比直接用像素灰度做特征稳定得多。OpenCV 自带 HOGDescriptor一行代码就能构建win_size (32, 32) # 检测窗口和归一化尺寸一致 block_size (16, 16) # 块大小包含 2x2 个 cell block_stride (8, 8) # 块滑动步长 cell_size (8, 8) # 细胞大小8x8 像素 nbins 9 # 每个细胞的方向直方图分箱数 hog cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, nbins) feature hog.compute(normalized_char_img).ravel().astype(np.float32)窗口大小必须和归一化尺寸一致都是 32×32。块 16×16块内 2×2 个细胞步长 8在 32×32 窗口内产生 3×3 共 9 个块位置。每个细胞出一个 9 维方向直方图因此总维度是 9 个块 × 4 个细胞 × 9 方向 324 维。这组参数是 32×32 字符图上的合理起点你可以把 nbins 改到 12 增加特征容量但要清楚维度变高对 SVM 不总是好事样本量不大时高维反而削弱泛化。计算完记得用 ravel() 展平成一维数组并转成 float32OpenCV 的 SVM 对数据类型相当敏感float64 有时会直接报类型错误。4.3 用 OpenCV 训练 SVMC、gamma 和终止条件OpenCV 的机器学习模块自带 SVM训练结果保存成 XML推理时再加载整套流程不依赖 sklearn。代码不长核心是理解调参svm cv2.ml.SVM_create() svm.setType(cv2.ml.SVM_C_SVC) # C-SVC支持多分类 svm.setKernel(cv2.ml.SVM_RBF) # 高斯径向基核 svm.setC(10.0) # 惩罚系数越大越不过拟合 svm.setGamma(0.01) # RBF 核宽度控制参数 svm.setTermCriteria( (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 1000, 1e-6) ) X np.array(features, dtypenp.float32) y np.array(labels, dtypenp.int32) svm.train(X, cv2.ml.ROW_SAMPLE, y) svm.save(plate_svm.xml)setKernel(cv2.ml.SVM_RBF) 把核函数设为高斯径向基核适合字符分类这种非线性边界问题。setC 控制惩罚项权重C 小则允许训练误差换泛化C 太大容易过拟合训练集针对 324 维 HOG 特征经验区间是 C 取 1 到 100。setGamma 控制 RBF 核的局部影响范围gamma 偏大决策边界更曲折偏小则边界平滑、不同字符更容易混在一起经验区间是 0.001 到 1。终止条件里 1000 是最大迭代轮次1e-6 是 epsilon训练收敛慢时优先调这两个而不是继续调 C。调参不能只看训练集准确率一定要留 30% 样本当验证集。我的做法是先粗粒度扫 C 和 gamma各取四五个候选值做网格搜索锁定准确率最高的组合后再围绕该组合细扫一轮。OpenCV 的 SVM 模型文件本身不大但要留意中文路径下 XML 写入偶尔会失败模型路径固定成英文最稳妥。4.4 推理环节加载模型、预测字符、规则后处理推理部分很短但有几个细节容易被新手踩到svm cv2.ml.SVM_load(plate_svm.xml) idx_to_char {v: k for k, v in char_to_idx.items()} def recognize_char(char_img): norm normalize_char(char_img) feat hog.compute(norm).ravel().reshape(1, -1).astype(np.float32) _, result svm.predict(feat) cls_idx int(result[0, 0]) return idx_to_char[cls_idx]predict 的返回值在 OpenCV 的 Python 绑定里是 (retval, result) 两个值result 是 1×N 的浮点数组取 result[0, 0] 再转 int才能拿去查标签表少取一个返回值或者取错下标是推理环节最常见的运行时报错。推理不要裸跑要接规则后处理第二位标签必须是字母第三到第七位可以是字母或数字后五位如果出现 O同时 0 的置信度接近优先输出 0第一位省份汉字如果两个候选分数接近可以用省份分布做纠正。规则后处理是传统管线相对神经网络的隐藏优势生产环境里的车牌识别从来不是孤零零跑一个模型而是“模型 规则”的复合系统。5. 车牌识别系统避坑环境、定位、分割、模型的五条排查记录5.1 现象import 时报 no module named opencv跑源码第一步就报错命令行里 import cv2解释器直接说找不到模块。原因很简单OpenCV 的 Python 包名不叫 opencv叫 opencv-python扩展模块是 opencv-contrib-python。不少人习惯性执行 pip install opencv装进来一个同名但无关的包import 自然失败。解决在虚拟环境里统一执行python -m pip install opencv-python opencv-contrib-python numpy装完立刻验证python -c import cv2; print(cv2.__version__)。关键是别在系统 Python 上装也别混用 conda 和系统环境。遇到包装哪边对不上的情况强制用 python -m pip 而不是 pip把包装到当前解释器所在环境。5.2 现象定位框满天飞蓝色车身和路牌都被框进来定位环节把天空、蓝色车身、指示牌全当成车牌候选矩形多到后排接不住。原因多半是 HSV 阈值太宽或者测试图里本来就有大片蓝色物体只靠颜色和宽高比过滤不掉。解决叠两条先验。第一是位置先验真实车牌出现在画面中部以下不会贴在上三分之一区域内。第二是边缘密度统计对候选块单独过一遍 Canny 或 Sobel车牌区域因为字符笔画密集边缘像素占比通常在 8% 以上而蓝色车身和天空的边缘密度明显低。两条规则叠加误检能压下去两个数量级。如果污染源是蓝色天空边缘密度过滤效果最好如果污染源是深色车身干脆放弃单一颜色定位改用 Sobel 垂直边缘搜索候选区域再用颜色验证收尾。5.3 现象第一个汉字被切成三块字符分割环节对“苏”“川”这些左右结构汉字切出来的轮廓零散不全训练样本里全是残缺字符SVM 识别自然错误。原因是连通域分割天然按连通块切汉字笔画之间本来就有空隙垂直投影也同理。解决不要试图让分割算法理解汉字结构改用“比例预切 投影”的混合方案。先根据车牌宽高比估算字符物理宽度按比例把第一位汉字区域单独切出来后六位再用投影或连通域切。标准蓝牌 440mm×140mm第一位汉字宽度约为后六位字符宽度的 1.7~2.2 倍。定位框高度准的话按宽度比切第一位是当前最稳的办法汉字不管内部结构多复杂都完整保住。我在实验里一直用这个方案因为它在代码里只有几行但对汉字切分错误率的下降非常明显。5.4 现象识别结果 O 和 0 分不清1 和 I 打架后五位字母数字区域模型经常把 O 和 0 混在一起1 和 I 互相错。原因有两条车牌字体里这几组字形高度相似特征向量里差异很小如果训练样本里两个类别的数量又不平衡模型会偏向样本多的一类。解决分三步处理。第一等价类合并后五位位置里 O 和 0 本来不需要区分把它们合并成同一标签识别后再根据位置规则后五位不会出现 I/O拆成标准字符。第二给相似类补齐训练样本专门收集一批清晰车牌图切出 0、O、1、I 单独加训。第三检查切图是否居中字形偏移会把区分 1 和 I 的小横线切掉模型再怎么调参都分不出来。很多“模型分不清”其实是“分割把区分特征切掉了”先查分割再动模型。5.5 现象答辩被问“为什么不用深度学习”答辩现场老师问你为什么不用 YOLO 和 CNN这是常规提问不是刁难但如果你只能回答“深度学习需要更多数据所以不用”等于没回答。解决准备两层话术。第一层从数据角度说课设场景能收集到的标注车牌图有限SVM 在几百维特征空间、有限样本下泛化可控而 CNN 的数据需求远大于现有条件。第二层从工程可解释性角度说车牌识别是流水线问题传统管线每个环节可见哪里错了能定位修复端到端模型出了问题只能整体重训在维护和排障上代价更高。说完两层补一句“我也跑过 CNN baseline准确率确实高一点但训练和标注成本涨了几倍课设范围内传统方案更合适”。这句话证明你比过、有判断而不是没学过。6. 把项目推进到答辩现场评测口径、调参顺序与演示习惯6.1 识别率不能自己瞎报字符级准确率和车牌级准确率是两个口径必须分开写。字符级看 7 个字符里判对几个车牌级看 7 个字符是否一字不差。整牌准确率 85% 而字符级准确率 96%这是正常水平如果你把字符级 96% 写成车牌识别准确率 96%答辩老师一眼就会拆穿。测试集要按场景切分白天、逆光、近距、远距各多少张列成表训练集和测试集不能包含同一段视频的连续帧否则数据泄漏会让指标虚高。评测口径立住了整篇论文的结论才站得住。6.2 调参按链路从前向后调别乱跳我踩过最多的坑是识别错了就往 SVM 参数上找原因查半天发现问题在分割环节字符切缺了一块特征自然错。沿着链路从前向后调先看定位框是否精准贴合车牌不贴合先修形态学核尺寸再看分割切片是不是一眼能看出字形最后才动 SVM 的 C 和 gamma。每改一步存一版中间图跑一条长视频看整体效果不要只盯一两张图调。定位和分割中间结果管住了后面所有环节的收益都会放大。6.3 演示 demo 的习惯决定答辩观感现场演示前把相机固定在稳定距离和角度预先录一段正常识别视频和一段失败视频。失败视频用来讲后续改进空间比全程零失败更有说服力。PPT 的流程页按第二章那张数据流表拍每页只讲一个环节的输入输出配调试时存的中间图比贴大段代码直观。做完这套项目我最大的习惯是每次改动都留一张中间过程截图论文插图直接用演示备用代码回溯也方便。整个系统最值得投入的不是 SVM 调参而是把定位和分割的中间结果管成可控的一环后面所有收益都从这里来。希望这套路数能帮你在拿到源码包之后少走几天弯路把精力花在把系统调稳、讲透上。本文还有配套的精品资源点击获取
返回列表