☰
基于OpenCV与TensorFlow的CNN人脸识别Win10锁屏项目实战解析
2026/10/1 23:10:46 网站建设 项目流程

简介:采用OpenCV与TensorFlow 2.0搭建CNN卷积神经网络,完成人脸识别后自动锁定与解锁Win10屏幕的实战项目资源,适合毕业设计、课程设计以及初学计算机视觉的开发者参考。资源共7个文件,压缩包仅13KB,包括4个Python脚本、1个VBS脚本、1个BAT批处理与1个Markdown说明文档。Python文件按训练、检测、锁定等阶段拆分,便于逐步理解人脸数据采集、模型训练与实时识别的完整流程;VBS与BAT脚本配合实现后台静默运行与一键启动,README提供项目说明。目前已有302人学习下载。整套代码轻量完整、开箱即用,可迁移到摄像头门禁、课堂考勤等类似场景,是快速上手CNN人脸识别落地开发的实用参考。

1. 用摄像头给你的 Win10 加把脸锁:CNN 人脸识别锁定解锁项目值得跑一遍

离开工位去接杯水,回来发现有人动过你的电脑,这种事在办公室比想象中常见。Windows 10 自带 Windows Hello 人脸解锁,但硬件门槛不低,很多老笔记本和台式机根本不支持;第三方锁屏工具又往往要装全家桶、连云端,怎么看都不划算。这个项目是条完全本地化的路子:OpenCV 负责从摄像头抓脸并做预处理,TensorFlow 2.0 训练一个小型 CNN 卷积神经网络做人脸分类,识别到“本人”就保持解锁,识别不到或置信度不够就调用系统接口直接锁屏。整条链路跑在 Windows 10 上,代码量不大,既能当计算机视觉方向的课程设计/毕业设计交,也能当人脸识别门禁的桌面级雏形来看。

2. 项目结构与全局流程:OpenCV 负责看,TensorFlow 负责认

拿到压缩包先别急着解压跑,把文件角色搞清楚,后面排错会省很多时间。这个项目不是单个脚本能搞定的,而是按“采集样本 → 训练模型 → 实时识别 → 触发锁屏”一条线串起来的,缺一环后面都跑不通。

2.1 五个脚本文件的分工与调用顺序

先看文件清单,每个文件和它的职责对应起来:

文件职责定位
face_1.py摄像头采集人脸样本,检测 + 裁剪 + 灰度化后归档到 dataset/ 目录
face_2.py读取样本,训练 CNN 分类器,保存最优模型为 .h5 文件
face_3.py实时识别,置信度低于阈值或无人脸时触发锁屏
face_4.py对照验证脚本,用于换模型、调阈值时做 A/B 对比
hide.vbs以隐藏窗口方式拉起 myface.bat,避免命令行黑框
myface.bat一键启动识别主程序,自动切到项目目录和 Python 环境
README.md依赖清单、运行顺序和参数说明

调用顺序很直接:先跑 face_1.py 采集 200 张左右的本人人脸图;再跑 face_2.py 训练出 face_model.h5,训练完会打印 class_indices 标签顺序;最后用 myface.bat 或直接python face_3.py启动实时识别。face_4.py 不是主链路里的必选项,但它存在的意义是让你改阈值、换模型文件时有个不干扰主流程的验证入口。我一般会把训练好的模型复制一份,在 face_4.py 里先做批量验证,确认指标没问题再切到 face_3.py 正式上线。

2.2 从摄像头画面到 LockWorkStation:整条数据流怎么串

整个程序的数据流可以概括成六步:摄像头取帧 → Haar 级联检测人脸 → 裁剪灰度缩放 → 模型预测得到每个类别的概率 → 与阈值比较 → 决定放行还是锁屏。前四步是每几帧才做一次,不是逐帧跑,因为办公场景里人脸不会瞬移,5 帧做一次检测和逐帧做,识别效果几乎没差别,CPU 占用却能降一个量级。

为什么会用 OpenCV 搭配 TensorFlow,而不是纯深度学习端到端?因为在“锁定/解锁电脑”这个场景里,人脸检测和人脸识别是两件事。Haar 级联分类器在 CPU 上就能稳定框出人脸,不需要训练、不需要 GPU,OpenCV 里直接内置了模型文件;而“认出这是谁”才需要 CNN。先 Haar 后 CNN 这种分工是工程上的常规套路,检测快、识别准,两个环节互相解耦,哪个环节出问题就单独换哪个。

