ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python人脸识别签到系统:从摄像头采集到特征匹配的完整实现

Python人脸识别签到系统:从摄像头采集到特征匹配的完整实现 简介基于Python与OpenCV、dlib、face_recognition构建的人脸识别签到系统资源面向Python开发者、AI入门者及有考勤签到需求的学生提供从人脸录入、特征提取到实时签到与记录导出的完整工程实现。资源共含20个文件压缩包大小约95KB6个py源码对应数据采集、通用处理、工具函数、摄像头调用等核心模块附pyc编译文件、xml配置文件、测试图片、npy特征文件与说明文档压缩包虽小但结构清晰适合学习与实践。已有3988人学习下载属于同类资源中较受关注的一份。具体内容包括create_dataset.py、camera_use.py、file_processing.py、util.py等脚本以及预生成的faceEmbedding.npy可快速跑通基于GUI的人脸签到流程并在此基础上理解人脸检测算法选型、特征向量比对、多线程实时视频处理与签到结果本地存储等关键技术点为二次开发或毕业设计提供可直接复用的代码基线。1. python人脸识别签到系统一个能直接跑通的本地摄像头签到方案人脸识别签到在Python里并不是只有大厂才能做的产品级方案用OpenCV做人脸检测、face_recognition提取特征向量、Tkinter搭GUI一套下来完全可以支撑办公室或小班课的日常签到需求。这套ggg.rar里的源码包正好是这么个结构里面有create_dataset.py建人脸库、camera_use.py开摄像头识别、file_processing.py做文件处理还预置了一个faceEmbedding.npy特征文件和common.py公共模块。我拆完整个包之后最直观的感受是它把采集人脸→训练特征→实时签到→记录下载这一条链路做得很完整适合拿来改一改就上生产而不是只有个识别demo就完事。适合谁用有Python基础、想快速落地一个人脸签到工具的人或者做课程设计、毕设需要一份完整可运行代码的学生。2. 整个人脸签到系统的模块划分从create_dataset到camera_use的完整闭环2.1 先看懂这份源码包的目录结构拿到ggg.rar解压之后第一件事不是急着跑而是把文件清单过一遍。我从包里整理出的核心文件如下文件作用create_dataset.py人脸数据采集脚本通过摄像头抓取人脸图像并保存Face_login/camera_use.py签到主程序打开摄像头实时识别并记录签到file_processing.py处理签到记录文件比如读取、写入、导出common.py公共工具模块通常放路径处理、常量定义等util.py辅助函数比如特征向量保存/加载faceEmbedding.npy存储所有人脸特征向量的numpy文件name.txt存放与特征向量对应的人名列表readme.md项目说明文档test_images/测试图片目录test/teset.py测试脚本这里最关键的是name.txt和faceEmbedding.npy这两个文件的配对关系。faceEmbedding.npy是一个numpy数组每一行对应一个人脸的特征向量name.txt里的每一行名字则与该向量按索引一一对应。也就是说第i行特征向量对应第i个名字这个映射关系一旦错位整个识别就会张冠李戴。我在看代码时特别注意了文件处理逻辑有没有做长度校验、有没有容错这决定了你新增人脸时会不会把整个库搞崩。2.2 create_dataset.py的工作机制一张张脸是怎么变成特征向量的create_dataset.py负责的是系统最前置的一步建立人脸特征库。它通过调用摄像头采集人脸图片然后用face_recognition库对检测到的人脸编码得到128维的特征向量最后把向量存进faceEmbedding.npy把名字写进name.txt。整体流程分三步走第一步调用摄像头获取视频帧第二步在帧里定位人脸区域并截取第三步把人脸图传给face_recognition.face_encodings函数生成特征向量。import face_recognition import cv2 import numpy as np from common import config_path def add_new_face(name: str, frame: np.ndarray): # 检测当前帧里的人脸位置 face_locations face_recognition.face_locations(frame) if len(face_locations) ! 1: return False, 需要且仅需要一张人脸出现在画面中 # 提取人脸特征向量默认是128维 face_encoding face_recognition.face_encodings(frame, face_locations)[0] # 加载已有特征库若文件不存在则新建 try: embeddings np.load(config_path(faceEmbedding.npy)) names open(config_path(name.txt), encodingutf-8).read().splitlines() except FileNotFoundError: embeddings np.empty((0, 128)) names [] # 检查是否与已有特征重复避免重复录入 if len(embeddings) 0: distances np.linalg.norm(embeddings - face_encoding, axis1) if np.min(distances) 0.45: return False, 该人脸已存在于特征库中 # 追加新特征并保存 embeddings np.vstack([embeddings, face_encoding.reshape(1, -1)]) names.append(name) np.save(config_path(faceEmbedding.npy), embeddings) with open(config_path(name.txt), w, encodingutf-8) as f: f.write(\n.join(names)) return True, 录入成功这段代码里有几个点需要重点说。face_encodings返回的向量是128个浮点数这是face_recognition基于dlib的预训练模型算出来的模型本身是在大规模人脸数据集上训练好的不需要自己训练这也是这个库对新手友好的根本原因。np.linalg.norm(embeddings - face_encoding, axis1)计算的是欧氏距离face_recognition官方文档建议以0.6作为阈值但实际场景里我会压缩到0.45到0.5之间因为0.6在摄像头角度变换大的时候容易把不同人判成同一个人。保存特征时为什么不直接存图片一个是体积问题一张128维的float数组只占512字节而一张jpg随便几十KB另一个是隐私问题特征向量理论上无法还原成人脸图像。2.3 核心识别逻辑欧氏距离阈值与容错设计人脸识别签到系统的主程序camera_use.py里最关键的一段就是实时帧的识别比对。它的做法很直接把摄像头当前帧里的人脸位置全部框出来然后对每个框提取特征向量和faceEmbedding.npy里的所有人算距离取最小的那个作为识别结果。如果最小的距离都超过了预设阈值就判定为陌生人。import face_recognition import cv2 import numpy as np def match_face(frame, embeddings, names, tolerance0.5): unknown_encodings face_recognition.face_encodings(frame) if not unknown_encodings: return None, None for unknown_encoding in unknown_encodings: distances np.linalg.norm(embeddings - unknown_encoding, axis1) min_index int(np.argmin(distances)) min_distance distances[min_index] if min_distance tolerance: return names[min_index], round(float(min_distance), 4) return 陌生人, None注意这段代码里的tolerance参数这就是整个系统最容易产生翻车体验的地方。tolerance设得太大比如0.65不同的人脸会很频繁地被互相误认设得太小比如0.35同一个人换个光线角度就有可能被拒之门外。我一般建议在0.45到0.55之间调先拿3到5个人做一轮测试统计同一人和不同人的距离分布再取两者的中间值作为阈值——这是一种很朴素但有效的校准方式。另外np.linalg.norm默认计算的是L2范数即欧氏距离你也可以换成face_recognition.compare_faces内置函数但那个函数内部同样是距离比较只是封装得更好读罢了。实时摄像头场景里每帧都做全库比对如果库里人很多超过500人特征矩阵在内存里会变得很大需要考虑用向量检索工具而这份源码包的规模下numpy直接算完全够用。2.4 GUI界面与签到记录Tkinter如何把识别结果变成一条可追溯的考勤数据签到系统没有界面就没有产品形态。这个项目用的是TkinterPython标准库自带的GUI框架优点是零依赖、打包发布时少背一个库的锅缺点是界面风格比较朴素。主窗口通常包含几个部分视频显示区用Label组件实时刷新帧、签到按钮、结果显示区、以及签到记录导出按钮。视频流由OpenCV的VideoCapture读取通过Tkinter的after方法周期性刷新画面这样才能让摄像头画面动起来。import tkinter as tk import cv2 from PIL import Image, ImageTk class SignInApp: def __init__(self, root): self.root root self.cap cv2.VideoCapture(0) self.video_label tk.Label(root) self.video_label.pack() self.record_text tk.Text(root, height8, width40) self.record_text.pack() self.update_frame() def update_frame(self): ret, frame self.cap.read() if ret: rgb_frame cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img Image.fromarray(rgb_frame) img_tk ImageTk.PhotoImage(img) self.video_label.config(imageimg_tk) self.video_label.image img_tk self.root.after(30, self.update_frame) # 约33fps def on_sign_in(self, name, timestamp): self.record_text.insert(tk.END, f{name} {timestamp}\n)这里有个容易被忽略的点self.video_label.image img_tk这一行不可少。Tkinter的Label如果只调用config(image...)而不保留引用图片会被垃圾回收机制回收掉画面会直接黑屏或者闪退——这是我跑类似GUI程序时踩过最典型的坑。root.after(30, self.update_frame)也就是每秒大约33帧对实时预览和识别来说是个合理的刷新间隔如果你CPU比较弱可以调整到50毫秒降低识别频率来换流畅度。签到的数据记录一般推荐直接用csv或者sqlite而不是用txt文件。csv的好处是Excel能直接打开sqlite的好处是并发读写和数据查询更可靠。3. 特征库的构建与管理让签到系统从认识一个人到认识所有人3.1 为什么用npy文件存特征而非数据库存图片这个问题的答案直接决定了整个系统的架构。包里用faceEmbedding.npy和name.txt两个文件就把人脸特征库管理起来了够用且简单。npy是numpy的二进制序列化格式加载速度非常快np.load一下整个矩阵就进内存了而如果存在sqlite或者mysql里每次识别都要查库再反序列化实时性就差很远。还有一点是图片的原始像素数据占空间一个特征向量压缩成人脸的关键信息后不仅体积小比对时算距离也快。但npy方案也有明显的边界它不支持增量写入的并发安全如果两个程序同时写这个文件文件会损坏它也不带索引当特征数量上万时全量距离计算会变慢。所以这个架构只适合中小规模几十到几百人这点一定要有清晰认知。3.2 批量建档的正确姿势采集多少张图才算够建档这个环节决定了整个签到系统到底好不好用。每录入一个新成员我一般会让他坐在摄像头前面缓慢转动头部系统自动采集10到20张不同角度的帧然后每张帧都做一次特征提取最后把这些特征向量求平均或者全部存进去。为什么要多角度采集因为face_recognition虽然是深度学习模型但小角度的侧脸、低头、仰头都会影响特征向量的稳定输出如果只采一张正脸照片实际签到时角度稍微偏一点距离就会飙升非常容易误判为陌生人。下面是典型的批量建档脚本写法import cv2, face_recognition, numpy as np, os def collect_dataset(person_name: str, save_dir: str, frames_to_collect: int 20): cap cv2.VideoCapture(0) collected [] while len(collected) frames_to_collected: ret, frame cap.read() if not ret: continue # 每帧都尝试提取特征检测到人脸才算有效 locs face_recognition.face_locations(frame) if len(locs) 1: encoding face_recognition.face_encodings(frame, locs)[0] collected.append(encoding) cv2.imwrite(os.path.join(save_dir, f{person_name}_{len(collected)}.jpg), frame) # 实时显示剩余采集数量 cv2.putText(frame, fCollected: {len(collected)}, (20, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.imshow(Collect, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() return np.array(collected)这段代码的采集思路是宁缺毋滥只有画面里恰好出现一张人脸时才采集避免多人乱入污染特征。if len(locs) 1这个条件是大多数人写采集脚本时最容易漏的如果你不限制背景里有路人经过时也会被当作数据采进去特征库就会混入杂音。采集完之后怎么处理这些向量简单的做法是把它们全丢进特征库里对同一个人的多个向量和名字做一一映射稍微讲究一点的做法是对同一组向量求平均得到一个更稳定的平均脸特征降低单帧噪声。我实践下来的经验是3到5张不同角度的特征融合效果足够好超过10张提升就非常有限了。3.3 特征库更新的坑直接在原npy上追加会毁掉整个数据库如果你试图用np.load加载一个npy文件、append一行又np.save保存回去版本不兼容或者维度不一致的问题会让你头皮发麻。一个特别常见的坑是刚初始化时faceEmbedding.npy还不存在代码第一次运行时np.load直接报FileNotFoundError或者特征库是空的0行128列但np.vstack的第一维是0拼接时不注意reshape就会得到(0,)的向量而不是(1,128)。所以保存新特征时务必确认形状是(1,128)再拼接我通常会在保存前打印一下矩阵shape做一个断言assert new_embedding.shape (1, 128), fshape mismatch: {new_embedding.shape}另外一个容易踩的问题就是编码一致性的问题。如果创建特征库时用的是GBK编码保存的name.txt而识别主程序用UTF-8读中文名字会变成乱码然后识别成功却显示一个乱码名签到记录完全没法用。整个项目里凡是涉及中文的地方编码格式必须全局统一我习惯在readme里直接写明所有文件一律UTF-8。4. 常见问题与避坑拿这套源码跑起来必看的排错手册4.1 摄像头打不开cap.read()永远返回False现象运行camera_use.py后界面能起来但视频区域是黑的或者提示摄像头初始化失败。 原因最常见是cv2.VideoCapture(0)的摄像头索引号不对。笔记本自带摄像头一般是0外接USB摄像头可能是1或者2还有可能是摄像头正被其他程序比如微信、腾讯会议占用OpenCV抢不到设备。 解决先用一个Python测试脚本暴力枚举索引0到2每个索引尝试打开并读取一帧如果索引都正确还是打不开关闭所有占用摄像头的软件再试。另外注意Linux下需要检查/dev/video*权限Windows下如果装了多个虚拟摄像头驱动也会有干扰。4.2 装了face_recognition却import失败现象import face_recognition直接抛ModuleNotFoundError或ImportError有时伴随dlib相关的报错。 原因face_recognition依赖于dlib而dlib需要CMake和C编译器才能从源码构建。Windows用户如果在Python 3.10以上直接pip install dlib大概率会卡在编译步骤因为官方wheel只发布到特定版本后面的版本需要自己编译。Linux用户同样需要apt install build-essential cmake来准备环境。 解决Windows下推荐先安装Visual Studio Build Tools勾选使用C的桌面开发然后pip install dlib成功后再pip install face_recognition或者用conda安装conda install -c conda-forge dlib省去编译痛苦。Python版本尽量用3.7到3.9踩坑最少。4.3 同一个人识别结果不稳定时而认出时而陌生人现象同一个同事坐在同一个位置上午签到正常下午阳光照进来就报陌生人人往左偏一点就识别失败。 原因光照和角度变化是face_recognition这类2D模型的通病。人脸特征是从图像像素里提取的光照变了像素值分布就变了纯侧脸时模型可能提取不到完整的面部关键点特征向量自然会漂移。 解决在签到场景架设固定的正面摄像头尽量避免强烈侧光控制人脸在画面中的大小录入时的脸部像素宽度和识别时保持相近我一般让脸宽占画面1/4到1/3左右实测把阈值从0.5放宽到0.55能吸收一部分波动。4.4 库里明明有人却频繁把A认成B现象两人同时出现在画面里系统把其中一人签成了另一人或者来回跳名字。 原因阈值过宽导致不同人的特征距离小于阈值误接受了错误匹配。也可能是建档时采到的样本质量差比如模糊、遮挡、光线暗导致特征向量本身代表性就不足。 解决调低阈值到0.4左右做一轮回归测试输出同一人和不同人的距离分布取两者之间的中位值做最终阈值同时把不合格的建档样本删除重新采集确保录入时正面、清晰、光线均匀。4.5 打包exe后识别和采集全部失灵现象代码在pycharm里跑好好的用PyInstaller打包成exe后摄像头也能开但识别永远不返回结果或者直接闪退。 原因face_recognition模型文件dlib的shape_predictor_5_face_landmarks.dat、dlib_face_recognition_resnet_model_v1.dat没有被打包进exe资源目录。PyInstaller默认只打包py文件dat文件经常漏掉运行时模型加载失败特征提取就会静默出错。 解决在spec文件里用datas参数把模型目录加进去并且在代码里改用相对资源路径比如sys._MEIPASS方式定位模型。具体做法我后面展开这里先记住核心是模型文件必须和exe一起带上。5. 进阶从单个摄像头到多人同时签到和迟到识别签到系统的价值不在能识别一个人而在多人同时经过时一个不漏时间戳准确记录。原始源码在处理多人时用的是循环遍历人脸多于两个时容易出现漏检。我的做法是多线程加队列主线程负责读帧识别线程负责对帧做人脸检测和特征比对然后把结果推入队列GUI线程只管消费结果渲染这样识别耗时不会阻塞画面刷新。import threading, queue, face_recognition, cv2, numpy as np frame_queue queue.Queue(maxsize2) result_queue queue.Queue() def recognition_worker(embeddings, names, tolerance0.5): while True: frame frame_queue.get() # 缩小尺寸加速检测feature匹配用原图精度 small_frame cv2.resize(frame, (0, 0), fx0.5, fy0.5) rgb_small cv2.cvtColor(small_frame, cv2.COLOR_BGR2RGB) locs face_recognition.face_locations(rgb_small, modelhog) encodings face_recognition.face_encodings(frame, locs) results [] for encoding in encodings: dists np.linalg.norm(embeddings - encoding, axis1) idx int(np.argmin(dists)) if dists[idx] tolerance: results.append((names[idx], round(float(dists[idx]), 4))) else: results.append((陌生人, round(float(dists[idx]), 4))) result_queue.put(results) def capture_loop(cap): while True: ret, frame cap.read() if ret and not frame_queue.full(): frame_queue.put(frame)这里有几个细节值得展开。face_locations用modelhog而不是cnn在CPU上HOG的速度快很多准确率对正面人脸足够face_encodings我仍然传原图frame而不是缩小图因为缩放会影响关键点定位精度。frame_queue设置了maxsize2如果识别线程来不及处理就直接丢旧帧保证画面永远是新的而不是越积越久越来越卡。多人签到还有一个边界问题同一个人站在摄像头前超过5秒系统会不会重复签到我一般会维护一个已签到名单的字典记录签到时间后10分钟内不重复记录这样既防止重复又能保留迟到判断的逻辑。验证整个系统是否达标其实有一套标准的做法录20个人的人脸库让这20个人每人分别签到5次统计识别率和平均响应延迟再找5个不在库里的陌生人来测试误识别率。我自己的验收线是识别率95%以上误识率低于1%单人识别延迟低于500毫秒。达不到这个指标先查光照均匀度再调阈值最后才怀疑模型本身——大多数情况下都是现场环境的问题。关于exe打包我再补充一个整体流程先把模型文件复制到项目的models/目录然后修改代码里的路径引用为os.path.join(getattr(sys, _MEIPASS, os.path.dirname(__file__)), models/xxx.dat)最后在spec文件中的datas里写[(models/, models)]。这样打出来的exe即使拷到没有Python环境的机器上也能运行。从那以后我每次搭建人脸签到系统都会强制先把建档、识别、导出三件事跑一遍验收流程再交付以防交付后一天到晚被叫去看下怎么回事。希望帮到你。本文还有配套的精品资源点击获取
返回列表