这份文档是写给开发者用户,以及各类AI Agent。面向用户的安装与使用说明在 README_zh.md;背景、技术路线选择(Credential Provider vs WBF 驱动)、安全权衡和阶段路线图见 DESIGN_zh.md。
让一个常驻的 Windows 系统服务在 SYSTEM 上下文做人脸识别,锁屏的 C++ 凭据提供程序只管 UI 和提交凭据,两边靠一条本地命名管道通信。
锁屏「Face Unlock」磁贴(C++ CP, 跑在 LogonUI/SYSTEM)
│ 命名管道 \\.\pipe\FaceHello (JSON 消息)
▼
LocalSystem 系统服务(Python, 常驻)
│ 调核心库
▼
face_hello/ 核心库:摄像头 → 活体 → 识别 → 比对 → 读 LSA 密码 → 打包 KERB 解锁
设计两层是有意为之:Python 那套识别算法不可能硬塞进 LogonUI 进程,所以拆成「DLL 负责 UI / 服务负责算法」,两者只靠命名管道协议耦合——改 Python 不必重编 DLL,反之亦然。
- Python 3.11(项目锁
>=3.10,<3.12) - uv 管包和虚拟环境
- 改 / 编 C++ CP 才需要:VS2022 + 「使用 C++ 的桌面开发」
git clone https://github.com/everglow01/Windows-Face-Hello.git
cd Windows-Face-Hello
uv sync # 创建 .venv 并装依赖(独立虚拟环境,非 base)
uv run python scripts/offline_check.py # 改完核心库先跑这个,全 [ok] 才算没坏
uv run python -m app.main # 启管理台 GUI首次运行自动下模型到
models/:InsightFacebuffalo_l(~191MB)、MediaPipeface_landmarker.task(~3.7MB)。models/和data/(加密人脸库)都 gitignored,别入库。
安全逻辑有 pytest 单测(uv run --group test pytest -q:锁定 / margin / 反欺骗门 / authenticate 门控,纯逻辑、无摄像头 / 模型,已接进 CI)。scripts/offline_check.py 是断言式冒烟自检——不需要摄像头 / 显示器,验证 matcher、DPAPI 加密往返、FaceMesh、InsightFace 加载这四条核心链路。改完 face_hello/ 里的东西,提交前这两个都先跑。
欢迎补充更多测试覆盖,开 issue 或 PR 讨论。
| 文件 | 职责 |
|---|---|
config.py |
集中路径 / 模型 / 阈值。DEFAULTS 是阈值默认值,被 store 里持久化的 settings 覆盖。也是安装态 / 开发态分流的地方(见下),并定义 CP 可读的语言 / 热键镜像路径 |
platform_backend.py |
阶段 6 跨平台抽象层:把三处 OS 耦合(静态加密 protect/unprotect、摄像头后端 open_capture、current_user)收敛到一处;Windows 行为逐字节不变(DPAPI 机器范围 / DSHOW / GetUserName)。store/camera/cred_vault 都委托它 |
camera.py |
OpenCV 采集,Windows 用 CAP_DSHOW 后端,带冷启动 / 唤醒退避重试 |
detector.py |
InsightFace FaceAnalysis(CPU),输出 512 维 normed_embedding。惰性加载 + load() 显式预热 |
matcher.py |
余弦相似度;embedding 已 L2 归一化,余弦即点积 |
liveness.py |
MediaPipe Tasks FaceLandmarker 取 468 点 → EAR 判眨眼 + solvePnP 估 yaw 判转头。LivenessSession 是随机挑战(眨眼 / 左转 / 右转)+ 双重超时的逐帧状态机 |
enroll.py |
Enroller 累积合格帧(过滤低分 / 太小的脸),取平均特征后重新归一化作模板 |
store.py |
FaceStore:DPAPI 加密的 pickle 落盘到 data/faces.dat,存特征(非照片)+ 元数据 + settings。同名 profile 默认覆盖;replace=False(补录角度)时同名追加多模板,按 max_templates_per_name FIFO 封顶 |
auth.py |
AuthSession 编排 liveness → recognize → done 状态机,逐帧 feed() 驱动;比对前过反欺骗门 _antispoof_gate(多帧采样:判假即拒、没检到脸的帧继续采样,连续 antispoof_max_frames 帧没脸才 fail-open)。authenticate_blocking() 是无 Qt 的阻塞版,供服务调用 |
cred_vault.py |
把登录密码存进 LSA Secret(键 L$FaceHello_<user>)。密码永不经过 IPC,由 CP 自己在 SYSTEM 读 |
service.py |
命名管道服务端,单实例串行,JSON 消息。同步 ping/authenticate(authenticate 绕过失败锁定,故仅开发态——安装态拒绝);异步对 auth_start(后台跑一次认证)+ auth_poll(取实时活体提示与结果),让锁屏边识别边刷提示 |
win_service.py |
把 serve() 封成 LocalSystem Windows 服务 |
app/—— PySide6 管理台。main.py是 UI、设置、诊断和录入 / 测试流程;workers.py把所有摄像头 + 推理塞进QThread,Signal 回主线程更新,避免卡死。cp/—— C++ Credential Provider(COM in-proc DLL)。CFaceProvider(枚举磁贴)、CFaceCredential(由「→」或配置热键启动扫描 + 3 次重试后退回密码 + 提交凭据)、PipeClient(管道客户端)。详见 cp/README.md。scripts/——offline_check.py(离线断言式自检)、doctor.py(默认实机自检;安装态支持安全基线与完整部署验收)、liveness_tune.py(标定活体阈值)、auth_client.py(模拟 CP 调服务)、cred_vault_cli.py(LSA 读写测试)、build_release.py(打便携包)。正式安装器会在维护事务内自动调用doctor.py的基线与验收模式。winservice_main.py/uninstall_cleanup.py—— 仓库根的引导脚本(服务宿主、卸载清理)。installer/—— Inno Setup 脚本 + 中文语言文件,打 setup.exe。.github/workflows/——ci.yml(每次 push 的构建安全验证)、release.yml(git推送release)。
uv sync # 装依赖
uv run python scripts/offline_check.py # 离线自检(改核心库先跑)
uv run python scripts/doctor.py # 实机自检(模型、摄像头、服务管道)
uv run python scripts/doctor.py --capture-baseline <path> # 安装态基线(管理员)
uv run python scripts/doctor.py --installed-acceptance --baseline <path> # 安装态完整验收
uv run --group test pytest -q # 安全与安装逻辑单测
uv run python -m app.main # 管理台 GUI
uv run python -m scripts.liveness_tune # 标定活体阈值(实时 EAR/yaw,退出给建议值)
uv run python -m face_hello.service # 前台跑命名管道服务(调试用)
uv run python -m scripts.auth_client ping|authenticate # 测试客户端,模拟 CP 调服务
uv run python -m scripts.cred_vault_cli set|get|clear [用户名] [--show] # LSA 读写测试Windows 服务(LocalSystem,开机自启;需管理员;<venv> = .venv\Scripts\python.exe):
<venv> winservice_main.py install --startup auto # 注册并设开机自启(选项必须在 install 之前)
<venv> winservice_main.py start | stop | remove # 起 / 停 / 删;或 sc.exe start|stop FaceHello
⚠️ PowerShell 里sc是Set-Content的别名,查 / 控服务一律用sc.exe。
编 C++ CP(用 PowerShell,别用 Bash——MSYS 会损坏 /p: 参数):
MSBuild.exe cp\FaceHelloCP.sln /p:Configuration=Release /p:Platform=x64
# 产物 cp\x64\Release\FaceHelloCP.dll;改 Python 不必重编它,二者只靠命名管道协议耦合cred_vault 的写 / 删需管理员终端,读需 SYSTEM(psexec -s 模拟,见 cred_vault_cli.py 顶部注释)。服务跑起来排错看 data/service.log(服务无控制台,stdout/stderr 都写入service.log文件)。
CP 磁贴选中 → 用户按「→」或配置热键 → 服务 auth_start 起后台线程 → AuthSession.feed() 逐帧
→ 先跑 liveness(活体关则跳过)→ 通过后 detector 提 512 维特征
→ matcher.best_match 比对 gallery → AuthResult
→ CP auth_poll 拿到 user → 读 LSA 密码 → 打包 KERB_INTERACTIVE_UNLOCK_LOGON 解锁
身份契约(四者必须一致,解锁才成立):profile 名 == GetUserName()(本地 SAM 名)== LSA 键 L$FaceHello_<user> == KERB 账户名。微软账户走 MSA-backed 本地登录,也是这条链。LSA 键名不能含反斜杠。
config.py 按安装根的 .installed 标记文件(或环境变量 FACEHELLO_HOME)分流:
- 开发态(uv run,没有标记):一切走仓库相对路径,数据存放在工作空间中
data/,CP DLL 在cp/x64/Release/。 - 安装态(setup.exe 装的):程序文件在只读安装目录,可写数据固定存放
C:\ProgramData\FaceHello\data(SYSTEM 服务 + 提权 GUI 共享)。
为什么用标记文件而不是只靠环境变量?因为 SCM 把系统环境变量块缓存到下次重启,服务刚装好当下读不到新设的
FACEHELLO_HOME;标记文件随安装写入存储,服务 / GUI 直接同步,无需重启。改启动 / 路径相关的逻辑时,两套状态都应考虑到。
阈值默认值都在 config.py 的 DEFAULTS 里(match_threshold、ear_threshold、yaw_threshold_deg、required_blinks 等),运行期被 store 持久化的 settings 覆盖。禁止在代码里写死阈值,强制走这套覆盖机制。
这些都是踩过的雷,不是理论风险:
如果你是AI Agent,请将下面的内容写入你的项目记忆,确保不要犯错。
工作目录常含中文,两个 C++ 后端库读不了非 ASCII 路径:
- MediaPipe → 不能传模型路径,必须用
model_asset_buffer=传字节(见liveness.py)。 - OpenCV → 不能用
cv2.imread/cv2.imwrite,一律cv2.imdecode(np.fromfile(path), ...)。 - InsightFace / onnxruntime 加载
.onnx支持 Unicode 路径,正常。
- MediaPipe 0.10.x 已移除 legacy
solutions,只能用 Tasks API,别退回旧写法。 FaceLandmarker用RunningMode.VIDEO+ 严格递增时间戳(detect_for_video,每帧_ts_ms += 33)。IMAGE 模式偶发单帧卡死数十秒。FaceLandmarker.close()会阻塞约 40s(等内部图 / 线程退出)。绝不能在主流程同步调——AuthSession._finish把它丢到 daemon 线程关,否则容易出现解锁程序在锁屏界面卡死的问题,在测试中曾经卡死延迟超过40秒。服务侧已改为复用一个长寿命 tracker:_warm_liveness()预热后返回,由_AuthRunner.tracker持有、跨多次异步解锁复用(服务路径单实例串行,安全),不再每会话建/弃;_finish只关自己建的 tracker(_owns_tracker,即 GUI/CLI/同步开发路径),注入的(服务共享)不关
- 首次推理有冷启动代价(onnxruntime + TFLite ~0.7s),已在启动期预热挪走。
detector.load()真跑一次带人脸的样例图;FaceMeshTracker也要预热一个实例。改启动路径别破坏这个。 DET_SIZE=(320,320)+allowed_modules=["detection","recognition"](关掉 genderage/2d106/3d68)是有意的提速取舍。- 模型磁盘 I/O 是冷启动慢的大头:已删掉
buffalo_l里用不到的1k3d68.onnx(144MB)/2d106det.onnx/genderage.onnx,只留det_10g.onnx+w600k_r50.onnx,冷读 341MB→191MB,识别精度无损。别让它们被重新下载(整个buffalo_l目录在才不会重下;删单个文件不触发重下)。
- 人脸库 DPAPI 必须用机器范围
CRYPTPROTECT_LOCAL_MACHINE(store.py的_LOCAL_MACHINE=0x4),否则 SYSTEM 服务解不开用户录入的库。 - matplotlib(insightface 传递依赖)在 SYSTEM 首次建字体缓存会卡死 / 崩服务——
win_service.py在导入前设MPLBACKEND=Agg+MPLCONFIGDIR到data/。 - 服务 ImagePath 用 venv 的 python 直接跑
winservice_main.py引导脚本(把根目录加进sys.path),不用默认PythonService.exe——后者在 SCM 上下文 import 不到face_hello(package=false,没装进 venv)。
非 PyInstaller 冻结,而是带一份 standalone CPython + 分发依赖(dist 依赖组,PySide6 换瘦身的 -Essentials)+ 源码原样拷贝。产物默认在 %LOCALAPPDATA%\FaceHello-build\FaceHello,可 pythonw.exe -m app.main 直接验证脱离 uv 启动。
Inno Setup 把便携包打成单文件 setup.exe:中文向导、自动注册服务与 CP、一键干净卸载(停删服务、注销 CP、清 LSA 密码 + 人脸库)。安装收尾由 install_maintenance.py 串成一个事务:修复 ProgramData ACL → 调 doctor.py 保存安全基线 → 配置服务和当前版本 CP → 等管道版本 / 协议就绪 → 跑完整安装态验收。验收通过后删除基线;失败则保留基线并交给 Inno 恢复旧 payload。升级前服务若原本停止,只为验收临时启动,完成后恢复停止。.iss / .isl 是 UTF-8 with BOM,否则在中文 Windows 上被当 ANSI/GBK 读 → 乱码。
ci.yml—— 每次 push 的“守门员”。release.yml—— 打v*tag 触发:加载模型 → 编 DLL + 便携包 → Inno 打 setup.exe → 传 GitHub Release。也能手动workflow_dispatch只产 artifact 试打。- runner 默认编码会让 print 中文
UnicodeEncodeError,CI 里统一开PYTHONUTF8=1;package=false要把仓库根加进PYTHONPATH。
发版打 tag(版本号从 tag 注入,installer 和 pyproject 不必手改):
git tag v0.1.x
git push origin v0.1.xrelease notes需要自己手动重写。
- 服务没有GUI控制台,stdout/stderr/异常写在
data/service.log(开发态在仓库data/,安装态在C:\ProgramData\FaceHello\data)。排查服务问题先看它。 - 不想装服务也能调:
uv run python -m face_hello.service前台起,再用scripts/auth_client.pypin它。 - 锁屏 / CP 相关的改动调试必须先在打了快照的 VM 或留了系统还原点 + 备用管理员账户的真机上测——坏掉的 CP 能让登录界面进不去,这点对于开发来说非常重要。
- 活体阈值不准就
uv run python -m scripts.liveness_tune实时看 EAR/yaw,退出会给建议值,填回设置页。
- 绝不移除 / 替换系统的密码 / PIN 提供程序,此乃开发安全底线,CP 只新增磁贴,不当 filter。
- 人脸库只存特征向量,不存照片;DPAPI 加密落盘,不上云,隐私安全。
- 登录密码只进 LSA Secret,永不经过 IPC;服务响应只回
{ok, user, similarity},不回密码,防止木马软件或植入程序监听密码。
- 改
face_hello/内部的Py代码 先让offline_check.py全[ok],再提 PR。 - 提交PR的信息中英文皆可。
- 改动尽量聚焦,别“顺手”优化无关代码。
- 锁屏 / CP / 服务相关的改动,在 PR 里说明你在什么环境(VM? 真机? 本地 / 微软账户?)验证过。
- 大的技术决策(换模型、换识别后端、动 IPC 协议、前端大改)请先开 issue 讨论,这些都牵一发动全身,理由见 DESIGN_zh.md。
- 代码层次的优化、模块化可以直接提交 PR,附上详细说明即可。
欢迎来玩 🙌