CNN 参数量也不大,图像输入缩到 64×64 灰度图,两层卷积加两层全连接,几百张样本在 CPU 上十几分钟能收敛,有 TensorFlow 2.0 的 Keras API 加持,代码量被压得很短。有一点必须提前说清楚:这个项目做的是人脸“分类”,不是人脸“验证”。分类意味着每个人是固定类别,新增一个人要重新训练;验证是 FaceNet 那种学嵌入向量、用距离比对的思路。对单机自用场景,分类够用;如果你想扩展成门禁系统那种“库里有几百号人也能认”的方案,就得换思路了。README 里最好把这个边界写明白,免得别人拿去当开集识别用然后翻车。

3. face_1.py 与 face_2.py 实战:数据采集和 CNN 模型训练

人脸识别系统的上限在数据,不在模型。face_1.py 采集的质量直接决定 face_2.py 训练出来的模型能不能用,所以这一章把两个脚本的参数和逻辑都拆开讲。

3.1 face_1.py:用 Haar 级联把脸抠出来,按人归档

import cv2 import os name = "your_name" # 改成你的名字,一个名字对应一个文件夹 save_dir = f"dataset/{name}" os.makedirs(save_dir, exist_ok=True) face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) cap = cv2.VideoCapture(0) # 0 是默认摄像头,外接摄像头可能是 1 count = 0 while count < 200: # 采集 200 张,太少后面训练容易过拟合 ret, frame = cap.read() if not ret: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale( gray, scaleFactor=1.1, minNeighbors=5, minSize=(50, 50) ) for (x, y, w, h) in faces: face = gray[y:y+h, x:x+w] # 只保留人脸区域 face = cv2.resize(face, (64, 64)) # 统一到模型输入尺寸 cv2.imwrite(f"{save_dir}/face_{count:04d}.jpg", face) count += 1 cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow("collect", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

逻辑说明:对每一帧做灰度化,然后用 Haar 级联检测人脸框,框到的人脸裁出来、缩放到 64×64 保存。统一转灰度是故意的,因为 face_2.py 里 CNN 第一个卷积层的输入通道数设的就是 1,如果这里存成三通道彩色图,后面训练时输入尺寸会对不上,报错报得莫名其妙。

参数说明里有两个值得细看:scaleFactor=1.1 表示每层图像金字塔缩小 10%,值越小检测越精细但越慢;minNeighbors=5 表示一个候选区域要被至少 5 个相邻窗口确认过才算人脸,调大会漏检,调小会误检。minSize=(50, 50) 用来过滤太小的框,把远处走动的杂脸挡在采集范围外。采集上限 200 张够小数据集训练用,想让模型更稳可以提到 300,但别低于 100,样本太少后面训练几乎必过拟合。

采集的时候别一直端端正正对着摄像头,左右偏头、戴不戴眼镜、顺光逆光都各拍一点。我一般会前后挪一挪椅子,让脸在画面里占的比例有变化,这样训练出来的模型对角度和距离的泛化会好不少。

3.2 face_2.py:Keras 读目录训练 CNN,保存最优模型

