ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

用普通摄像头自制Windows Hello:人脸识别+动态锁实现离开自动锁屏

用普通摄像头自制Windows Hello:人脸识别+动态锁实现离开自动锁屏 1. 先拆解Windows Hello到底在做什么再来谈“自制”很多朋友看到“自制 Windows Hello”这个标题第一反应是Windows Hello不是系统自带功能吗有什么好自制的确实Windows Hello是微软在Windows 10/11里内置的生物识别登录方案但它的硬件门槛卡死了一大批设备——人脸识别需要红外摄像头指纹识别需要专用指纹传感器很多三四年前的笔记本和台式机根本没有这些模块只有一个普普通通的USB摄像头。外接一个经过微软认证的Windows Hello摄像头价格从两三百到上千不等说实话有点肉疼。所以我当时给自己定的目标很朴素用现有的一颗1080p普通摄像头加上开源人脸识别算法做一套低配版Windows Hello实现“人坐下来就亮屏解锁、人走开就自动锁屏”的完整体验。要动手做这件事不能上来就写代码先把Windows Hello的原理搞明白才知道哪些能仿、哪些仿不了。Windows Hello本质上包含了三种登录模态人脸识别、指纹识别、PIN码。其中PIN码是纯软件逻辑不依赖特定硬件任何设备都支持指纹需要专门的电容式或光学式传感器人脸识别是门槛最高的一个它要求摄像头具备红外IR成像能力能捕捉人脸的深度信息和红外纹理用来做活体检测防照片、防视频、防面具。普通RGB摄像头拍出来的彩色图像在人脸识别算法眼里只是一堆像素虽然也能比对出“这是哪张脸”但没有任何手段分辨镜头前是一张真脸还是平板电脑上的一张照片。这一点是整个自制方案最关键的安全边界后面所有的设计都要围绕它来取舍。Windows Hello背后的硬件和软件栈也不是一个开源库能替代的。它依赖TPM可信平台模块芯片来生成和保护密钥生物特征数据通过Windows Biometric FrameworkWBF采集、加密后存储在本地不传云端。识别过程在设备本地完成比对成功后再用受TPM保护的密钥去解锁系统。这套链路的安全级别非常高普通摄像头开源算法只能做到“形似”做不到“神似”。所以我在动手前先给自己划了一条线自制方案定位为家庭电脑的便捷登录方案适合低风险场景不适合处理机密资料的办公设备。这条线想清楚之后踩坑才有方向。2. 自制Windows Hello的总体设计普通摄像头开源算法系统动态锁2.1 硬件和软件清单先列一下我这次用到的全部家当都是常见东西不需要额外买认证硬件。硬件清单普通USB摄像头一台1080p分辨率即可30fps够用不需要红外功能台式机或者笔记本一台系统为Windows 10/11开启蓝牙用于离开检测手机一台带蓝牙平时随身携带软件清单Python 3.9实测3.10、3.11都可以太新的Python版本在装dlib时容易踩编译坑OpenCV-Python摄像头取流、人脸检测、图像保存face_recognition基于dlib封装的人脸识别库识别精度好接口也简单dlibface_recognition的底层依赖安装是最大痛点后面细说系统自带功能Windows动态锁这套方案的软件成本为零唯一要顶住的是dlib的安装过程。2.2 为什么选这套组合而不是上IR摄像头模组我仔细研究过一条“看起来更接近原生Windows Hello”的路线买一块拆机的红外摄像头模组通过USB转接板接到电脑上再找厂商驱动让Windows把它识别成Windows Hello摄像头。这条路的理论成本很低几十到一百多块钱就能买到拆机模组但实际操作坑极深。首先是驱动很多笔记本的IR摄像头模组用的是定制排线接口转接后ID识别不到对应驱动Windows Hello直接不认。其次是固件部分模组出厂时写入的固件只适配原厂机型换到别的机器上IR灯都不亮。再其次是微软的认证要求Windows Hello对摄像头的FOV、IR波长、功率、帧率都有详细规范非认证模组即使能被系统识别识别成功率也很随机。折腾了两周之后我放弃了这条路老老实实用普通USB摄像头开源算法。这套组合的好处是确定性强OpenCV一定能取到流face_recognition的模型是通用的效果可预期。代价就是没法和原生Windows Hello一样在登录界面直接刷脸必须换一种实现思路把“解锁”这件事拆解成“人来了亮屏解锁”“人走了锁屏”两个动作用软件脚本和系统功能分别完成。2.3 关键设计能做什么不做什么做这个项目之前我最想搞清楚的一个问题是Python脚本到底能不能直接操作Windows锁屏界面因为Windows从Vista开始引入了一个叫Secure Desktop安全桌面的机制凡是涉及登录、UAC确认、CtrlAltDel这些敏感操作的界面都在一个受保护的桌面上运行。普通用户态程序往这个桌面上发模拟键盘事件比如SendKeys默认是无效的这是系统层面的安全边界不是技术问题是设计如此。网上有少数方案通过加载驱动或者以SYSTEM权限跑服务来绕但那种做法的风险已经超过了自制项目能承受的范畴我不建议碰。所以我的方案设计成下面这样做一个明确的能力边界划分系统层面的“离开锁屏”用Windows原生动态锁通过蓝牙检测手机信号强度手机离开电脑一定距离后自动执行WinL锁屏系统层面的“唤醒免密”把电源选项里的“唤醒时需要登录”设为“从不”这样屏幕关闭后按任意键即可直接进入桌面不需要再输密码脚本层面的“人在识别”Python脚本常驻后台定时抓摄像头画面判断当前有没有本人脸出现在镜头前持续超过设定时间就模拟按键唤醒屏幕这三者组合起来的体验链是这样的人离开-蓝牙信号变弱-系统自动锁屏人回来坐下-摄像头识别到本人-脚本模拟按键-屏幕亮起直接进入桌面如果识别不到本人屏幕就一直黑着系统处于锁定状态。这个体验相比原生Windows Hello少了一个“输PIN”的环节更像是“自动唤醒自动锁定”组合拳但它确实达成了“免手动操作登录”这个核心目标。3. 手把手实现刷脸解锁脚本3.1 搭建Python人脸识别环境dlib安装是最大痛点环境搭建是整个项目里最劝退人的一步尤其是dlib。face_recognition库本身很好装但它的底层依赖dlib需要编译C代码如果电脑上没有Visual Studio的C生成工具或者CMake编译环境不完整装到一半就会报错。我整理了两条路任选其一。第一条路用dlib预编译wheel包适合Python 3.9~3.11、64位Windows系统简单省事pip install cmake pip install dlib19.24.2如果pip源里没有对应你Python版本的预编译包pip会现场拉源码编译这时候极大概率失败。我建议在安装前先到网上搜一下“dlib 19.24.2 cp311 win_amd64.whl”这种关键词找到对应当前Python版本的wheel文件下载后本地安装pip install C:\path\to\dlib-19.24.2-cp311-cp311-win_amd64.whl第二条路用OpenCV自带的LBPH人脸识别器替代dlib。精度比face_recognition差一些但安装极其简单只需要一行pip install opencv-python。如果只是自己一个人用、环境光线固定LBPH完全够用。我在下面的实操里以face_recognition为主因为它的接口更符合人脸识别场景但如果你的环境装不上dlib直接看3.3节最后的替代方案。装完基础库之后把face_recognition也装上pip install face_recognition验证一下环境是否正常python -c import face_recognition; import cv2; print(ok)能打印出ok说明环境没问题可以继续。3.2 第1步采集人脸样本识别一个人得先让程序记住这个人长什么样。人脸样本质量直接决定识别准确率这里有几个经验多角度、多光照、多表情各采集一些不要只拍一张正脸。我通常采集30张左右包括正面、左右轻微侧脸、戴不戴眼镜、室内正常光线下不同时间段的照片。样本太少或者姿势太单一后续识别时稍微换个角度就会误判。新建一个目录用来存样本比如D:\hello_cam\dataset\owner然后运行下面的采集脚本。这个脚本会打开摄像头检测到人脸就自动裁剪并保存直到攒够30张import cv2 import os import time save_dir rD:\hello_cam\dataset\owner os.makedirs(save_dir, exist_okTrue) cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头打开失败检查索引或驱动) exit(1) face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) count 0 while count 30: ret, frame cap.read() if not ret: continue gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(100, 100) ) for (x, y, w, h) in faces: count 1 face_img frame[y:y h, x:x w] cv2.imwrite(os.path.join(save_dir, f{count:03d}.jpg), face_img) cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText( frame, fcaptured: {count}/30, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2 ) cv2.imshow(capture, frame) if cv2.waitKey(1) 0xFF ord(q): break time.sleep(0.2) cap.release() cv2.destroyAllWindows() print(f采集完成共保存 {count} 张样本到 {save_dir})采集的时候保持摄像头稳定不要边走动边拍会导致人脸模糊。拍完检查一下保存的照片如果有模糊、过曝或者脸部被遮挡的直接删掉重拍。样本质量比数量重要20张清晰的比50张模糊的有效得多。3.3 第2步写后台识别与唤醒逻辑样本就绪后写核心脚本。这个脚本的逻辑是启动时加载所有样本的人脸编码然后循环读摄像头每帧做人脸定位和比对。如果识别到本人脸持续一段时间就模拟按键唤醒屏幕如果长时间识别不到任何人脸或识别出的人不是本人就保持当前状态不动。这样设计有一个好处就是不会因为一帧误判就执行锁屏或解锁动作避免不必要的闪烁。import face_recognition import cv2 import os import time import subprocess # 加载样本 known_dir rD:\hello_cam\dataset\owner known_encodings [] for fname in os.listdir(known_dir): img face_recognition.load_image_file(os.path.join(known_dir, fname)) encodings face_recognition.face_encodings(img) if encodings: known_encodings.append(encodings[0]) print(f已加载 {len(known_encodings)} 张人脸样本) # 开启摄像头 cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头打开失败) exit(1) last_active_time time.time() WAKE_HOLD_SECONDS 1.0 # 识别到本人脸持续1秒后才唤醒 LOCK_TIMEOUT_SECONDS 10 # 超过10秒没看到本人触发锁屏实际锁屏交给动态锁 while True: ret, frame cap.read() if not ret: time.sleep(0.2) continue # 缩小画面提高识别速度 small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) rgb cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB) # 人脸定位 编码 locs face_recognition.face_locations(rgb, modelhog) encodings face_recognition.face_encodings(rgb, locs) is_owner False for encoding in encodings: matches face_recognition.compare_faces( known_encodings, encoding, tolerance0.5 ) if any(matches): is_owner True break now time.time() if is_owner: last_active_time now # 避免频繁触发仅当距离上次唤醒超过一定时间才发按键 # 这里假设上次唤醒操作后有一个全局变量记录实际可简化为每秒最多触发一次 else: # 可选超过阈值自动锁定 # if now - last_active_time LOCK_TIMEOUT_SECONDS: # subprocess.run([rundll32.exe, user32.dll,LockWorkStation]) # last_active_time now pass time.sleep(0.1)上面这段代码我故意预留了逻辑位置没有直接灌满因为唤醒按键怎么发、多久发一次要根据你的实际Windows设置来调。我实测下来最稳的唤醒方式是用PowerShell调用WScript.Shell发空格键前提是屏幕熄灭后系统处于“需要登录但不强制密码”的状态def wake_screen(): subprocess.run( [ powershell, -Command, $wshell New-Object -ComObject wscript.shell; $wshell.SendKeys( ) ], timeout5 )注意我前面提到的Secure Desktop问题——如果系统彻底锁屏在登录界面这个SendKeys是没有用的。所以必须配合修改电源选项把“唤醒时需要登录”设为“从不”。具体路径是设置-系统-电源和睡眠-其他电源设置-选择电源按钮的功能-更改当前不可用的设置-取消勾选“启用快速启动推荐”如果它导致唤醒异常-在“唤醒时需要密码”里选择“不需要密码”。不同版本的Windows路径略有差异但核心就是让屏幕熄灭后不进入必须输密码的锁屏态而是进入一个“按任意键就亮屏”的待机态。这是自制方案能用脚本唤醒的前提。如果你不想用face_recognition用OpenCV的LBPH人脸识别器做替代核心逻辑是一样的只是把dlib那套换成了cv2.face.LBPHFaceRecognizer_create()训练和预测的接口都更简单但准确率和鲁棒性会差一些对环境光线更敏感。3.4 第3步接上Windows动态锁做离开锁定识别唤醒这块搞定之后再说离开锁屏。Windows有一个被很多人忽略的原生功能叫“动态锁”原理是检测配对手机的蓝牙信号手机离开一定距离后自动锁定电脑。这个功能恰恰能弥补自制方案里“识别不到人脸时主动锁屏”的短板——因为摄像头经常有盲区人可能走到摄像头看不到的地方但人还在电脑附近这时候识别脚本会误判为“人不在”如果让脚本主动锁屏就会很烦。用蓝牙动态锁判断的是人的实际物理距离比摄像头更可靠。设置步骤很简单手机和电脑配对蓝牙确保电脑蓝牙开着且已配对成功打开设置-账户-登录选项找到“动态锁”区域勾选“允许Windows在你离开时自动锁定设备”系统会提示“当你带着配对的设备离开时Windows会自动锁定”实测下来动态锁的判定距离大概是10米左右穿一堵墙会触发锁定。这个距离对于家庭和办公室场景都算合理。如果觉得太灵敏或者太迟钝可以在设备管理器的蓝牙设备属性里调整“信号强度阈值”这个参数改变蓝牙信号衰减到多少时触发动作。动态锁的细微之处在于它只在“锁屏策略允许”的情况下生效。如果系统被设置为“从不”要求重新登录动态锁虽然会锁定会话但唤醒时因为免密设置依然直接进入桌面这就形成了一种“锁了又没完全锁”的状态。这里需要你在体验和安全之间做个取舍如果你希望人走开后再回来时需要身份验证那就要把唤醒密码设为需要但这样又会导致自制脚本的SendKeys无法穿透Secure Desktop自动解锁。我目前的取舍是在家用电脑上选择“唤醒免密动态锁”追求体验如果哪天需要更严格的安全策略直接去买一个Windows Hello认证摄像头这是最终解。4. 常见问题与排查实录4.1 原生Windows Hello报错“抱歉出现问题”怎么办做这个项目的过程中很多朋友找我说还没开始自制呢系统自带的Windows Hello突然罢工了一直弹“抱歉出现问题请关闭Windows Hello然后尝试”。这个问题我在折腾摄像头驱动时也遇到过原因是Windows Biometric Service生物识别服务状态异常或者系统里残留了多个摄像头驱动组件。遇到这个报错按下面的顺序排查成功率很高。先看服务状态。按WinR输入services.msc找到“Windows Biometric Service”确认它的状态是“正在运行”启动类型是“自动”。如果服务停了手动启动或者用管理员权限运行命令行sc query WbioSrvc sc start WbioSrvc如果服务启动失败多半是驱动层有问题。到设备管理器里找到摄像头设备卸载设备然后“扫描检测硬件改动”让系统重新装驱动。这一步能解决大部分驱动残留导致的报错。驱动没问题的话再查两个不太容易被注意到的地方。一是组策略在gpedit.msc里找到“计算机配置-管理模板-Windows组件-生物识别”确保“允许使用生物识别”设置为“已启用”而且“允许用户使用生物识别登录”没有被禁用。二是TPM状态WinR输入tpm.msc确认TPM已就绪且状态正常。有些老机器TPM默认关闭需要在BIOS里打开Windows Hello人脸识别依赖TPM生成密钥TPM没就绪也会报这个错。最后还有一个大招启用内置管理员账户来重置生物识别数据net user administrator /active:yes切换到管理员账户打开设置-账户-登录选项把所有Windows Hello方法全部删除重新设置一遍设置完再切回原账户。这一套组合拳下来大多数“抱歉出现问题”都能解决。顺带说一句如果你在折腾自制方案时误装了某些摄像头虚拟驱动也可能导致原生Windows Hello失效。我建议自制方案和原生Windows Hello不要同时启用二者争抢摄像头时会互相干扰。4.2 自制脚本常见故障脚本运行时遇到的问题主要集中在摄像头取流、人脸识别误判、唤醒无效三个地方。摄像头取流失败最常见的原因是摄像头被其他程序占用比如微信、浏览器会议、自带相机应用这些都会独占摄像头。运行脚本前把这类程序全部关掉。如果确认没有占用但依然报错把脚本里的VideoCapture(0)改成VideoCapture(1)试试有些笔记本内置摄像头和USB摄像头的索引顺序不一样。人脸识别误判的典型表现是本人坐在电脑前却一直不触发唤醒或者偶尔把家人识别成自己。这时要调节compare_faces的tolerance参数这个参数是容差度值越小判定越严格。默认0.6我实测用0.45~0.5比较均衡。太严格会导致真人偶尔翻车太宽松会增加误判风险。另外可以增加样本数量多采集几个角度的照片指数级提升鲁棒性。唤醒无效的问题我在3.3节提过几乎都是因为系统设置了“唤醒时需要密码”导致按键事件被Secure Desktop拦截。还有一个小坑是“快速启动”被启用时电脑从睡眠状态唤醒的路径和从屏幕熄灭唤醒不一样快速启动有时会把亮屏动作变成一次冷启动导致各种状态异常。建议把“启用快速启动”关掉实测对脚本的稳定性有正向帮助。关闭路径控制面板-电源选项-选择电源按钮的功能-更改当前不可用的设置-取消勾选“启用快速启动”。4.3 安全性和隐私保护的注意事项这部分是我个人认为自制方案里最重要的很多网上教程都不提。普通摄像头做的人脸解锁安全级别确实低照片攻击就能绕过。face_recognition库里没有内置活体检测能力网上有一些基于“眨眼检测”“头部姿态估计”的改进方案可以在一定程度上提高安全性但依然无法和红外活体检测相比。所以我强烈建议这个方案只放在家里别用在公司电脑上更别用来保护包含重要密码、网银、私密文档的机器。如果电脑被人物理接触对方拿一张你的高清照片往摄像头前一放系统就解锁了这是方案本身的局限性不是代码能解决的。隐私方面样本照片是明文的JPG文件直接躺在硬盘目录里。如果电脑丢了样本照片就泄露了。我建议把样本目录放在BitLocker加密的盘符里并且不要同步到任何云盘。人脸数据属于生物识别信息泄露之后无法像密码一样修改处理时要谨慎。更好的做法是识别完成之后把原始照片删除只保留128维的人脸特征向量文件。向量文件理论上无法还原成人脸图像安全性会好很多。这个改造也不难把3.2节的训练逻辑改成直接保存face_encodings的结果到本地文件即可。5. 实测体验和后续扩展思路整套方案在我自己电脑上跑了大概两周最终的效果是坐在电脑前屏幕熄灭状态下大约2秒钟内能触发唤醒亮屏手机带着离开电脑约10米系统会在30秒内自动锁屏回来后再次坐下又自动亮屏。操作全程不需要碰键盘和鼠标“人在电脑前就可用人不在就锁”这个体验基本达成。和原生Windows Hello相比少了“刷脸动画”那种仪式感识别速度也慢一些但考虑到一分钱没花这个效果我很满意。这段期间踩的坑也算是宝贵的经验总结成几条给想动手的朋友先做减法再做加法。第一条坑就是不要一上来就追求“脚本自动输PIN解锁”这类高级功能先把“亮屏”和“锁屏”这两个基础动作打通体验就已经很好了摄像头的位置很关键放在显示器上沿正对脸部的位置识别率能高出一大截放在侧面会导致经常识别不到最后你可能以为是代码问题其实是角度问题人脸识别脚本要设成开机自启不然每次用完电脑还得手动运行一次脚本体验就断了。自启方式用“任务计划程序”是最稳的比放启动文件夹更可靠可以设置为“用户登录时运行”并且勾选“不管用户是否登录都要运行”这样脚本会以更高权限跑在后台这个项目后续还有不少可以扩展的地方。比如加入活体检测最简单的做法是在识别到人脸之后要求用户做一个头部的左右转动动作OpenCV里有现成的人脸特征点可以算头部偏转角度实现也不难。再比如支持多用户每个家庭成员注册自己的样本脚本里做一个用户映射识别到谁就唤醒谁的桌面。还有更进阶的方向把识别结果通过HTTP API广播出去和智能家居联动实现“进家门自动亮灯、开电脑”之类的场景。这些扩展我之后应该会逐个折腾到时候再来分享。最后再说一个真实体会自制方案的本质是用软件组合去弥补硬件的缺失它能让你理解Windows Hello背后的一些设计细节能折腾出适合自己的交互体验但它终究不是一个安全等高替代品。如果你真的很依赖刷脸登录且预算允许花几百块买一个经过认证的Windows Hello摄像头才是最省心的选择。自制这件事的乐趣更多在于过程中搞清楚每一条链路“为什么是这样”而不仅仅是得到一个解锁工具。
返回列表