AI 全自动考勤原理与实现


0x00 前言

相信很多人都有上下班忘记考勤签到的问题,这里结合 Mediapipe 这个号称可以达到亚毫秒级的人脸识别开源工具,可以有效解决这个问题。

项目源码: https://github.com/lyy289065406/AI-auto-checkin

Demo-AI-Auto-Checkin

0x10 约束

下文虽然是以 DingDing 为例说明,但也支持同时存在以下限制条件的考勤 APP :

  1. 禁止在 ROOT 手机上启动考勤 APP
  2. 禁止在模拟器启动考勤 APP
  3. 禁止第三方 APP 拉起考勤 APP 服务
  4. 考勤 APP 内置了人脸活体检测

0x20 思路

0x21 版本一

最简单粗暴直接的方法,就是用一个支架把手机立起来,并且打开考勤 APP 的界面,然后写一个后台程序定时模拟点击打卡按钮。

但是这个方法不可取,因为考勤 APP 默认是无权限解锁手机屏幕的(起码非 ROOT 环境不行),换言之这个方案成立的必要条件是手机一直亮屏,从安全性考虑就已经不会采纳了。

0x22 版本二

由于我的工作手机是 Android 的,所以我在呼吸时就想到借助 ADB (Android Debug Bridge) 工具了。

因此思路转变为:

  1. PC 通过 ADB 连接手机
  2. PC 设置【上/下班打卡】两个定时任务脚本,脚本触发时:
    • 通过 ADB 指令解除手机的锁屏状态
    • 通过 ADB 指令模拟人工操作,启动考勤 APP 并打卡
    • 通过 ADB 指令通知手机锁屏

硬件接线参考下图:

由于需要 ADB 通讯和长时间待机,因此手机支架我采购了可充电式的

但是这个方法纯粹只是通过定时任务触发的,若触发时我不在工位上,手机被解锁,依然会有安全风险。

所以这里还欠缺一个关键条件:必须是【我】在工位上才能解锁手机!

0x23 最终方案

由于我不久前才做过一期视觉 AI,于是我又很自然地在喝水时想到了:利用 PC 的摄像头做人脸识别不就可以了吗?

所以最终整个方案的设计变成这样:

由于只需要识别【我】一个人,所以人脸识别服务完全可以部署在 PC 本地

0x30 关键实现

基于前面的方案设计和技术原理,必要的准备工作是:提前计算【我】本人的面部特征值、并写入特征库,需要识别时再取出来比对。

那具体应该怎么做呢?

上图已经完整地描述了人脸识别整个过程需要用到的技术点。

下面会跟着这些步骤解释一下我实现的过程,细节地方不做深入展开,后面有时间再做一个专题探讨一下。

这里不涉及活体检测

0x31 人脸检测(Face Detection)

人脸检测涵盖了关键点检测(关键点坐标一般称为地标),这里推荐用开源工具实现,如 MediaPipeOpenPoseMMPosedlib 等等都可以。

我用摄像头自拍的时候,把我的【正脸】和【侧脸】都一起拍进去检测了。

这是因为我的 PC 摄像头是斜对着我的,我写的这个 AI 又比较低智,没学过怎么从侧脸推理出正脸,对 AI 而言正脸和侧脸就是两个不同的人。

所以为了提高成功率,需要把两种角度的人脸都采样分析。

以正脸为例,这里可以得到我面部 6 个关键点的地标分别为:

[
    [642.6734161376953, 307.81837463378906],    # 右眼
    [730.0695037841797, 299.2723846435547],     # 左眼
    [698.189697265625, 358.39449405670166],     # 鼻尖
    [697.9909515380859, 398.7664604187012],     # 口中
    [583.0513000488281, 324.0900707244873],     # 右耳
    [772.1451568603516, 306.3517427444458]      # 左耳
]

但如果想更准确的话,例如希望戴着口罩也能准确识别到身份,那么在检测时可以尽可能地提取更多的关键点。

目前我使用 MediaPipe 的模型,一个人脸网格最多可以识别出 468 个关键点,包括眼睫毛、眼眶、瞳孔等等。

但是因为我在工位的时候很少戴口罩,所以最终只取了五官的地标。

0x32 人脸对齐(Face Alignment)

但是关键点的地标不能直接用于特征计算,因为人脸不可能每次都刚好完全在同一个角度、位置、距离对着摄像头,甚至不同型号的摄像头的因为分辨率不一样,也会造成同一个人每次检测到的关键点地标不一样。