import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.callbacks import ModelCheckpoint IMG_SIZE = 64 BATCH = 32 EPOCHS = 30 datagen = ImageDataGenerator( rescale=1./255, validation_split=0.2, rotation_range=10, width_shift_range=0.05, height_shift_range=0.05, ) train_gen = datagen.flow_from_directory( "dataset", target_size=(IMG_SIZE, IMG_SIZE), color_mode="grayscale", batch_size=BATCH, subset="training", ) val_gen = datagen.flow_from_directory( "dataset", target_size=(IMG_SIZE, IMG_SIZE), color_mode="grayscale", batch_size=BATCH, subset="validation", ) model = Sequential([ Conv2D(32, (3, 3), activation="relu", input_shape=(IMG_SIZE, IMG_SIZE, 1)), MaxPooling2D((2, 2)), Conv2D(64, (3, 3), activation="relu"), MaxPooling2D((2, 2)), Flatten(), Dense(128, activation="relu"), Dropout(0.5), Dense(train_gen.num_classes, activation="softmax"), ]) model.compile( optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"], ) ckpt = ModelCheckpoint("face_model.h5", save_best_only=True, monitor="val_accuracy") model.fit(train_gen, validation_data=val_gen, epochs=EPOCHS, callbacks=[ckpt])

逻辑说明:flow_from_directory会按 dataset/ 下的子目录名自动生成标签,这是最容易踩坑的地方——目录名写错、或者多了个隐藏文件夹,生成的标签表就全乱了。数据增强部分加了小角度旋转和轻微平移,用来模拟坐姿的微小晃动;水平翻转不要开,人脸是不对称的,翻转之后特征语义就变了。CNN 结构是两轮卷积加池化,再接 128 维全连接和 Dropout,最后 softmax 输出类别概率。

参数说明:EPOCHS=30 配合save_best_only=True,Keras 只看验证集准确率,val_accuracy 不再上升就自动停在最优轮次,不会死板地跑满 30 轮。Dropout=0.5 在样本只有一两百张的时候是刚需,不是可选项——模型容量一大就容易把训练集背下来,测试时换个人就翻车。训练结束后打印一下model.class_indices,把标签顺序记到 README 里,face_3.py 的 CLASS_NAMES 必须和它严格一致。

3.3 训练完先看三个数,再决定要不要继续

训练收尾别急着跑识别,先在终端确认三件事。第一是 val_accuracy,达到 0.95 以上基本可用;0.90 以下先回数据层找问题,看看是不是样本都在同一个角度拍的。第二是 loss 曲线,val_loss 先降后升说明过拟合,常见做法是加大数据增强强度、补样本或者把 Dropout 提到 0.6。第三是 class_indices 的顺序,这个错得最隐蔽:模型文件本身不存标签名,face_3.py 里顺序写反就会出现“自己锁自己”的诡异现象。我的习惯是把这三项记在 README 的“训练记录”小节里,下次调参对着看,省得反复试。

4. face_3.py 识别、face_4.py 验证与隐藏启动:锁屏不再弹黑框

训练出模型只是第一步,真正决定体验的是识别脚本的循环逻辑和启动方式。这一章把 face_3.py、face_4.py、hide.vbs、myface.bat 这四个文件的用法串起来讲。

4.1 face_3.py 的识别循环:置信度阈值怎么设

import cv2 import numpy as np import tensorflow as tf import os model = tf.keras.models.load_model("face_model.h5") # 顺序必须和 face_2.py 打印的 class_indices 一致 CLASS_NAMES = ["nobody", "your_name"] CONF_THRESHOLD = 0.8 # 本人置信度低于这个值就锁屏 CHECK_EVERY = 5 # 每 5 帧做一次识别,省 CPU face_cascade = cv2.CascadeClassifier( cv2.data.haarcascades + "haarcascade_frontalface_default.xml" ) def lock_screen(): # 调用 Windows 锁屏接口,等价于按 Win+L os.system("rundll32.exe user32.dll,LockWorkStation") cap = cv2.VideoCapture(0) frame_id = 0 while True: ret, frame = cap.read() if not ret: continue frame_id += 1 if frame_id % CHECK_EVERY != 0: continue gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(64, 64)) if len(faces) == 0: # 无人脸直接锁屏,适合离开工位的场景 lock_screen() continue for (x, y, w, h) in faces: face = gray[y:y+h, x:x+w] face = cv2.resize(face, (64, 64)).reshape(1, 64, 64, 1) / 255.0 prob = model.predict(face, verbose=0)[0] label = CLASS_NAMES[int(np.argmax(prob))] me_idx = CLASS_NAMES.index("your_name") if prob[me_idx] < CONF_THRESHOLD: lock_screen()

逻辑说明:主循环每隔 CHECK_EVERY 帧取一次画面,检测到人脸就送进模型预测,看“your_name”这个类别的概率是不是高于阈值;没人脸时直接锁屏,这是针对“离开工位”场景设计的策略。rundll32.exe user32.dll,LockWorkStation是 Windows 系统自带的锁屏接口,不需要装任何额外程序,也不涉及系统安全策略改动。

参数说明:CONF_THRESHOLD=0.8 是经验值,但实际应该根据 face_4.py 的统计结果来定。办公环境光照稳定、摄像头固定,0.75~0.85 之间通常表现都不错;如果家里光线变化大,阈值要下调到 0.7 左右。没人脸就锁屏这个策略有个变体需求:有人只是转个身、低个头,画面里短暂没人脸,电脑就锁了,体验很差。我一般会在脚本里加一个last_seen时间戳,连续 3 秒没检测到人脸才锁屏,这个改动不大但体验提升非常明显。

4.2 face_4.py 的定位:先验证再上线

face_4.py 在我的工作习惯里是一个“验证台”角色,不是主流程的一部分。常见做法是复制一份 face_3.py,把锁屏动作去掉,改成打印每次预测的置信度,专门用来回答两个问题:一是本人 50 次预测的最低置信度是多少,把这个最低值乘以 0.9 作为正式的 CONF_THRESHOLD,留出安全余量;二是对比不同模型文件,比如 face_model_30.h5 和 face_model_40.h5,在验证集上哪个更稳,就切哪个给 face_3.py 用。

别小看这个验证步骤,直接拍脑袋定阈值最容易出问题:阈值太高,模型把你自己的脸也锁在外面;阈值太低,同事路过摄像头前电脑都不带锁的。face_4.py 存在的意义就是让你在改参之后有个不打扰主流程的测试入口。

4.3 hide.vbs 与 myface.bat:隐藏窗口和开机自启

Set ws = CreateObject("Wscript.Shell") ws.Run "cmd /c myface.bat", 0, False
@echo off cd /d %~dp0 call conda activate face_env python face_3.py

VBS 的第二个参数 0 表示以隐藏窗口方式启动 cmd,这样程序运行时不弹黑色命令行框;第三个参数 False 表示不等待子进程结束,VBS 本身瞬间退出。bat 文件里的%~dp0是批处理脚本所在目录,加上cd /d %~dp0避免在别的目录启动时找不到 face_model.h5。如果你没用 conda,把call conda activate face_env这一行删掉,直接python face_3.py就行。

开机自启的做法是:Win+R 输入shell:startup回车,打开启动文件夹,把 hide.vbs 的快捷方式放进去。下次开机系统会自动以隐藏窗口方式拉起识别程序,全程无感。不建议把 bat 直接塞启动文件夹,那样每次开机都会弹一个黑框悬在桌面上,还得手动关。

5. 避坑:从 No module named 'opencv' 到反复误锁屏,我踩过的五个坑

这个项目跑通不难,但跑得顺不顺,全看能不能避开下面这几个高频问题。有些坑属于环境,有些坑属于数据,有些坑属于策略,按频率从高到低排。

5.1 环境安装:pip 装错环境、缺 VC++ 运行库

现象:import cv2直接报ModuleNotFoundError: No module named 'opencv',即使你明明已经pip install opencv-python成功了,换个终端再跑还是找不到。

原因:一台机器上装了多个 Python,pip 装包的 Python 和运行脚本的 Python 不是同一个解释器。Windows 上这种情况非常普遍,尤其是装过 Anaconda 又装过官方 Python 的机器。

解决:用 conda 建独立环境,把依赖全部装进这个环境里,之后所有脚本都在这个环境里跑。conda create -n face_env python=3.8,然后conda activate face_env,在环境内执行python -m pip install opencv-python tensorflow,用python -m pip而不是裸的pip,能确保装进当前环境而不是别的环境。另外 Windows 上 TensorFlow 依赖 VC++ 运行库,运行时报DLL load failed基本是没装运行库,去微软官网下vc_redist.x64.exe装一遍就好了。

5.2 摄像头采集:被人占用、USB 拉流中断

现象:程序启动后画面黑屏或者闪一下就没了,cap.read()返回的 ret 一直是 False,有时候跑着跑着画面突然冻结,再过几秒才恢复。

原因:摄像头被其他软件占用了,最常见的元凶是钉钉会议、浏览器视频通话、微信视频,摄像头是独占设备,一个进程占着,另一个进程打开就是黑屏;USB 摄像头则是供电不稳或者线缆接触不良,拉流会中断。

解决:先把占用摄像头的软件全部退掉,再用cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)指定 DirectShow 后端,Windows 下比默认后端稳很多。如果是硬件问题,在循环里加重连逻辑:连续 30 帧 ret 为 False 就cap.release(),等一秒重新VideoCapture(0)。这个问题排查起来有点玄学,但九成是占用和供电两个原因,先试占用再试线缆。

