尧图精选

基于深度学习的人脸识别考勤系统:从人脸检测到打卡落库

🕒 发布时间:2026/9/28 5:01:28 📁 来源:尧图网络
简介面向计算机专业毕业设计与人脸识别应用实战的Python源码项目基于深度学习完成考勤场景下的面部检测、特征提取与比对识别适合正在筹备毕业设计、课程设计或期末大作业的高校学生也适合希望掌握人脸识别系统完整开发流程的学习者。压缩包共25个文件大小为239.41MB主要包含Python脚本、UI界面图片素材、字体与图标文件、配置文件及使用手册docx其中3个py文件为核心算法与界面逻辑15个png覆盖登录、用户管理、签到状态等界面元素另附独立的人脸识别软件zip便于直接运行体验。已有166人学习下载。项目经导师指导评审98分提供完整可运行源码、人脸识别工具包、使用手册与界面资源目录涵盖人脸识别核心模块、静态资源、配置与工具可帮助读者快速复现系统、理解考勤流程并在此基础上进行功能扩展。1. 人脸识别考勤系统到底在做什么先看清这个毕设项目的技术构成python的本科毕业设计-基于深度学习的人脸识别考勤系统源代码这类项目每年在论文库和源码分享里都是热门。它解决的问题很直接教室或实验室门口放一个摄像头人走过去系统识别出你是谁、几点几分到的写进考勤表省去点名和打卡机。听上去和小区门口的人脸识别门禁机是同一套逻辑但自己做一遍会发现难点不在认人而在怎么把一帧普通画面变成一条可靠、不重复、能导出的考勤记录。适合打算拿这个方向做毕设、或者在公司里搭一个轻量考勤原型的同学。2. 从摄像头到考勤记录系统工作原理与模型选型2.1 一段流水线人脸检测、特征提取、比对与考勤落库一个人走进摄像头视野到你看到数据库里多出一条打卡记录中间要过四道工序。第一道是画面采集。USB 摄像头默认输出 25 到 30 帧每秒但考勤场景不需要每帧都做识别否则 CPU 会被拖垮。我一般做 3 帧取 1 帧也就是每秒跑 8 到 10 次识别对一个人走过去这件事已经非常充裕。第二道是人脸检测。这一步只回答画面里有没有人脸在哪。输出是一个矩形框就是你在演示界面上看到的绿框。检测模型常见的是 OpenCV 自带的 Haar 级联、dlib 的 HOG 或 CNN 检测器、MTCNN。Haar 是传统方法侧脸和暗光下漏检率高MTCNN 和 CNN 检测器属于深度学习方法稳定性好很多代价是计算量大一些。第三道是特征提取。检测到人脸框之后把这块区域归一化成固定尺寸送进一个深度卷积神经网络输出一串数字向量。dlib 的模型输出 128 维ArcFace 输出 512 维。这串数字就是这张脸的身份证同一个人的不同照片向量之间的距离很近不同人距离远。第四道才是考勤动作。把当前人脸向量和注册库里每个人的向量算一遍距离找到最近的如果距离小于阈值就判定为命中然后写库。这里有个新手最容易犯的错检测到就写库结果一个人站在门口五秒库里多出几十条记录。考勤写入必须带排重逻辑后面第 4 章细说。2.2 为什么选深度模型而不选 Haar 或 LBPH很多毕设源码里写的是 OpenCV 的 Haar 检测加 LBPH 特征代码短运行快但标题带深度学习的项目还这么做答辩时会有个尴尬场面导师问你的深度神经网络在哪你指着 LBPH 说这就是。LBPH 是手工设计的局部二值纹理直方图不是端到端学习出来的特征光照一变就翻车。我的选型建议是检测用 MTCNN 或 dlib 的 CNN 检测器特征用深度 CNN 生成的 embedding。MTCNN 是三级联结构P-Net 先在全图快速出候选框R-Net 精筛O-Net 输出最终框和眼睛、鼻子、嘴角五个关键点关键点还能用来做人脸对齐把歪着的脸转正识别率能明显提升。这套东西在 CPU 上跑一张 640x480 的画面大约几十到一百毫秒考勤场景下抽帧运行完全够用。特征层面dlib 的 128 维 embedding 和 FaceNet 的思路一致用三元组损失拉开类间距离。FaceNet 训练时让同一个人的向量尽可能聚拢不同人的向量尽可能分开最后所有脸都映射到同一个向量空间里比对就是算距离简单直接。表格总结一下三个方案的差别环节传统做法深度学习做法结论人脸检测Haar 级联滑动窗口加 AdaboostMTCNN 三级联 / dlib CNN 检测器Haar 侧脸、遮挡、暗光漏检严重特征提取LBPH 直方图统计纹理FaceNet / dlib / ArcFace 输出向量LBPH 光照敏感深度特征更稳开发成本低但论文没亮点用现成预训练模型工作量不大深度方案性价比更高2.3 距离阈值考勤判断真正的黑匣子特征向量算出来后判断是不是这个人靠的不是某个模型而是一个简单的数字比较。dlib 的向量在欧氏距离下小于 0.6 通常算同一个人这是 face_recognition 库的默认值。但通常两个字很要命。0.6 这个值是在通用人脸数据集上调出来的你的教室灯光、摄像头位置、注册照片质量都会改变距离分布。我见过白天好好的系统傍晚开了日光灯同一批人距离集体从 0.4 涨到 0.65结果全被拒之门外。实操经验是考勤系统别追低误识优先别漏。阈值设 0.5 到 0.55 会比较严格代价是光线差的时候真人进不来设 0.6 到 0.65 更宽松但容易把邻座同学识别成你。我一般先按 0.6 跑一天把每次比对的距离都写进日志第二天看分布再调。阈值调多少有点玄学但只要有日志玄学就能变成科学。3. 用 Python 跑通最小考勤系统环境安装、人脸注册与识别打卡3.1 Python 环境与依赖安装、虚拟环境与 vscode 配置先把环境装利索。Python 版本别追新3.9 或 3.10 最稳很多深度学习相关 wheel 还没跟上 3.12。用 conda 建独立环境省得把系统 Python 搞乱。conda create -n face-att python3.9 conda activate face-att conda install -c conda-forge dlib pip install opencv-python face_recognition numpydlib 是 face_recognition 的底层依赖在 Windows 上直接 pip install dlib 经常要现场编译报错信息里全是 CMake 和 Visual Studio Build Tools。用 conda-forge 装是二进制包省掉编译这一步。装完在 vscode 里 CtrlShiftP 选解释器指向 face-att 环境的 python.exe环境配置就结束了。第一次 import face_recognition 会慢几秒它在加载检测和特征两个模型文件。如果你的电脑连不上外网模型下载会卡住解决方法是找一台能上网的机器跑一次让模型缓存到用户目录的 .face_recognition 文件夹把整个文件夹拷过来放到同样位置。3.2 人脸注册端摄像头采集合格人脸并提取特征入库注册阶段做两件事从摄像头里抓到一张合格的人脸照片然后提取特征向量存进 JSON 文件。我这里按一人一张基准照来做生产环境可以每人存 5 到 8 张覆盖角度和光线。import face_recognition import cv2 import os def register(name, save_dirfaces): cap cv2.VideoCapture(0, cv2.CAP_DSHOW) # Windows 用 DSHOW 后端更稳定 if not cap.isOpened(): raise RuntimeError(摄像头打不开检查占用或换个设备索引) face_img None while face_img is None: ret, frame cap.read() if not ret: continue # face_recognition 内部按 RGB 处理cv2 读出来是 BGR先转换 rgb frame[:, :, ::-1] boxes face_recognition.face_locations(rgb) if boxes: # 取面积最大的人脸避免注册进合照里的路人 top, right, bottom, left sorted( boxes, keylambda b: (b[2]-b[0]) * (b[1]-b[3]), reverseTrue)[0] face_img frame[top:bottom, left:right] face_img cv2.resize(face_img, (150, 150)) # 按 q 跳过当前帧方便调整角度 cv2.imshow(register, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() os.makedirs(save_dir, exist_okTrue) cv2.imwrite(os.path.join(save_dir, f{name}.jpg), face_img)这段代码的逻辑是持续读摄像头直到某一帧检测到人脸裁剪出最大的人脸框缩放成 150x150 存成 JPG。裁剪这一步很重要直接把摄像头原始画面整体存入特征提取时会带上大量背景噪声识别率会掉。cv2.CAP_DSHOW 是 Windows 下解决摄像头打不开的常见手段后面避坑章节还会展开。存完照片后提取特征写入一个全局的 JSON 文件后续识别端启动时一次性加载。import face_recognition import json import os def build_embedding(name, save_dirfaces, db_pathembeddings.json): img_path os.path.join(save_dir, f{name}.jpg) img face_recognition.load_image_file(img_path) # 会自动转 RGB loc face_recognition.face_locations(img) if len(loc) ! 1: raise ValueError(f{name}.jpg 里应该恰好有一个人脸当前检测到 {len(loc)} 个) emb face_recognition.face_encodings(img, known_face_locationsloc)[0] db {} if os.path.exists(db_path): db json.load(open(db_path, r, encodingutf-8)) db[name] emb.tolist() with open(db_path, w, encodingutf-8) as f: json.dump(db, f, ensure_asciiFalse, indent2) print(f{name} 已注册特征维度 {len(emb)})这里强制要求照片里只有一张脸。如果注册照片是合照特征向量包含多张脸的信息比对的数值会反复横跳这是识别率崩掉的常见原因。ensure_asciiFalse 保证中文名字正常写入 JSON这一行经常会漏漏了之后 JSON 里全是 \uXXXX 转义数据库里看着没问题日志和报表里全是乱码。3.3 识别端实时检测、比对与考勤写入识别循环是系统的核心逻辑不复杂读帧、检测人脸、提取特征、算距离、判断、写库。import face_recognition import cv2 import json import numpy as np import datetime import sqlite3 DB_PATH attendance.db EMB_PATH embeddings.json THRESHOLD 0.55 # 欧氏距离阈值先按 0.55 跑看日志再调 def load_known(): raw json.loads(open(EMB_PATH, r, encodingutf-8).read()) names list(raw.keys()) known [np.array(raw[n]) for n in names] return names, known def mark_attendance(name, db_pathDB_PATH): conn sqlite3.connect(db_path) today datetime.date.today().isoformat() now datetime.datetime.now().strftime(%H:%M:%S) cur conn.execute( SELECT count(*) FROM attendance WHERE name? AND date?, (name, today)) if cur.fetchone()[0] 0: conn.execute( INSERT INTO attendance(name, date, time) VALUES(?,?,?), (name, today, now)) conn.commit() print(f{name} 打卡成功 {now}) else: print(f{name} 今天已打过卡 {now}) conn.close() def run(): names, known load_known() cap cv2.VideoCapture(0, cv2.CAP_DSHOW) frame_skip 0 while True: ret, frame cap.read() if not ret: continue frame_skip (frame_skip 1) % 3 if frame_skip ! 0: continue rgb frame[:, :, ::-1] # 检测模型用 hog 在 CPU 上速度快论文里写 MTCNN 也说得通 locs face_recognition.face_locations(rgb, modelhog) if locs: embs face_recognition.face_encodings(rgb, known_face_locationslocs) for emb, (top, right, bottom, left) in zip(embs, locs): dists [np.linalg.norm(emb - k) for k in known] best_idx int(np.argmin(dists)) if dists[best_idx] THRESHOLD: cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2) label f{names[best_idx]} {dists[best_idx]:.2f} cv2.putText(frame, label, (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) mark_attendance(names[best_idx]) cv2.imshow(attendance, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码有两点值得说明。第一每一帧都做检测太费 CPUframe_skip 控制每 3 帧跑一次肉眼完全感知不到延迟CPU 占用能降一半以上。第二就算匹配成功也只有当天第一次才写库后面的命中只更新界面不产生重复记录。这里埋了个伏笔如果两个人长得像或者光线差导致距离波动还是可能出现反复横跳第 5 章的避坑清单会给出更完整的解法。3.4 三个必调参数distance_threshold、抽帧间隔、连续确认帧数跑通第一版后调参是收益最大的工作。我把这三个参数单独拿出来因为它们直接决定演示效果是灵还是卡。参数默认值我的建议影响THRESHOLD0.60.50 ~ 0.60阈值越小越严格误识少但漏检多frame_skip0每帧2每 3 帧一次数值越大 CPU 占用越低响应越迟钝连续确认帧数13连续 N 次命中同一人再写库防误识连续确认帧数这个参数我没写进上面的代码因为要加一个简单的投票字典放在第 5 章避坑清单里展示更合适。调参顺序也有讲究先固定阈值 0.6 跑一天收集距离日志再根据误识和漏检的比例决定收紧还是放宽。不要一上来就追求零误识考勤系统的实际痛点是漏检——人到了却没记录比偶尔张冠李戴更麻烦。4. 把考勤逻辑做厚数据库设计、迟到早退与报表导出4.1 SQLite 三张核心表学生、人脸特征、考勤记录很多毕设源码在考勤部分只有一张表字段是 id、name、time删掉重建都无所谓。但要做到高分数据库设计必须能回答三个问题这个人和学号怎么关联特征向量存在哪同一天多次识别到怎么处理我习惯拆成三张表。CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT NOT NULL UNIQUE, name TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS face_embeddings ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT NOT NULL UNIQUE, emb_json TEXT NOT NULL, updated_at TEXT DEFAULT (datetime(now, localtime)) ); CREATE TABLE IF NOT EXISTS attendance ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_no TEXT NOT NULL, date TEXT NOT NULL, first_time TEXT NOT NULL, last_time TEXT, UNIQUE(student_no, date) );students 表存学号和姓名面向教务导出。face_embeddings 表存特征向量用 JSON 字符串保存而不是拆成 128 个浮点列因为特征维度取决于你换的模型dlib 是 128 维ArcFace 是 512 维用列存模型一换就要改表结构。考勤人数几十人的场景下全量向量加载进内存做暴力比对就够不需要上向量数据库。attendance 表的 UNIQUE(student_no, date) 是防重复打卡的数据库层保证后面会反复用到。4.2 写一条不漏不重的考勤记录同一天只记一次的排重逻辑识别端代码里先查后插的思路在单机场景够用但更好的做法是利用 SQLite 的 UPSERT 语法把查重和写入合并成一个原子操作。import sqlite3 import datetime DB_PATH attendance.db def punch(student_no): now datetime.datetime.now() today now.date().isoformat() time_str now.strftime(%H:%M:%S) with sqlite3.connect(DB_PATH) as conn: conn.execute( INSERT INTO attendance(student_no, date, first_time, last_time) VALUES(?, ?, ?, ?) ON CONFLICT(student_no, date) DO UPDATE SET last_time excluded.last_time , (student_no, today, time_str, time_str) ) conn.commit()注意 ON CONFLICT 分支里只更新 last_time不动 first_time。这样同一个同学早上 8:00 刷脸进来first_time 记录 08:00:00下午 17:50 离开再刷一次last_time 更新成 17:50:00。一条记录既回答了几点到也回答了几点走同时天然去重。这个操作在 SQLite 3.24 以上版本可用Python 3.9 自带的 SQLite 版本足够。不需要先 SELECT 再 INSERT避免并发情况下两个人同时打卡产生竞态条件。识别端代码里那个先查后写的版本建议统一替换成这个函数。4.3 出勤状态怎么算迟到、早退、缺勤与导出报表有了一张表里的 first_time 和 last_time出勤状态就是一条 SQL 的事。假设上课时间是 08:30下课时间是 18:00。SELECT s.student_no, s.name, a.date, CASE WHEN time(a.first_time) time(08:30:00) THEN late WHEN time(a.last_time) time(18:00:00) THEN early_leave ELSE normal END AS status FROM attendance a LEFT JOIN students s ON a.student_no s.student_no WHERE a.date ?;time() 函数把 TEXT 类型的 08:00:00 转成可比较的时间值和 08:30:00 直接比较。缺勤的同学不会出现在 attendance 表里所以报表需要从 students 表出发做左连接把没有打卡记录的人标成 absent。导出 CSV 也有讲究直接用 open 写 UTF-8 编码的文件Excel 打开会乱码因为 Excel 默认按 ANSI 解析。正确的写法是加 utf-8-sig。import csv def export_day(date_str, conn): rows conn.execute( SELECT s.student_no, s.name, a.first_time, a.last_time FROM attendance a LEFT JOIN students s ON a.student_no s.student_no WHERE a.date ? , (date_str,)).fetchall() with open(fattendance_{date_str}.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([学号, 姓名, 首次签到, 最后在场]) writer.writerows(rows)4.4 数据集表达标自己采集 LFW 兜底 标注规范毕设答辩必问的一句是你的训练集从哪来人脸数据是怎么标注的这个问题回答得漂亮比多跑通一个功能加分更多。考勤系统的注册库必须自己采集。方法是用手机或摄像头给每个学生拍 5 到 8 张照片覆盖正面、左右侧脸 15 度左右、戴不戴眼镜、开灯和关灯两种光线。照片里只允许出现一个人拍照时背景干净。每人存一个文件夹文件名规范是学号_姓名_场景编号.jpg比如 20210001_张三_01.jpg。这个命名习惯在答辩时说出去老师会觉得你的数据管理是工程级而不是玩具级。公开数据集兜底指的是 LFW 这类经典人脸数据集。它可以用在预训练模型的验证环节说明你对模型在人脸公开数据集上的表现做过调研但不建议拿 LFW 里的名人照片当考勤学生注册进系统。一来名人和你项目背景不符答辩时容易被追问二来 LFW 照片多为新闻图角度和光线和你现场的摄像头差异极大识别率反而会很难看。还有一个答辩要点你的模型参数是拿大规模人脸数据集预训练好的你做的不是从零训练而是用预训练模型做特征提取用自采数据做验证和阈值标定。这个定位既诚实又符合本科毕设的工作量预期。5. 人脸识别考勤的避坑清单现象、原因与解决5.1 摄像头打不开cap.isOpened() 为 True 但 read 一直返回 False现象cv2.VideoCapture(0) 返回了对象isOpened() 也是 True但循环里 ret 一直为 False画面黑屏。或者笔记本摄像头一调用就弹摄像头被占用。原因最常见的是摄像头被微信、钉钉、腾讯会议这类软件占用了Windows 下摄像头设备默认只允许一个进程打开。另一个坑是 OpenCV 默认用 MSMF 后端部分老摄像头驱动兼容性差。还有人笔记本摄像头设备索引不是 0是 1 或 2。解决先关掉所有占用摄像头的程序。代码里指定后端cap cv2.VideoCapture(0, cv2.CAP_DSHOW)。如果还是黑屏写一个 for 循环把 0 到 3 的设备索引全试一遍。更稳妥的调试策略是先用一张本地图片把识别流程整个跑通再去接摄像头两件事分开调试不会互相干扰。5.2 反复打卡一个人站在门口五秒库里多了几十条记录现象同一个人脸出现在画面里的每一帧都写入库数据库里同一人同一天几十条记录或者晚上灯光变暗张三被识别成李四两人交替命中。原因识别循环里匹配成功就写库没有去重。光照变化时同一个人的特征向量会产生抖动距离在阈值边缘反复横跳前一帧命中张三后一帧命中李四。解决数据库层加 UNIQUE(student_no, date) 约束这一条只能兜底同人同日一条治不了张冠李戴。业务层加连续确认机制同一个名字必须连续命中 N 帧才写库中途换人就清零。from collections import defaultdict vote defaultdict(int) def confirm(name, min_hits3): for k in list(vote.keys()): if k ! name: del vote[k] # 换人了之前的计数作废 vote[name] 1 return vote[name] min_hits调用时把识别端的 mark_attendance(names[best_idx]) 改成if dists[best_idx] THRESHOLD and confirm(names[best_idx]): punch_by_name(names[best_idx])识别频率是 3 帧一次连续 3 帧确认大约对应 0.5 到 1 秒人正常走过门口不会觉得卡。关键点是换人清零这一行没有它两个人交替出现时各自的计数都在累积照样误判。5.3 dlib 装不上编译报错、import 太慢、模型加载失败现象Windows 上 pip install dlib 报 CMake 错误、提示需要 Visual Studio Build Tools装好之后第一次 import 卡好几秒换了一台内网机器报错说模型文件下载失败。原因dlib 是 C 写的pip 在 Windows 上没有预编译 wheel 时会走源码编译本机缺编译工具链就翻车。face_recognition 启动时要加载检测和特征两个模型文件文件在用户目录的 .face_recognition 文件夹下首次运行自动从网上下载内网环境下载就卡死。解决优先 conda install -c conda-forge dlib这是二进制包不需要编译器。Python 版本不要用 3.12用 3.9 或 3.10。内网机器先在外网跑一次把用户目录下的 .face_recognition 整个文件夹复制到内网机器相同位置。这个坑几乎每个用 face_recognition 的毕设都会踩一遍提前准备能省一晚上。5.4 识别率忽高忽低上午 98%下午开了日光灯变成 70%现象同一个教室上午顺光的时候识别率很高下午开日光灯后大量漏检或者有人戴了口罩系统彻底认不出来。原因光照变化直接改变人脸图像的像素分布深度特征对极端光照仍然敏感。口罩遮挡下半脸检测模型给出的关键点质量下降特征向量偏移明显。解决注册阶段就让照片覆盖多种光照条件每人至少一张开灯、一张关灯的照片。考勤机摆放位置避开正对窗户侧光比顶光更友好。口罩问题不要硬刚演示时直接说明系统默认场景是不戴口罩识别。导师如果追问给两个方向一是利用关键点检测嘴部遮挡提示请摘下口罩二是更换红外摄像头门禁机上普遍用红外人脸方案不受可见光影响。5.5 导出的 CSV 在 Excel 里打开全是乱码现象用 Python 写出的考勤 CSV用记事本打开正常拖进 Excel 中文全部乱掉。原因Excel 默认按 ANSI 编码读取 CSVPython 默认写的是 UTF-8没有 BOM 头时 Excel 不做编码探测。解决写文件时指定 encodingutf-8-sig。这看起来是个小问题但在答辩演示时当着老师的面打开乱码报表观感非常差。还有同学用 pandas 的 to_csv记得同时指定 encodingutf-8-sig。6. 把演示做成高分置信度可视化、日志审计与验收验证答辩演示只有几分钟你要让评委在一屏之内看懂三个东西画面里有人脸框、框上有名字和距离值、旁边能看到今天的考勤记录在实时增加。我习惯在 OpenCV 窗口右侧叠加一个签到列表每成功打卡一人就从数据库里查出今天的已签到名单画在画面上。在画面左上角绘制签到名单逻辑是启动时把今天的签到记录查出来每成功 punch 一次就刷新def overlay_signin(frame, names_today): y 20 for name in names_today[-15:]: # 最多显示最近 15 人 cv2.putText(frame, name, (10, y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) y 30 return frame除了画面日志是答辩时的第二个大杀器。每次比对都把时间、姓名、距离、是否通过写进 recognition.log老师问你这系统可靠吗你不用空口证明直接打开日志按距离排序指出误判样本集中在哪个时段、什么光线条件这比任何口头解释都有说服力。验收验证建议按下面这个表做记录截图和日志都保存到项目目录里测试场景预期结果记录方式正脸顺光正确签到距离 0.5截图侧脸 30 度正确识别或拒绝但不误识他人日志昏暗灯光允许漏检不允许误识日志戴眼镜 / 换发型稳定识别截图两人同时入镜两人分别被识别并打卡日志进阶方向上把 dlib 换成 ArcFace 是性价比最高的一步。face_recognition 的识别主流程不用大改换掉 load_known 里加载特征的方式把向量维度从 128 改成 512阈值重新标定一次就行。论文里可以写采用余弦相似度 角度间隔损失训练的 ArcFace 模型深度学习的含量和答辩档次直接上一个台阶。我自己当年做毕设时最大的教训是把所有时间都砸在调模型上最后留给验收和演示的时间只剩两天搞得很被动。如果你现在刚开始建议反着来先按第 3 章的代码跑通最小闭环再补考勤逻辑和避坑最后用一周时间做日志审计和可视化。这套顺序下来你手上有一个能现场演示、经得起追问、逻辑完整的系统这才是高分项目真正的底气所在。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →