
简介一套面向计算机视觉课程设计与毕业设计的实战代码基于OpenCV人脸检测与TensorFlow 2.0深度学习框架构建CNN人脸识别模型用于锁定与解锁Windows 10屏幕。资源共7个文件压缩包仅13KB包含4个Python脚本、1个VBS脚本、1个BAT批处理文件及1个Markdown说明文档。Python脚本承担人脸采集、训练与识别任务其中CNN模型利用TensorFlow 2.0构建OpenCV负责图像预处理和实时检测VBS与BAT脚本配合Windows任务计划完成屏幕锁定/解锁的系统级联动Markdown文档清晰说明环境搭建与运行步骤。目前已有302人学习下载。通过这套代码可深入理解卷积神经网络在人脸识别中的完整工作流程掌握从模型训练到推理、再到系统脚本集成调用的落地方法为毕业设计、课程设计提供可直接修改和二次开发的参考工程。1. 人脸识别锁屏实战OpenCV TensorFlow 2.0 这条链路值不值得走工位离开忘了锁屏、课程设计要交人脸识别作业、想搞懂 CNN 从采集到部署的完整流程——这三个场景撞到一起时你需要的不是又一个只讲原理的教程而是一套能跑起来的东西。这个基于 OpenCV 与 TensorFlow 2.0 的实战项目把「摄像头采集人脸 → 训练 CNN 模型 → 实时识别本人 → 锁定/解锁 Windows 10 屏幕」整条链路串通了。它不是企业级 ArcFace 那种商用方案而是让初学者用最少的代码看清人脸识别系统每个环节怎么工作的完整标本。适合计算机视觉课程设计、毕业设计预研以及想在 Win10 上做点自动化小工具的开发者。下面我按文件实际拆解它的实现路径和坑。2. 运行环境与数据采集先过配置关再谈模型2.1 版本选型Python 3.7 OpenCV 4.x TensorFlow 2.0 为什么这么搭拆这个项目之前先明确一个观念人脸识别系统不是只有模型前面还有摄像头读取、人脸检测、数据集采集一整套链路。OpenCV 在这里承担的是图像采集和人脸定位TensorFlow 2.0 负责训练 CNN 分类器两者分工明确。OpenCV 选 4.x 的原因很简单——cv2.data.haarcascades这个路径常量在 4.x 里才稳定可用旧版 2.4/3.x 经常要手动拼接 XML 路径动不动就报cv2.error。这个报错在热搜里反复出现后面避坑章节会详细说。TensorFlow 选 2.0 而不是 1.x核心差异是 Keras 被整合进来了。写 CNN 不用再跟tf.Session()、placeholder这些东西纠缠tf.keras.Sequential几行就能堆出一个卷积网络。这对课程设计来说效率高得多调试成本也低。Python 版本建议 3.7 或 3.8。TensorFlow 2.0 在 3.9 上有过一段兼容性尴尬期而 OpenCV 的预编译 wheel 对 3.7/3.8 支持最稳。如果你用的是 Halcon 做工业视觉那套东西偏重测量和标定跟 OpenCV 定位不同做教学项目和人脸识别原型OpenCV 的社区资料量和调试便利性优势明显这也是多数课程设计选它的原因。2.2 人脸检测face_1.py 里的 Haar Cascade 与 detectMultiScale 参数项目里face_1.py是最基础的一版完成「打开摄像头 检测人脸 画框显示」。它用的不是深度模型而是 OpenCV 自带的 Haar Cascade 级联分类器。原理是滑动窗口 积分图 AdaBoost 级联判断训练好的 XML 文件里存了一堆弱分类器。import cv2 face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头打开失败请检查设备) exit() while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(80, 80) ) for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.imshow(face detect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码逻辑很简单循环读帧 → 转灰度 → 检测人脸 → 画矩形。注意detectMultiScale三个参数是调优重点。scaleFactor1.1表示每次缩放窗口的步长值越小检测越慢但越准实场景下 1.1 是精度和速度的平衡点小于 1.05 会有明显卡顿。minNeighbors5控制候选矩形合并阈值越大越严格误检少但可能漏检。minSize(80, 80)过滤太小的窗口我一般会根据摄像头距离调坐姿离屏幕半米左右80 像素起步能避免把远处路人框进来。新手经常在这里翻车摄像头离得太远人脸区域在画面里只有几十像素被minSize过滤掉结果框不出来。血泪经验是先用默认参数跑通再根据你的工位距离微调。2.3 数据采集face_2.py 怎么构建自己的数据集检测能用了下一步是采集训练数据。face_2.py做的事情是把检测到的人脸区域裁剪出来统一尺寸后存盘。这里有个关键设计一个数据集里至少要有两个人——你自己和一个同事或同学否则 CNN 只需要学「有人/没人」而不用学「是谁」后面解锁逻辑没法验证。import cv2 import os person_name input(输入你的名字(拼音): ) save_dir fdataset/{person_name} os.makedirs(save_dir, exist_okTrue) cap cv2.VideoCapture(0) face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) count 0 while count 200: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, 1.1, 5, minSize(100, 100)) if len(faces) 1: x, y, w, h faces[0] face_roi frame[y:y h, x:x w] face_roi cv2.resize(face_roi, (128, 128)) cv2.imwrite(f{save_dir}/{count:04d}.jpg, face_roi) count 1 cv2.imshow(capture, face_roi) if cv2.waitKey(10) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() print(f已保存 {count} 张到 {save_dir})这段代码有几个细节值得琢磨。要求len(faces) 1是为了排除画面里出现多个人脸的脏数据——如果同时框到你和背后的同事这张图就会变成标签错乱的样本。每检测到一张就存一张但通过waitKey(10)控制采集节奏实际每秒大概存 5~8 张200 张大约需要半分钟期间你要缓慢转头、变换角度、调整表情。图像统一resize到 128×128 有三个原因一是 CNN 输入尺寸必须固定二是 128 比 64 保留更多纹理信息比 224 省内存CPU 训练也能跑三是裁剪后的人脸区域已经是正方形直接缩放不会严重变形。采集时最容易犯的错是偷懒只拍正脸。真实场景里摄像头角度固定但人会有低头、侧头、抬头动作训练集全正脸的话模型一遇到你低头看手机就直接判「不认识」解锁逻辑就会失灵。我一般会让用户拍三组正视镜头、左右转头各 30 度、远近两个距离。3. CNN 模型训练face_3.py 里的网络结构与调参逻辑3.1 数据集组织与预处理目录即标签采集完的数据长这样dataset/yourname/0000.jpg、dataset/colleague/0000.jpg。目录名就是标签这是小型图像任务最省事的组织方式ImageDataGenerator可以直接按目录结构读取并自动生成标签不用手写 CSV 标注文件。from tensorflow.keras.preprocessing.image import ImageDataGenerator data_gen ImageDataGenerator( rescale1.0 / 255, validation_split0.2, rotation_range15, width_shift_range0.1, height_shift_range0.1, horizontal_flipTrue ) train_ds data_gen.flow_from_directory( dataset, target_size(128, 128), batch_size16, subsettraining, class_modesparse ) val_ds data_gen.flow_from_directory( dataset, target_size(128, 128), batch_size16, subsetvalidation, class_modesparse )rescale1.0/255把像素值从 0~255 压缩到 0~1这个步骤不做的话CNN 前几层权重更新会被大数值输入带偏训练很容易发散。validation_split0.2表示从每个人目录里抽出 20% 作为验证集注意flow_from_directory的两个subset参数必须配合使用且数据集目录下每个人至少要有两张以上图片否则抽样会失败。class_modesparse对应的是整数标签配合sparse_categorical_crossentropy损失函数使用。如果你用categorical标签会变成 one-hot 编码损失函数也得换成categorical_crossentropy这是新手最容易混的一对组合。数据增强参数rotation_range15等做了轻度扰动。小数据集只有两三百张图不加增强的话 CNN 几乎必然过拟合——验证集准确率停在 90% 上下训练集刷到 99%一上摄像头就露馅。增强相当于免费扩充样本量我一般会开旋转和平移翻转只在人脸对称性可接受时开。3.2 网络结构四层卷积的小模型为什么够用face_3.py里的模型是一个经典的轻量 CNN三层卷积 池化 全连接。这个体量在人脸识别门禁机那种嵌入式场景里跑不动真实推理但在课程设计和本机锁屏场景下CPU 实时推理完全够用。import tensorflow as tf from tensorflow.keras import layers, models num_classes 2 # 按你的数据集人数改 model models.Sequential([ layers.Conv2D(32, (3, 3), activationrelu, input_shape(128, 128, 3)), layers.MaxPooling2D((2, 2)), layers.Conv2D(64, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), layers.Conv2D(128, (3, 3), activationrelu), layers.MaxPooling2D((2, 2)), layers.Flatten(), layers.Dense(128, activationrelu), layers.Dropout(0.5), layers.Dense(num_classes, activationsoftmax) ]) model.compile( optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy] ) model.summary()每层的作用拆开看第一个Conv2D(32, (3, 3))提取低级特征——边缘、颜色块、明暗交界MaxPooling2D把特征图尺寸减半既降低计算量又增强平移不变性第二层 64 个卷积核开始组合出五官局部特征比如眼睛区域、眉毛走向第三层 128 个核负责更抽象的模式。Flatten之后接Dense(128)做全局特征整合Dropout(0.5)随机屏蔽一半神经元防止过拟合最后的Dense(num_classes, softmax)输出每个类别的概率。为什么不直接上 ResNet 或者 MobileNet两个原因。第一数据集只有几百张图深层网络一上来就严重过拟合第二锁屏场景要求实时响应每帧推理控制在 50ms 以内小网络在普通笔记本 CPU 上就能做到。这里体现的工程思想是「模型复杂度要匹配数据量和硬件」不是越深越好。如果你想往后走换 MobileNetV2 做迁移学习是完全可行的升级路径但那是后话。3.3 训练与保存epochs、batch_size 和模型导出history model.fit( train_ds, validation_dataval_ds, epochs30, batch_size16 ) model.save(face_model.h5) print(模型已保存为 face_model.h5)epochs30对这个小网络和几百张图的数据集来说大约在第 15~20 轮就能收敛。训练过程中盯验证集准确率如果它连续 5 轮不涨而训练集还在涨就是过拟合信号应该提前停止而不是等满 30 轮。batch_size16是 CPU 训练的安全值调成 32 会快一些但内存小的机器容易报错。model.save(face_model.h5)保存的是 Keras H5 格式包含了网络结构和权重。后面face_4.py加载时直接tf.keras.models.load_model就能拿到完整模型不用重新定义网络结构。训练结束建议做一次快速验证加载模型对每个类别的验证集图片跑一遍预测打印置信度。这一步能提前发现「模型把某个人和某个人搞混」的隐患比直接上摄像头再排查省事得多。4. 识别与系统联动face_4.py 如何锁定和解锁 Win10 屏幕4.1 实时识别每 5 帧检测一次的节奏控制face_4.py是整个项目的主程序整合了模型加载、实时识别和系统控制。识别部分的核心不是检测频率而是检测策略——不追求每一帧都做推理而是控制节奏避免 CPU 占用过高。import cv2 import tensorflow as tf import numpy as np model tf.keras.models.load_model(face_model.h5) face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) cap cv2.VideoCapture(0) frame_count 0 unlocked True while True: ret, frame cap.read() if not ret: break frame_count 1 if frame_count % 5 ! 0: continue gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale(gray, 1.1, 5, minSize(100, 100)) if len(faces) 0: continue x, y, w, h faces[0] face_roi cv2.resize(frame[y:y h, x:x w], (128, 128)) face_roi face_roi / 255.0 face_roi np.expand_dims(face_roi, axis0) pred model.predict(face_roi, verbose0) person_id np.argmax(pred) confidence pred[0][person_id] label fperson_{person_id}: {confidence:.2f} cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(frame, label, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(face recognition, frame) # 后续根据 person_id 和 confidence 决定锁定/解锁 if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()关键点有两处。第一frame_count % 5 ! 0时跳过处理也就是每秒只做 5~6 次推理而不是 30 次这样可以显著降低 CPU 占用。第二np.expand_dims把单张图像扩成(1, 128, 128, 3)的批次维度这是 TensorFlow 模型的固定输入要求漏了这步会直接报维度错误。predict返回的是各类别概率数组np.argmax取最大概率对应的类别索引confidence是对应的置信度。决策逻辑不能只看是不是「本人」还要看置信度是否超过阈值——即使模型只认识两个人面对一个陌生人时它也会硬分一个类别出来置信度阈值就是用来兜底的。我一般设 0.7低于这个值就视为「不认识」。4.2 锁定屏幕ctypes 调用 LockWorkStation 的两种方式锁定屏幕是 Windows 系统级操作不需要管理员权限因为锁屏是用户自己会话内的动作不是系统篡改。项目里用的是ctypes直接调user32.dll这是最干净的方式。import ctypes def lock_screen(): ctypes.windll.user32.LockWorkStation()这一行代码执行后系统立刻进入锁屏状态和手动按Win L效果完全一样。另一种等价写法是rundll32.exe user32.dll,LockWorkStation两者区别不大但ctypes方式在 Python 里更好控制返回值和异常处理。要注意的是LockWorkStation调用后当前会话被锁定你的 Python 程序仍在后台运行摄像头还在工作——这正好为后面的自动解锁提供了条件。有一点必须提醒锁屏动作触发不要太激进。检测到一次「非本人」就锁屏会让你体验极差——同事路过你工位、你低头捡东西、摄像头角度偏移这些瞬间都会误判。后面避坑章节会讲连续帧策略这里先理解锁定 API 本身。4.3 解锁屏幕识别通过后模拟输入密码解锁是 Windows 体系里比较敏感的部分。系统本身不允许任何程序直接绕过密码解锁会话——这是安全机制不应该也不应该去突破它。课程设计里常见的做法是识别到本人且置信度超过阈值后用pyautogui模拟键盘输入密码密码保存在本地配置文件里。import pyautogui import time import configparser config configparser.ConfigParser() config.read(config.ini) password_seq config.get(unlock, password_seq, fallback) def try_unlock(confidence): if confidence 0.85: time.sleep(1.5) # 等待锁屏动画结束 pyautogui.typewrite(password_seq, interval0.08) pyautogui.press(enter)这里有几个工程细节。time.sleep(1.5)是等锁屏界面完全渲染出来同时避免在锁屏动画期间按键丢失interval0.08是每两个字符之间的间隔太快的话 Win10 密码框偶尔会吞字符。密码存config.ini而不是硬编码在face_4.py里是为了换密码时不用改代码也方便在演示时临时禁用解锁功能。这个方案的定位要清楚它是给课程设计演示用的自动化模拟不是绕过 Windows 安全机制。如果你的目标是类似 Windows Hello 那种真正的「刷脸解锁」那需要红外摄像头和系统级驱动支持不是 Python 脚本能实现的。这个边界写进你的课程设计报告里反而是加分项——说明你理解安全边界。4.4 启动脚本myface.bat 与 hide.vbs 的分工项目里最后的两个文件myface.bat和hide.vbs解决的是「怎么优雅地启动程序」问题。直接用命令行跑python face_4.py会一直留着一个黑窗口既不美观又容易被误关所以项目用 VBS 脚本隐藏窗口。myface.bat是入口批处理echo off cd /d %~dp0 wscript.exe hide.vbscd /d %~dp0把工作目录切到脚本所在目录防止双击运行时找不到模型文件和配置文件。wscript.exe hide.vbs调用 VBS 脚本。hide.vbs是隐藏窗口的核心Set WshShell CreateObject(WScript.Shell) WshShell.Run python face_4.py, 0, FalseWshShell.Run的第二个参数0表示窗口状态为隐藏False表示不等待程序结束就继续执行。这两行配合的效果是双击myface.bat→ VBS 后台启动face_4.py→ 黑窗口消失 → 只有摄像头指示灯亮着程序在后台运行。这个启动组合拳在很多 Python 小工具里都能复用比如后台定时任务、监控脚本。需要注意的坑是WshShell.Run里的路径如果 Python 不在系统 PATH 里要把python换成绝对路径否则 VBS 会报找不到程序。5. 实战避坑从 ModuleNotFoundError 到锁屏闪退的 5 条记录5.1 ModuleNotFoundError: No module named cv2现象运行face_1.py直接报错ModuleNotFoundError: No module named cv2但在终端里pip show opencv-python又能看到已安装。原因最常见的是 Python 环境错位——你用pip install装到了一个 Python 环境运行脚本时用的却是另一个环境。很多 Windows 机器上同时存在系统 Python、Anaconda、PyCharm 自带的虚拟环境三个环境互不相通。解决先确认运行脚本的 Python 是哪个在脚本目录执行python --version和pip --version看两者的路径是否指向同一个环境。再用python -m pip install opencv-python强制在指定 Python 下安装而不是裸调pip。安装后测试python -c import cv2; print(cv2.__version__)能打印版本号才算真装好。5.2 cv2.error: OpenCV 4.4.0 的 haarcascade 路径加载失败现象CascadeClassifier构造时报cv2.error: ... haarcascade_frontalface_default.xml路径找不到尤其是从压缩包解压后直接运行时必现。原因face_1.py早期版本用相对路径haarcascade_frontalface_default.xml加载分类器但这个 XML 文件在 OpenCV 的安装目录里不在项目目录里。解压后如果没把 XML 复制出来路径就失效。解决用cv2.data.haarcascades拼接绝对路径。这个常量指向 OpenCV 安装目录下的data/haarcascades/无论项目放哪都能找到。注意如果你自定义了HAAR_CASCADE_PATH环境变量优先级会覆盖默认路径排查时先检查这个变量是否被改过。5.3 摄像头打开失败或画面卡死现象VideoCapture(0)一直返回False或者程序运行一会儿画面冻结在最后一帧。原因0是摄像头设备索引笔记本内置摄像头一般是0但如果你插了 USB 摄像头系统分配索引可能是1或2。另外摄像头被其他程序占用比如微信视频、OBS Studio会导致read()失败。解决加一层设备探测依次尝试索引for idx in range(3): cap cv2.VideoCapture(idx) if cap.isOpened(): print(f使用摄像头索引 {idx}) break运行中卡死要检查ret返回值连续读取失败的帧数超过阈值就自动重启摄像头——cap.release()后重新VideoCapture。我遇到过挂着锁屏程序一整天后摄像头掉线的情况加一个「连续 50 帧读取失败就重建摄像头连接」的逻辑就稳了。5.4 TensorFlow 2.0 CPU 训练内存溢出现象训练到一半进程报内存错误或者整个电脑卡死风扇狂转。原因batch_size设置过大加上ImageDataGenerator的数据增强在 CPU 上实时生成增广图像内存占用成倍增长。128×128 的图看似不大但一个 batch 的中间张量加上梯度计算实际占用远超你的预期。解决先把batch_size降到 8 或 16同时把workers参数从默认调低避免数据增强的进程池抢占太多 CPU 资源。还可以把图像尺寸从 128×128 降到 96×96精度损失很小但内存占用下降超过 40%。如果你在 TensorFlow 2.0 里用 GPU 版本还要留意allow_growth配置否则显存会被一次性占满physical_devices tf.config.list_physical_devices(GPU) if physical_devices: tf.config.experimental.set_memory_growth(physical_devices[0], True)5.5 锁屏后重新登录程序被系统杀掉现象触发锁屏后再刷脸解锁成功但face_4.py已经不在运行了摄像头指示灯灭着又要手动重新启动。原因Win10 默认对后台应用有电源管理策略尤其是 UWP 应用和通过命令行启动的 Python 进程。当屏幕锁定且系统进入节能状态时非前台进程可能被挂起或回收。hide.vbs启动的进程没有注册成系统服务生命周期跟当前用户会话绑定锁屏重登录后部分环境下进程会死掉。解决两个方向。一是把启动方式从 VBS 改成创建计划任务设置「登录时运行」和「唤醒时重启」二是在face_4.py里加崩溃自愈——用 Pythonsubprocess起一个守护进程定期探测face_4.py是否存活死了就拉起来。常见做法是把face_4.py里锁屏和解锁逻辑抽成独立模块守护进程只负责保活职责分离。还有就是摄像头驱动的问题。部分笔记本摄像头驱动在锁屏后进入省电模式解锁后要等 2~3 秒才能恢复采集程序如果在这个窗口期反复尝试读取会因超时报错退出。代码里要对read()失败做容忍处理连续失败才退出而不是一次失败就崩。6. 把它变成能一直跑的小工具开机自启、防误锁与模型迭代6.1 开机自启与窗口隐藏的组合拳课程设计交完如果你想让这个程序在工位上真正发挥作用第一步是让它开机自动跑。把myface.bat的快捷方式丢进启动目录是最简单的方式——按Win R输入shell:startup打开启动文件夹把myface.bat的快捷方式放进去。但注意这个方式依赖用户登录且 UAC 弹窗会影响自启。更稳的是通过任务计划程序创建登录触发器任务操作设为运行wscript.exe并带hide.vbs参数。这样即使 VBS 被安全软件拦截计划任务也更容易获得系统放行。我实际用下来后者比启动目录可靠——至少不会出现在桌面晃一眼看到一堆黑窗口的情况。6.2 防误锁连续 N 帧判断 置信度阈值锁屏逻辑最容易翻车的地方不是技术实现是误触发的体验问题。单独一帧检测到「非本人」就锁屏你会被反复锁屏逼疯的。正确策略是「连续 阈值」双保险miss_count 0 unlock_cooldown 0 while True: # ... 识别逻辑 ... if confidence 0.7 or person_id ! SELF_ID: miss_count 1 else: miss_count 0 if miss_count 5 and unlock_cooldown 0: lock_screen() unlock_cooldown 60 # 锁屏后 60 帧内不再触发第二次锁屏连续 5 帧约 1 秒判定为非本人或低置信度才触发锁屏。unlock_cooldown防止你解锁后系统还没反应过来又立刻锁上形成「锁屏-解锁-锁屏」的死循环。这两个参数我实际调过很多次连续帧数设 3 太灵敏设 8 又嫌迟钝5 在多数工位场景是甜点值冷却帧数 60 对应约 10 秒足够完成解锁后的状态稳定。6.3 模型迭代用误识别样本重训项目迭代的终点是「把误判样本收集起来重训模型」。具体做法每次识别时把置信度在 0.5~0.7 之间的模糊样本存到hard_samples/目录积累到二三十张后把它们合并进训练集重训一轮。这些小样本往往能修正模型在角度、光线上的盲区。ArcFace 那种商用方案的差距就在数据和训练策略上项目本身的结构没有本质区别——先检测、再对齐、然后特征比对你的 CNN 做的是简化版特征提取。另外给一个习惯建议每次重训模型前把旧的face_model.h5改名备份避免训练到一半断电导致模型文件损坏。我这人曾经图省事直接覆盖保存结果第二天程序加载模型时报HDF5 format错误整天的演示差点泡汤。从那以后我每次重训都会先复制一份带时间戳的模型备份再开始训练循环这个习惯帮我省掉了好多次返工。希望这些踩坑记录能帮你把这条路走得更顺一些让这份代码真正变成你自己能掌控的人脸识别小工具。本文还有配套的精品资源点击获取