5.3 数据太脏:背景和椅子比脸还重要

现象:训练时 val_accuracy 高得离谱,跑到 99%,结果换个人坐在同一个工位上也能解锁,或者自己换个角度就被锁在外面。

原因:采集时背景单一、机位固定,CNN 偷懒了,它学到的可能是“这张椅子 + 这张脸”的组合,甚至背景比脸更重要。小数据集训练很容易这样,模型没有真正学到面部特征。

解决:采集阶段的多样性比数量更重要。同一个上午分三次采,每轮戴眼镜、摘眼镜、左右转头、前后挪椅子、开灯关灯各拍一段;数据增强参数也加上,rotation_range、width_shift_range、height_shift_range 这三个值不要设成 0。如果发现模型已经被背景污染了,最省事的办法是把训练集里背景变化大的样本挑出来删掉,重新训练一轮看看。

5.4 阈值拍脑袋:自己锁自己 vs 陌生人开锁

现象:阈值设 0.9,识别到自己仍然咔哒一声锁屏;阈值改成 0.5,人走了电脑还亮着。

原因:置信度阈值是个和光照、样本量、摄像头位置强相关的量,直接套别人的经验值肯定会翻车。0.9 对某些光线差的环境就是太高,0.5 又低到把同事误认成你。

解决:用 face_4.py 把“本人坐在工位上的真实场景”跑 50 次预测,记录置信度分布,把最低值乘 0.9 作为正式阈值。我这边实际数据一般是本人置信度稳定在 0.9~0.97 区间,最终阈值设在 0.78~0.82 之间。记住一条原则:阈值要留余量,但不能余量太大,余量大到把本人也锁了的程度,项目就废了。

