ARTICLE DETAIL

资讯详情

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

基于Python深度学习的车牌识别系统:从OpenCV预处理到Flask部署完整实现

基于Python深度学习的车牌识别系统:从OpenCV预处理到Flask部署完整实现 简介一套基于深度学习的车牌识别系统完整源码面向Python初学者、毕业设计及智能交通/停车场管理相关项目开发者。系统实现从车牌图像采集、预处理、CNN模型训练、字符分割到识别结果入库的完整流程并集成MySQL数据库与后端服务可用于快速搭建车牌识别应用并进行二次开发。包内共862个文件涵盖Python源码及编译文件py/pyc、前端页面html/css/js、图像素材png/gif/jpg、数据库脚本sql及说明文档md/docx等压缩包约27.81MB目录结构清晰便于查阅。已有142人学习下载。通过阅读源码可深入理解TensorFlow/PyTorch模型训练、CRNN字符识别、Flask/Django接口开发等关键技术同时获得从数据处理到部署优化的完整工程思路对完成课程设计或求职项目具有较高参考价值。1. 基于深度学习的车牌识别系统这套毕设源码解决了什么问题车牌识别这几年在智能交通和停车场管理里几乎是标配需求也是毕设选题里最常被点名的方向。这套基于 Python 深度学习的车牌识别系统源码覆盖了从车牌图像输入、车牌定位与字符分割、CNN 模型识别再到 Flask 后端把结果写入 MySQL 的完整链路。我拆完这套包之后的直接感受是它不像网上那些只丢给你一个训练脚本的半成品而是带 Bootstrap、LayUI 搭建的管理界面能上传图片、能展示识别结果、能查历史记录的完整应用。对正在做毕设或者想在公司内部快速搭一套车辆识别演示系统的工程师来说这是一个很好的参照对象照着跑一遍就能明白这类系统的真实工作量分配在哪里。2. 系统架构与技术选型前端、模型、后端数据库是怎么协同的2.1 从源码包目录结构反推系统分工拿到任何一套源码我习惯干的第一件事不是读代码而是先把目录结构过一遍。这套包里有一批非常典型的 Web 前端资源文件bootstrap.css、bootstrap.min.css、layui.css、_all-skins.css、admin.css、ueditor.css 等等。看到 _all-skins.css 和 admin.css 同时出现基本可以断定前端用的是 AdminLTE 或同类后台管理模板Bootstrap 负责栅格布局和基础组件LayUI 负责弹窗、表格和表单控件UEditor 则是富文本编辑器。这个组合在毕设项目里出镜率非常高因为它不需要专门的前端工程师就能在短时间内把图片上传、识别结果表格、历史记录查询这些页面完整搭出来。Python 代码层负责的核心是深度学习模型的加载和推理MySQL 负责落库。整个系统的调用链大致是这样前端页面上传一张车牌图片 → 后端接收图片 → 进行图像预处理 → 车牌定位 → 字符分割 → 字符识别 → 把结果写入 MySQL → 返回给前端展示。这里面每个环节都有独立的模块文件训练脚本和推理脚本是分开的这一点对于学生党来说很友好因为你可以只改其中一个环节而不影响整体调用。2.2 识别流程的四个关键节点实际处理车牌识别任务时我一般会把流程拆成四段车牌定位、字符分割、字符识别、结果清洗。这四个节点的顺序不能乱而且各自的选型逻辑完全不同。车牌定位有两种主流做法。第一种是用 YOLO 等目标检测模型直接框出车牌位置精度高但需要标注数据第二种是用 OpenCV 的颜色过滤加轮廓分析来找车牌利用车牌颜色蓝底白字、黄底黑字等和宽高比这两个强先验来锁定区域。毕设级别的项目绝大多数选第二种原因很简单不需要额外标注数据集几个阈值调好就能稳定框出车牌区域CPU 上跑得也快。这个包里的定位模块走的也是 OpenCV 这条路子通过 HSV 颜色空间找出蓝色区域再按宽高比过滤候选框。具体流程是转 HSV → 用 cv2.inRange 提取蓝色掩膜 → 形态学闭运算消除断裂 → findContours 找轮廓 → 按车牌宽高比和面积过滤。字符分割是整个系统里最考验参数调优的一步。车牌区域拿到之后要做的是把 7 个字符中国的蓝牌是 1 个汉字 6 个字母数字逐个切出来。这一步用连通组件分析就能实现核心是 cv2.findContours 加 boundingRect然后按 x 坐标排序。难点在于噪点处理车牌上可能有铆钉、污渍、边框残留这些都会形成额外的连通域需要设置宽高阈值把它们滤掉。阈值设得太松会把字符切碎设得太紧会把两个字符粘连在一起这里没有万能参数得根据你实际用的图片分辨率来调。字符识别用的是卷积神经网络。常见做法是把每个分割出来的字符缩放到固定尺寸比如 32×32送入 CNN 分类器输出 65 个类别31 个省份简称汉字 24 个字母 10 个数字其中 I 和 O 通常去掉。也有人用 CRNN 直接做序列识别但那是针对整张车牌一次性输出的方案对训练数据量和模型复杂度要求都更高毕设场景下用字符级 CNN 已经足够。2.3 为什么不推荐直接上端到端模型很多人会问现在目标检测和 OCR 都这么成熟了为什么不直接用 PaddleOCR 或者 YOLO 直接输出车牌号原因有三个。第一车牌字符的字体是定制的通用 OCR 模型在车牌字符上的识别率并不理想第二端到端方案需要海量带标注的车牌图片而字符级方案只需要每张字符图片的标签数据获取成本低一个量级第三车牌识别这个场景的字符集合是封闭的总共就 65 个类别用简单的 CNN 就能达到很高的准确率没必要引入大模型。所以这个源码包的方案选择是合理的轻量、可解释、数据需求量小适合教学和毕设场景。理解了这个选型逻辑你在改代码的时候就不会盲目把模型换掉而是知道精度瓶颈大概率出在字符分割而不是分类模型上。3. 预处理与字符分割把车牌图像变成干净的独立字符3.1 图像预处理四件套灰度化、均衡化、二值化、尺寸标准化拿到一块定位出来的车牌区域之后第一件事是预处理。这一步做得干不干净直接影响后续字符分割的成功率。我见过很多新手跳过预处理直接把原图送去识别结果就是字符边缘有大量噪声分割出来十个有九个是残的。车牌图像的预处理通常包含四个固定操作灰度化、直方图均衡化、自适应阈值二值化、尺寸标准化。import cv2 def preprocess_plate(plate_img): # 灰度化去掉颜色信息减少计算量 gray cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY) # 直方图均衡化增强对比度让字符和背景的差异更明显 equalized cv2.equalizeHist(gray) # 自适应阈值二值化光照不均时比全局阈值更稳 binary cv2.adaptiveThreshold( equalized, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 10 ) # 尺寸标准化固定到 440x140方便后续统一处理 resized cv2.resize(binary, (440, 140), interpolationcv2.INTER_CUBIC) return resized这段代码的逻辑很直接但每个参数背后都有讲究。灰度化不多说颜色信息在字符识别阶段用不上。直方图均衡化的作用是应对车牌在阴影下或强光下的对比度不足均衡化之后字符笔画和背景的灰度差会被拉开。自适应阈值二值化这里我用了 ADAPTIVE_THRESH_GAUSSIAN_C意思是用邻域内像素的高斯加权平均值作为阈值blockSize 取 21C 值取 10。blockSize 越大对局部光照变化的适应性越强但也更容易把小噪点一起保留下来C 是从计算出的阈值中减去的常量C 越大二值化结果中保留的像素越少能滤掉一部分浅色噪声但调太大会把字符笔画也切断。最后 resize 到 440×140这是国内车牌标准的宽高比换算字符宽度大约落在 45 像素左右这个尺寸对后续轮廓检测比较友好。3.2 基于连通组件的字符分割实现预处理完成之后二值图里车牌字符是一个个白色连通区域字符分割这一步要做的就是把每个连通区域找出来然后按从左到右的顺序排好。这里用 cv2.findContours 找轮廓再对每个轮廓求外接矩形。def segment_characters(binary_plate): # 查找所有连通域的外轮廓 contours, _ cv2.findContours( binary_plate, cv2.RETR_EXTERNAL, # 只取最外层轮廓忽略内部嵌套 cv2.CHAIN_APPROX_SIMPLE # 压缩轮廓点减少内存 ) boxes [] for cnt in contours: x, y, w, h cv2.boundingRect(cnt) # 过滤噪声太窄、太矮、过高的区域都不可能是字符 if w 15 or h 30 or h binary_plate.shape[0] * 0.9: continue # 过滤掉明显偏大的连通域可能是车牌边框残留 if w binary_plate.shape[1] * 0.3: continue boxes.append((x, y, w, h)) # 按 x 坐标从左到右排序保持字符顺序 boxes.sort(keylambda b: b[0]) return boxes这段代码里有三个关键过滤条件。第一个是宽高过滤w 15 会把细小的噪点滤掉h 30 会把车牌上下边缘的残留线条滤掉h 0.9 倍图像高度则说明这个区域很可能是横贯整张图的边框而不是字符。第二个是最大宽度限制单个字符的宽度一般不会超过整个车牌宽度的 30%超过这个比例的基本上是把两个相邻字符粘连成了一个区域或者是边框残留。第三个是排序这一步绝不能省因为 findContours 返回的轮廓顺序是不确定的不排序的话识别出来的字符顺序完全乱掉车牌号就拼不出来。分割完了之后你还需要对每个字符区域做一次尺寸归一化把所有字符图统一缩放到 32×32 或 48×48然后才能送入模型。这一步在训练和推理时都要做如果你训练时用 32×32推理时就必须用同样的尺寸。3.3 数据增强让模型不再怕模糊、倾斜和反光模型识别准确率的瓶颈往往不在模型结构而在训练数据的多样性。真实场景里车牌会出现各种情况晚上反光、雨天后尾灯映照、倾斜视角、运动模糊。要提升模型的泛化能力数据增强是性价比最高的一步。常见做法是用 TensorFlow 或 PyTorch 的图像变换工具在训练过程中动态地给每个字符图片做随机变换。from tensorflow.keras.preprocessing.image import ImageDataGenerator datagen ImageDataGenerator( rotation_range15, # 随机旋转正负 15 度 width_shift_range0.1, # 水平方向随机平移 10% height_shift_range0.1, # 垂直方向随机平移 10% zoom_range0.1, # 随机缩放 10% brightness_range[0.7, 1.3], # 亮度在 70% 到 130% 之间随机变化 fill_modenearest # 平移/旋转后边缘像素用最近邻填充 )数据增强的参数设置需要把握一个度。rotation_range 设 15 度已经足够因为车牌在真实场景中很少出现超过 15 度的俯仰或倾斜设太大会生成一些现实中不存在的极端样本反而干扰模型学习。brightness_range 是一个很值得调的参数因为夜间的车牌图片普遍偏暗而白天强光下又容易过曝这个范围设置的越宽模型对光照变化的鲁棒性越好。第三行到第六行之间清楚地看到裁剪和旋转的作用它们模拟了不同摄像头安装位置带来的视角差异让模型学会忽略字符在画面中的精确位置。如果你的训练集里已经有很多真实拍摄的图片增强参数可以适度收紧避免模型过度拟合增强后的伪样本。4. 模型训练与 Flask 部署从 CNN 定义到识别结果入库4.1 CNN 分类模型定义与训练参数字符分类模型是整个系统的核心但结构上并不复杂。我用的是一套简单的 CNN两个卷积池化层提取特征接一个全连接层做分类最后是 softmax 输出。用 TensorFlow 的 Sequential API 实现非常直观。import tensorflow as tf def build_cnn_model(num_classes65): model tf.keras.Sequential([ # 第一层卷积提取边缘、笔画等低级特征 tf.keras.layers.Conv2D(32, (3, 3), activationrelu, input_shape(32, 32, 1)), tf.keras.layers.MaxPooling2D((2, 2)), # 第二层卷积提取字符的局部结构特征 tf.keras.layers.Conv2D(64, (3, 3), activationrelu), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), # 全连接层把特征映射到类别空间 tf.keras.layers.Dense(128, activationrelu), tf.keras.layers.Dropout(0.5), # 防止过拟合 tf.keras.layers.Dense(num_classes, activationsoftmax) ]) model.compile( optimizeradam, losscategorical_crossentropy, metrics[accuracy] ) return model模型结构这里有两个容易踩的细节。Dropout 层放在全连接层之后而不是卷积层之后这是经验做法——卷积层的参数共享本身就有正则效果全连接层才是过拟合的重灾区。损失函数用 categorical_crossentropy意味着你的标签必须是 one-hot 编码格式如果标签是整数形式得先用 to_categorical 转换否则训练时 loss 会一路 NaN。优化器用 Adam 是默认选择learning rate 如果你的训练集只有几千张字符图初始学习率从 0.001 开始比较稳定不用调太快。训练环节的参数设置也很关键。batch size 建议在 32 到 128 之间太小的话梯度震荡厉害太大则容易收敛到平坦的局部最优。epoch 数我一般先设 30 个配合 EarlyStopping 回调监测验证集的 losspatience 设为 5——连续 5 个 epoch 验证集 loss 不下降就提前终止。4.2 Flask 后端把模型封装成可调用的识别 API模型训练完接下来的工作就是把它封装成 HTTP 接口让前端页面能调用。Flask 是最轻量的选择Django 对这个场景来说太重了。这个包的 API 设计大致是前端用 POST 请求把图片传给 /api/recognize 接口后端接收图片流转成 OpenCV 能处理的格式走一遍预处理 → 定位 → 分割 → 识别 → 返回结果。import base64 import numpy as np import cv2 import tensorflow as tf from flask import Flask, request, jsonify app Flask(__name__) model tf.keras.models.load_model(plate_char_cnn.h5) def run_recognition(image_bytes): # 把上传的图片字节流转成 OpenCV 图像 img_array np.frombuffer(image_bytes, dtypenp.uint8) img cv2.imdecode(img_array, cv2.IMREAD_COLOR) # 这里是完整识别流程定位、分割、逐字符预测 plate locate_plate(img) # 定位车牌区域 binary preprocess_plate(plate) # 预处理 chars segment_characters(binary) # 字符分割 result for (x, y, w, h) in chars: char_img binary[y:yh, x:xw] char_img cv2.resize(char_img, (32, 32)) char_img char_img.reshape(1, 32, 32, 1).astype(float32) / 255.0 pred model.predict(char_img, verbose0) char decode_label(np.argmax(pred)) result char return result app.route(/api/recognize, methods[POST]) def recognize(): file request.files.get(image) if file is None: return jsonify({code: 1, msg: no image uploaded}) image_bytes file.read() try: plate_number run_recognition(image_bytes) return jsonify({code: 0, plate: plate_number}) except Exception as e: return jsonify({code: 1, msg: str(e)}) if __name__ __main__: app.run(host0.0.0.0, port5000, threadedTrue)这是整条推理链路的骨架其中有几个细节值得注意。model.predict 之前必须把像素值归一化到 0-1 之间也就是除以 255.0如果训练时做过归一化而推理时忘了做模型输出的置信度会全面漂移。app.run 里我加了 threadedTrue这个参数在多用户同时上传图片时非常必要不加的话 Flask 默认单线程模式第二个请求会一直阻塞到第一个请求处理完我一个跑在 CPU 上的模型推理一次要 0.8 秒两个用户同时上传就会明显卡顿。4.3 MySQL 存储与查询识别到车牌号之后怎么落库识别出车牌号之后要把结果存进 MySQL。Python 生态里操作 MySQL 最常见的是 PyMySQL 或者 SQLAlchemy这套源码用的 PyMySQL 方案足够简洁。import pymysql def save_to_mysql(plate_number, image_name, confidence): conn pymysql.connect( hostlocalhost, # MySQL 服务地址 userroot, # 数据库用户 password123456, # 改成你自己的密码 databaseplate_system, charsetutf8mb4 # 必须用 utf8mb4否则中文省份简称会乱码 ) try: with conn.cursor() as cursor: sql (INSERT INTO recognition_records (plate_number, image_name, confidence, created_at) VALUES (%s, %s, %s, NOW())) cursor.execute(sql, (plate_number, image_name, confidence)) conn.commit() finally: conn.close()这段代码里最容易出问题的两点。第一是 charset 参数必须写 utf8mb4因为车牌号里的省份简称是中文京、沪、粤、苏这些如果用默认的 latin1 或 utf8写入数据库时会报错或者变成乱码。第二是这里的 INSERT 用了参数化查询 %s 占位直接拼接字符串是 SQL 注入的重灾区尤其在 Web 系统里这个习惯从一开始就要养成。另外一个细节是 created_at 字段直接用了数据库的 NOW() 函数省去了在 Python 端处理时间的麻烦。如果你要按时间查询识别记录前端只需要传一个日期范围给后端的查询接口SQL 用 WHERE created_at BETWEEN %s AND %s 就能搞定。毕设答辩时识别记录能按时间筛选这个功能非常加分因为很多评审老师都会问系统能不能做历史查询。5. 常见问题排查车牌识别系统中最容易翻车的五个点5.1 图片上传后识别的车牌号全是乱码字符现象前端上传一张很清晰的车牌照片返回的字符串包含大量不应该出现的字符比如车牌号变成「京A88888」正常应有7个字符被识别成七八个甚至更多字符。原因这个问题的根源几乎都在字符分割环节。程序把车牌边框残留、铆钉、污渍当成了字符区域。我在调试时遇到过最典型的情况是车牌左边第一个字符是省份简称「京」它的高度比其他字符略矮如果过滤阈值设置不当这部分区域会被切掉一块或者和边框残留粘连。解决回到 segment_characters 函数里排查两个参数。第一是 w 15 的最小宽度阈值如果你的车牌图片分辨率偏低字符宽度可能本身就不到 15 像素试着把这个值降到 8。第二是检查 h 0.9 倍图像高度的过滤条件这个条件的本意是滤除横向边框线但如果二值化时边框和字符的连通区域连在一起了这个过滤条件会失效。这时候需要用形态学开运算cv2.morphologyEx先腐蚀掉细长的边框残留再重新做连通组件分析。5.2 训练时 loss 降到很低但识别准确率一直上不去现象模型在训练集上的准确率超过 99%但拍一张真实场景的车牌识别结果经常错尤其是汉字识别错误率很高。原因训练集和测试集的分布不一致也就是所谓的“过拟合加域偏移”。如果训练数据里的汉字字符大多来自合成图片而测试时遇到的是真实拍摄的模糊车牌模型就会像是在做“开卷考试”一样只记住了训练集里的纹理而不是字符的本质特征。解决用数据增强把训练集往真实场景拉。我一般会在增强配置里把 brightness_range 设为 [0.6, 1.4]模拟不同光照时段rotation_range 加到 20 度模拟摄像头的不同视角。如果你有真实车牌照片哪怕只有几十张也把它们全部补充到训练集里。另外一个容易被忽视的点真实场景中字符边缘会有透视畸变可以在增强时加入一点随机透视变换用 OpenCV 的 warpPerspective 实现。5.3 MySQL 写入报错字段长度超出范围或中文乱码现象识别结果写入数据库时报错错误信息类似 Data too long for column plate_number或者写入成功后读出中文是问号。原因第一个报错的原因是车牌号的字段长度设置不足。中国车牌号是 7 个字符但如果你想存新能源车牌8 个字符就会超长char(7) 或 varchar(7) 都会翻车。第二个问题是字符集问题MySQL 表级别的默认字符集如果是 latin1连接时就算指定了 utf8mb4数据写入后依然会乱码。解决建表时直接把 plate_number 设为 VARCHAR(16)预留足够的空间。表级别和连接级别都统一用 utf8mb4。建表语句里加上 DEFAULT CHARSETutf8mb4连接字符串里 charsetutf8mb4两边一致才能保证中文正常。如果你用的是在线数据库管理工具比如 phpMyAdmin建表的时候也要注意把 collation 选成 utf8mb4_general_ci。5.4 Web 端上传图片后长时间无响应或超时现象点击识别按钮之后前端转圈转了半天然后提示请求超时但直接用 Python 脚本调用模型推理又很快。原因Flask 开发服务器默认单线程如果推理是在主线程里同步执行的第一个请求没处理完第二个请求就会排队。更隐蔽的是开发服务器app.run在 Windows 上默认的 reloader 会对每次代码改动都重启服务而模型加载又很慢你调试前端时频繁改代码每次重启都要重新加载模型给人“卡死了”的错觉。解决第一app.run() 加上 threadedTrue。第二模型加载放到模块级别执行不要在每次请求里都 load_model。第三如果并发压力较大直接用 gunicorn 作为生产级服务器用多个 worker 进程来承载并发请求。我在本地调试时还习惯把 reloader 关掉也就是 debugFalse确保服务不会因为代码保存而频繁重启。5.5 识别结果顺序错乱车牌号被拼成混乱的组合现象识别的字符都对但顺序完全不对比如「京A12345」被识别成「A京51234」。原因字符分割结果没有按 x 坐标排序或者分割后处理字符的循环打乱了顺序。sort(keylambda b: b[0]) 这一步在代码里写对了但如果你在后续流程中对 boxes 做了二次过滤或拼接不小心改变了顺序就会出这个问题。解决在分割函数返回前强制排序并把这个约定写清楚。此外多了一层保险识别完所有字符后用正则表达式校验结果格式。中国车牌的标准格式是「第一个汉字 一个大写字母 五个大写字母或数字」用正则^[京津冀沪…][A-Z][A-Z0-9]{5}$去校验不合格的识别结果直接标记为未知不写入数据库。这个校验规则相当于兜底即使模型在某些极端图片上出错也不会把错误结果污染到数据库里。6. 优化识别速度与验证准确率让系统达到真实可用水平6.1 用按位准确率替代整串准确率来评估模型很多同学验收模型时只看一个指标车牌整串识别准确率。这个指标不够精确因为某个字符错了会导致整串判错但你看不出来是哪个字符类型最容易出错。我更推荐用字符级准确率来细看模型能力统计每个字符的预测是否正确然后按汉字、字母、数字三个类别分别计算准确率。import numpy as np def evaluate_char_accuracy(y_true, y_pred, label_map): # y_true 和 y_pred 是多个测试样本的字符标签序列 detail {hanzi: [0, 0], letter: [0, 0], digit: [0, 0]} for true_label, pred_label in zip(y_true, y_pred): if true_label pred_label: category categorize_char(true_label) detail[category][0] 1 # 正确数 category categorize_char(true_label) detail[category][1] 1 # 总数 for cat, (correct, total) in detail.items(): acc correct / total if total 0 else 0 print(f{cat} 准确率: {acc * 100:.2f}% ({correct}/{total}))这个评估方法的妙处在于它能告诉你问题集中在哪。如果你的汉字准确率只有 80% 而数字准确率有 99%说明省份简称字符的样本量太少或者模糊样本太多你需要把精力放在汉字数据上而不是盲目调整整个模型结构。我见过不少项目组花了一个月调模型结构结果最后发现是汉字类别数据不足数据增强一补上准确率立刻上去了。按位评估的价值就在于不用等整串识别失败才知道问题在哪直接定位到具体字符类别的短板。6.2 GPU 加速与并发处理的边界把控如果模型推理速度太慢最基本的优化是换用 GPU。TensorFlow 在 GPU 上跑推理速度通常能提升 5 到 8 倍但这有一个大前提你要装好 CUDA 和 cuDNN。这里有个常见的错误是显卡太老装了新版 CUDA 不支持或者 TensorFlow 版本和 CUDA 版本不匹配。我的建议是先用 CPU 把整个流程跑通验证逻辑再考虑 GPU 加速因为推理速度很大程度上受限于图片预处理和字符分割这些 OpenCV 操作不管你用不用 GPU 都是跑在 CPU 上的。另外车牌识别系统的响应时间和吞吐量是需要分开考虑的。响应时间是指单张图片从上传到返回结果的时间吞吐量是系统每秒能处理多少请求。如果你做的是停车场出入口的实时识别场景首要优化目标是响应时间如果是批量处理历史照片归档吞吐量才是重点。针对前者可以缩短车牌定位环节的搜索范围先用小分辨率图片粗定位再去原始分辨率精裁车牌针对后者用多进程并行处理每个进程加载一个模型实例处理一批图片。以前我总觉得模型准确率达到 95% 就万事大吉实际落地几个月后发现真实场景里的边缘情况——夜间逆光、车牌倾斜、泥浆遮挡——远比训练集里的测试图片凶残。从那以后我每次交付车牌识别相关的项目都会强制走一遍异常角度测试、夜间图片实测和并发请求压测这三步准确率指标再好看这三关过不了都不敢说系统能稳定跑起来。不要等到答辩现场或者客户演示的时候才让真实场景打脸提前用真实数据跑一遍能省掉后面无数的解释成本。希望帮到你。本文还有配套的精品资源点击获取
返回列表