为了解决这个问题,我们需要对关键点的地标做预处理。

简单来说,找一个标准脸(标准尺寸、五官位置标准的人脸图像),计算其关键点地标;之后采集的每一张人脸都以标准脸作为参考系,把关键点一一映射到标准脸上。

这个过程其实就是人脸对齐,目的是把人脸尽可能映射到同一参考系中,提高后续计算的精度

例如,以分辨率 512 x 512 的吴彦祖头像作为标准脸,那么先找到吴彦祖脸部关键点的地标:

[
    [204, 297],     # 右眼
    [324, 297],     # 左眼
    [260, 381],     # 鼻尖
    [260, 433],     # 口中
    [145, 312],     # 右耳
    [389, 312]      # 左耳
]

然后我希望长得和吴彦祖一样,就把自己的五官摆到吴彦祖五官对应的位置。

当然附近的位置同时要做平移、旋转、缩放,这个过程在数学上称为矩阵的仿射变换

变换后我的脸就变成这样:

0x33 特征提取(Face Feature)

人脸对齐之后,把地标做归一化处理(即映射坐标值范围到 [0, 1] 之间),然后输入神经网络进行 Embedding(把离散变量转为连续向量)处理,就可以得到一个 n 维的特征值,这个特征值就可以代表一张脸。

例如我的正脸特征值是:[ 0.20714756965086467 ... 0.589757012077309 ]

特征值的计算方法目前一般是闭源的,常见的特征值一般是 1 x 128 的矩阵

0x34 人脸匹配(Face Compare)

有了特征值,往后的事情就比较简单了:

  • 每当到达打卡时间点时,激活 PC 摄像头、并采集最接近画面的人脸
  • 如果该人脸的特征值,与特征库中【我】的特征值相似度较高,则用 ADB 解锁手机、启动考勤 APP 打卡
  • 否则取消该轮打卡动作

而判断两张脸是否为同一人,常用的算法是通过 欧氏距离/余弦距离 计算特征值的相似度:相似度越高,则为同一人的置信度就越高。

0x35 ADB

ADB (Android Debug Bridge) 是 Android 调试的常用工具之一。

在这里主要是用 ADB 模拟手动操作,所以不需要 ROOT 手机

要实现模拟考勤 APP 打卡,只需要组合 6 个 ADB 命令:

  • 唤醒屏幕: adb shell input keyevent 224
  • 滑动屏幕: adb shell input swipe <x1> <y1> <x2> <y2>
  • 输入锁屏密码: adb shell input text <password>
  • 返回首页: adb shell input keyevent 3
  • 点击屏幕某个坐标: adb shell input tap <x> <y>
  • 锁屏: adb shell input keyevent 223

根据实际手机的屏幕分辨率修改命令坐标,即可实现在任意 Android 中模拟手动在考勤 APP 打卡的流程。

查看手机坐标方法

坐标值可以截屏后查看:

  • 方法一 PS : 菜单 -> 窗口 -> 信息 -> -> 像素,鼠标悬浮在图片上后即可查看坐标
  • 方法二画图: 鼠标悬浮在图片上后,直接在左下角即可查看

0x40 成品展示

0x50 后记

我现在只要把手机放在支架上充电,全自动考勤机就能确保我每天至少打满了 8 小时的卡。

后来为了避免 AI 频繁打开摄像头确认我的身份,我还添加了一些前置约束条件进行优化:

  • 手机不在支架上时不触发
  • 电脑锁屏时不触发
  • 打了上班卡之后,未满 8 小时不触发下班卡
  • 已打满 8 小时考勤时不触发

0x60 参考文档


文章作者: EXP
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EXP !
 上一篇
Visual Studio 版本详解 Visual Studio 版本详解
0x10 版本关系因为微软的版本比较乱,所以要理清版本,首先需要区分 VS 和 VC 的含义: VS(Visual Studio): 针对多语言(C++、C#、F#、J#、Asp、Web 等)的 IDE 集成开发环境 VC(Visual
2022-11-25
下一篇 
解决 Windows 10 启动 Docker 时端口被占用问题 解决 Windows 10 启动 Docker 时端口被占用问题
背景由于 Windows 的 Docker 是依赖 Hyper-V 的,但在启用 Hyper-V 后,会产生各种端口莫名被占用的问题。 例如启动 Docker 时会报错(端口随机在一个小范围内波动的): Error starting use
2022-11-20
  目录