5.5 自启动:黑框悬桌面、路径带中文

现象:开机自启之后一个黑色 cmd 窗口一直悬在桌面上;或者程序在资源管理器里双击能跑,放到启动目录就报错。

原因:bat 直接放启动目录会弹窗;项目路径带中文、带空格,而 TensorFlow 读取模型文件和 OpenCV 写图片时遇到中文路径容易出编码问题,报错内容还不直观。

解决:自启动入口统一用 hide.vbs,不要用 bat 本体;项目所在目录不要带中文和空格,直接放C:\face_project这种干净路径,Python 的路径处理和模型加载都会省心很多。启动目录用shell:startup而不是去翻C:\ProgramData,Path 带空格是 Windows 启动目录最常见的坑。

6. 让识别更稳的三个技巧:混淆矩阵、MobileNetV2、连续帧投票

模型跑通只是及格线,下面这三个技巧是让识别从“能用”变成“好用”的关键。

6.1 用混淆矩阵找“被认错的脸”

训练日志只能告诉你准确率,告诉不了你哪两个类别在互相打架。

from sklearn.metrics import classification_report, confusion_matrix # y_true 是验证集真实标签下标,y_pred 是模型 argmax 后的预测下标 print(classification_report(y_true, y_pred)) print(confusion_matrix(y_true, y_pred))

逻辑说明:混淆矩阵的行是真实类别,列是预测类别,非对角线上的数字就是“被认错”的样本。如果只有两个类别,看对角线左下和右上的数字;如果有多个人脸类别,矩阵能直接告诉你哪一个最容易和哪一个混淆。这一步能把训练阶段的模糊信号变成明确结论,很多人模型效果上不去,根本原因就是没做过这层诊断。

6.2 从自研 CNN 换到 MobileNetV2:泛化更强

自研的小 CNN 在小数据集上容易过拟合,换迁移学习是立竿见影的升级路线。

import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, GlobalAveragePooling2D base = tf.keras.applications.MobileNetV2( input_shape=(128, 128, 3), include_top=False, weights="imagenet" ) base.trainable = False model = Sequential([ base, GlobalAveragePooling2D(), Dense(2, activation="softmax"), ])

逻辑说明:MobileNetV2 的预训练权重已经见过海量真实图像,拿它当特征提取器,只训练最后的分类层,收敛快、泛化好。代价是输入从单通道灰度图变成三通道彩色图,face_1.py 采集时要改成存彩色图,代码里cvtColor那步删掉即可。这个方案在光线变化大的家里场景尤其稳,如果对识别时效有追求,它对 CPU 的占用也比自研 CNN 小。

6.3 连续 N 帧投票:别因为转头就锁屏

单帧预测的偶然误差是不可避免的,低头看手机、转头说话都可能让置信度瞬间跌破阈值。

from collections import deque votes = deque(maxlen=10) # 最近 10 帧的识别结果 # 每帧识别后追加结果 votes.append("ok" if prob[me_idx] >= CONF_THRESHOLD else "unknown") # 只有 unknown 占多数才锁屏 if votes.count("unknown") >= 8: lock_screen()

逻辑说明:滑动窗口里 10 帧中至少 8 帧判为“非本人”才触发锁屏,单帧的抖动就会被过滤掉。窗口设 10 帧、阈值设 8,换算成时间是 10 帧约等于 1 秒的误判容忍窗口,实际操作中既不迟钝也不敏感。这个方案比单纯加“识别人脸才锁屏”的延迟更靠谱,因为人脸检测会漏、模型会错,但连续 8 帧同时错是小概率事件。

从那以后我每次交付人脸识别项目,都会强制走一遍这套流程:先统计真实场景置信度再卡阈值,跑一遍混淆矩阵确认没有类别互认,最后用连续帧投票兜底。这三步帮我挡掉过至少三次“自己锁自己”的线下翻车。如果你也想用这套流程改造自己的 Windows 10 锁屏,解压这份“基于opencv tenserflow2.0实战CNN人脸识别锁定与解锁win10屏幕.zip”后,按 face_1 采集 → face_2 训练 → face_3 识别 → hide.vbs 自启的顺序走一遍就行,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询