
简介基于PythonDjangoOpenCV、dlib的员工人脸识别考勤系统完整实现面向计算机相关专业毕业生、课程设计学生及人脸识别入门开发者适合用于毕设、课设或项目初期演示。系统涵盖源码、数据集与详细文档能帮助理解人脸检测、特征提取、考勤记录与Django后端展示的完整流程。压缩包共包含659个文件以Python源文件为主另有预训练模型pth/dat、依赖组件pyd/dll/exe、配置脚本bat/cfg以及说明文档docx/md/txt等整体约196MB目录结构清晰。目前已有251人学习下载适合需要参考完整项目或在此基础上扩展功能的开发者。代码已经测试运行成功并附有环境配置脚本与依赖文件能显著降低环境搭建门槛配套文档可辅助毕业设计撰写与功能二次开发。1. 这套员工考勤系统为什么值得你拆开看先给结论这不是那种只有登录注册加增删改查的“水货毕设”而是一条从摄像头采集、人脸检测、特征提取、比对识别到 Django 后台考勤记录落库的完整链路。它把 OpenCV 的图像处理能力、dlib 的 68 点人脸关键点模型和 Django 的 ORM 管理端串在了一起意味着你拿到的不是孤立的算法 Demo而是一套能跑通“刷脸打卡 - 生成考勤记录 - 后台查询导出”的业务系统。适合三类人来拆一是正在做 Python 毕业设计或课程设计、需要快速搭起一个既有技术含量又有业务闭环项目的学生二是想了解 OpenCV 和 dlib 在真实工程里怎么配合、而不是只在 Jupyter 里玩图片的开发者三是想给现有 Django 项目接入人脸识别模块、但不想从零调 dlib 的工程师。资源包里自带数据集和详细文档特征文件 feature_all.csv 已经生成好你可以直接复现识别流程也可以用自己的员工照片重新训练。接下来我会按“环境搭建 - 识别链路 - 考勤逻辑 - 部署和调优”的顺序把这套系统的设计思路和坑全部摊开。2. 环境准备与 dlib、OpenCV 的安装陷阱2.1 虚拟环境结构说明资源解压后你会看到 activate.bat、deactivate.bat、pyvenv.cfg 和 sysconfig.cfg 这几个文件这代表项目作者用的是 Python 自带的 venv 虚拟环境。activate.bat 是 Windows 下激活虚拟环境的脚本deactivate.bat 用于退出pyvenv.cfg 记录了虚拟环境对应的基础 Python 路径sysconfig.cfg 则是 Python 的配置文件。直接双击 activate.bat 后命令行前缀会出现(venv)说明你已经进入了项目隔离环境后续安装的包不会污染全局 Python。cd 项目目录 .\activate.bat python --version pip list注意activate.bat 前面要加.\因为 Windows PowerShell 默认不允许直接执行当前目录的脚本。如果执行报错提示“禁止运行脚本”需要以管理员身份打开 PowerShell 执行Set-ExecutionPolicy RemoteSigned再重新激活。另外不建议用deactivate.bat去退环境直接关闭命令行窗口更干净。2.2 dlib 安装失败的三种解法dlib 是这套系统里最折腾的依赖它依赖 CMake 和 C 编译器pip 直接安装经常报错。最常见的问题是ModuleNotFoundError: No module named dlib以及编译时报CMake must be configured。Windows 下建议优先尝试 pip 直接安装pip install dlib如果因为 Python 版本太新比如 3.12导致找不到对应 wheel就换两个思路。第一是降级 Python 到 3.8 或 3.9这两个版本能直接命中 dlib 的预编译包第二是用pip install dlib19.24.0指定版本这个版本兼容性最好。要是仍然卡在编译阶段检查 CMake 和 Visual Studio Build Tools 是否安装。装 Build Tools 时勾选“使用 C 的桌面开发”工作负载。Linux 下相对简单先装系统依赖再 pip 安装即可sudo apt-get update sudo apt-get install build-essential cmake libx11-dev libopenblas-dev liblapack-dev pip install dlib2.3 OpenCV 与 Django 版本的选择逻辑OpenCV 推荐安装 opencv-python 4.5.x 系列4.5.2 之前的部分版本不支持 Code128 解码但人脸识别场景用不到条码识别所以不必纠结具体小版本。需要注意的坑是opencv-python 和 opencv-contrib-python 不要同时装否则会出现AttributeError: module cv2 has no attribute face之类的奇怪报错。Django 版本建议 3.x 或 4.x 都行只要和 Python 版本匹配。我用的是 Python 3.9 搭配 Django 3.2LTS 版本省心。装完所有依赖后用一段简单代码验证三个核心库是否正常import cv2 import dlib import django print(OpenCV version:, cv2.__version__) print(dlib version:, dlib.__version__) print(Django version:, django.get_version()) detector dlib.get_frontal_face_detector() print(Face detector loaded:, detector)这段代码的作用是逐库验证导入是否成功。dlib.get_frontal_face_detector()会加载 dlib 自带的 HOG 人脸检测器这也是整个系统人脸检测的基础。如果执行到这一行不报错说明 dlib 的 C 扩展能和 Python 正常交互。到这里环境就绪下一章拆解核心识别链路。3. 人脸识别链路拆解从 OpenCV 采集到 dlib 特征比对3.1 摄像头调用与帧处理的最小实现这套系统的人脸数据来源是 OpenCV 的 VideoCapture。摄像头调用本身并不复杂但帧率控制、灰度转换和检测时机的选择会影响识别准确率。看下面这段简化后的核心代码import cv2 import dlib import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) face_rec dlib.face_recognition_model_v1(dlib_face_recognition_resnet_model_v1.dat) cap cv2.VideoCapture(0) if not cap.isOpened(): raise IOError(Cannot open camera) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 1) for face in faces: shape predictor(gray, face) face_descriptor face_rec.compute_face_descriptor(frame, shape) embedding np.array(face_descriptor) # embedding 是一个 128 维特征向量用于后续比对 print(embedding[:5]) cv2.imshow(Face Recognition, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码完成了从摄像头读取一帧图像到检测人脸、定位关键点、计算 128 维特征向量的完整链路。detector(gray, 1)中的第二个参数 1 表示图像金字塔的上采样次数数值越大越能检测出小尺寸人脸但耗时也会成倍增加。cv2.cvtColor把 BGR 转为灰度是因为 dlib 的 HOG 检测器本质上是基于灰度纹理特征转灰度后检测速度更快且识别率几乎不变。predictor负责定位面部 68 个关键点而face_rec.compute_face_descriptor接受的是彩色图 frame 和关键点 shape输出一个 128 维的浮点数组这个数组就是后续比对的“人脸指纹”。3.2 feature_all.csv 的结构与特征存储原理资源包里的 feature_all.csv 是整个识别系统的索引文件。它的格式是每一行代表一张人脸照片第一列是员工姓名或工号后面 128 列是该人脸对应的特征向量数值。之所以要用 CSV 而不是数据库表来存是因为 dlib 计算出的特征向量本质上就是一组浮点数CSV 读写方便且不会引入额外的 ORM 开销。下面是一段典型的读取与比对代码import csv import numpy as np def load_face_database(csv_path): known_names [] known_embeddings [] with open(csv_path, r, encodingutf-8) as f: reader csv.reader(f) for row in reader: name row[0] embedding np.array(row[1:], dtypenp.float64) known_names.append(name) known_embeddings.append(embedding) return known_names, known_embeddings known_names, known_embeddings load_face_database(feature_all.csv) print(fLoaded {len(known_names)} faces)读取 CSV 时要注意编码问题如果调试发现名字乱码将encodingutf-8替换成encodinggbk。特征比对用的是欧氏距离计算待识别的人脸特征向量与数据库中每个已知向量的距离距离越小说明两张脸越像。通常 dlib 官方推荐的判定阈值是 0.6也就是欧氏距离小于 0.6 判定为同一个人大于 0.6 则拒绝。但实际工程项目里阈值需要根据现场采集的图片质量调整光线稳定、正脸居中的场景可以收紧到 0.5多角度、暗光环境则要放宽到 0.65 甚至 0.7。3.3 识别流程的工程化封装如果把 3.1 和 3.2 的代码直接铺在 Django 视图函数里代码会非常冗余且难以维护。常见做法是单独建一个face_service.py把摄像头初始化、人脸检测、特征提取、数据库比对封装成一个类class FaceRecognizer: def __init__(self, csv_path, threshold0.6): self.detector dlib.get_frontal_face_detector() self.predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) self.face_rec dlib.face_recognition_model_v1(dlib_face_recognition_resnet_model_v1.dat) self.threshold threshold self.names, self.embeddings load_face_database(csv_path) def recognize(self, frame): gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces self.detector(gray, 1) results [] for face in faces: shape self.predictor(gray, face) desc np.array(self.face_rec.compute_face_descriptor(frame, shape)) distances np.linalg.norm(self.embeddings - desc, axis1) min_idx np.argmin(distances) min_dist distances[min_idx] if min_dist self.threshold: results.append((self.names[min_idx], min_dist)) else: results.append((unknown, min_dist)) return resultsnp.linalg.norm(self.embeddings - desc, axis1)一次性计算待识别向量与所有已知向量的欧氏距离比 for 循环逐个计算快一个数量级。这里把阈值作为构造参数传入方便在不同环境里调参。注意类实例化时会加载两个 dlib 模型文件和 CSV如果放在 Django 视图里每次请求都实例化模型加载耗时会导致接口响应非常慢正确做法是模块级单例或者直接在 Django app 启动时加载到内存。3.4 模型文件与数据集的对应关系资源包里的数据集与模型文件是配套使用的。shape_predictor_68_face_landmarks.dat 是 dlib 官方预训练的 68 点人脸关键点检测模型约 99MBdlib_face_recognition_resnet_model_v1.dat 是 ResNet 结构的 128 维人脸特征提取模型约 21MB。这两个文件不需要自己训练直接下载后放到项目根目录或 models 子目录下。生成 feature_all.csv 的过程是把数据集里的员工照片逐张读入检测人脸后计算特征向量并写入 CSV。数据集里同一员工的照片建议至少 3 张每张都要检测到人脸否则生成的 CSV 会缺行。还有个容易忽略的点CSV 中同一员工的多张照片特征可以取平均值再存这样比对时更稳定from collections import defaultdict def build_average_database(feature_csv_path, output_csv_path): groups defaultdict(list) with open(feature_csv_path, r, encodingutf-8) as f: reader csv.reader(f) for row in reader: groups[row[0]].append(np.array(row[1:], dtypenp.float64)) with open(output_csv_path, w, encodingutf-8, newline) as f: writer csv.writer(f) for name, vectors in groups.items(): avg np.mean(vectors, axis0) writer.writerow([name] avg.tolist())这样处理后每个员工在 CSV 里只剩一行比对时减少距离计算次数同时平均特征能抵消光照和角度的随机偏差。到这里识别链路的代码完全跑通了下一章看 Django 如何承接识别结果并完成考勤记录。4. Django 考勤业务闭环识别结果如何变成考勤记录4.1 数据模型设计考勤系统至少要包含员工表、考勤记录表和识别日志表。Django 的 ORM 模型定义如下from django.db import models from django.utils import timezone class Employee(models.Model): emp_id models.CharField(max_length20, uniqueTrue, verbose_name工号) name models.CharField(max_length50, verbose_name姓名) department models.CharField(max_length100, blankTrue, verbose_name部门) face_image models.ImageField(upload_tofaces/, blankTrue, verbose_name人脸照片) created_at models.DateTimeField(auto_now_addTrue) def __str__(self): return f{self.emp_id} - {self.name} class AttendanceRecord(models.Model): ATTENDANCE_TYPE ( (checkin, 上班打卡), (checkout, 下班打卡), ) employee models.ForeignKey(Employee, on_deletemodels.CASCADE, verbose_name员工) timestamp models.DateTimeField(defaulttimezone.now, verbose_name打卡时间) record_type models.CharField(max_length10, choicesATTENDANCE_TYPE, verbose_name打卡类型) confidence models.FloatField(default0.0, verbose_name识别距离) photo_path models.CharField(max_length255, blankTrue, verbose_name抓拍照片路径) class Meta: ordering [-timestamp]三个字段值得说明。confidence存的是欧氏距离即上一章识别结果中的min_dist这个值用于事后排查误识别photo_path存抓拍的照片路径用于管理员人工复核emp_id用 unique 约束防止工号重复。考勤记录不做每日唯一约束而是让同一员工上下班各产生一条记录判断逻辑放在视图层而不是数据库层。4.2 识别结果接入 Django 视图当摄像头识别到人脸并返回员工姓名后Django 视图需要把这个结果写入 AttendanceRecord。一个通用的 POST 接口设计如下import json from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import Employee, AttendanceRecord csrf_exempt def attendance_checkin(request): if request.method POST: try: data json.loads(request.body) emp_id data.get(emp_id) distance data.get(distance, 0.0) employee Employee.objects.get(emp_idemp_id) record AttendanceRecord.objects.create( employeeemployee, record_typecheckin, confidencedistance ) return JsonResponse({ status: success, name: employee.name, time: record.timestamp.strftime(%Y-%m-%d %H:%M:%S) }) except Employee.DoesNotExist: return JsonResponse({status: error, message: 员工不存在}, status404) return JsonResponse({status: error, message: 仅支持 POST 请求}, status405)csrf_exempt是开发阶段为了方便摄像头客户端直接 POST 而加的装饰器。正式部署时如果客户端是内部网络的可信设备可以保留如果客户端会暴露到公网建议去掉并改用 Token 认证。视图逻辑里没有做“同一员工一分钟内重复打卡”的限制这是有意为之——在设计考勤系统时是否允许反复打卡取决于业务规则。如果要求一天只能打一次卡要做查询判断from django.utils import timezone from datetime import timedelta today_start timezone.localdate() existing AttendanceRecord.objects.filter( employeeemployee, record_typecheckin, timestamp__datetoday_start ).exists() if existing: return JsonResponse({status: error, message: 今日已打卡}, status400)timestamp__datetoday_start是 Django ORM 的日期范围查询写法会在 SQL 层面对 timestamp 字段做日期过滤而不是把全表数据拉到 Python 里比较性能更好。4.3 考勤打卡的策略模式实际考勤场景比“按钮打卡”复杂。有些公司要求早上 9 点前打卡下午 6 点后打卡有些公司允许弹性工作制。把这些规则抽象出来不要写死在视图函数里。常见做法是维护一个打卡时段配置表或者在视图层用时间判断from datetime import datetime def determine_check_type(): now datetime.now() hour, minute now.hour, now.minute if hour 12: return checkin else: return checkout这个判断逻辑非常粗糙但适合作为毕设项目的起点。更精细的做法是把上下班时间配置到 Django 的 settings 里CHECKIN_START 08:30 CHECKIN_END 09:30 CHECKOUT_START 17:30 CHECKOUT_END 19:00然后写一个工具函数判断当前时间落在哪个区间不在任何区间就返回“非打卡时段”。这里的核心思想是考勤业务规则是变化的代码里只留策略接口具体规则由配置驱动。你在自己的项目里可以直接把这个时间判断替换成读取数据库配置表这样管理员可以在后台修改规则而不用改代码。4.4 Django Admin 后台的人脸数据管理Django 自带的 admin 后台可以管理员工和考勤记录但默认界面比较简陋。想快速上手并让演示效果好看注册模型后设置 list_display 和 list_filterfrom django.contrib import admin from .models import Employee, AttendanceRecord admin.register(Employee) class EmployeeAdmin(admin.ModelAdmin): list_display (emp_id, name, department, created_at) search_fields (emp_id, name) list_filter (department,) admin.register(AttendanceRecord) class AttendanceRecordAdmin(admin.ModelAdmin): list_display (employee, record_type, timestamp, confidence) list_filter (record_type, timestamp) date_hierarchy timestampdate_hierarchy会在 admin 列表页顶部生成一个按日期逐级筛选的导航条对考勤记录这种时间密集型数据非常实用。search_fields支持跨字段搜索输入工号前缀或姓名关键字即可过滤员工。admin 界面美化的方向是引入 django-simpleui 或 django-admin-interface 这类第三方库安装后只需要在 INSTALLED_APPS 里替换 admin 应用即可不用改业务代码。5. 实战运行从数据集训练到第一次打卡识别5.1 用数据集生成 feature_all.csv网上很多教程直接叫你运行某个create_dataset.py就完事但你必须理解生成 CSV 到底做了什么。给定数据集目录结构为dataset/员工姓名/图片1.jpg生成特征文件的完整脚本逻辑如下import os import csv import cv2 import dlib import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) face_rec dlib.face_recognition_model_v1(dlib_face_recognition_resnet_model_v1.dat) dataset_dir dataset output_csv feature_all.csv with open(output_csv, w, encodingutf-8, newline) as f: writer csv.writer(f) for name in os.listdir(dataset_dir): person_dir os.path.join(dataset_dir, name) if not os.path.isdir(person_dir): continue for img_name in os.listdir(person_dir): img_path os.path.join(person_dir, img_name) img cv2.imread(img_path) if img is None: print(f无法读取图片: {img_path}) continue gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces detector(gray, 1) if len(faces) 0: print(f未检测到人脸: {img_path}) continue face faces[0] shape predictor(gray, face) desc face_rec.compute_face_descriptor(img, shape) writer.writerow([name] list(desc)) print(f已处理: {img_path}) print(f特征文件已生成: {output_csv})注意这里只取了检测到的第一张人脸faces[0]。如果一张照片里有多个人的脸这种处理方式会出错。实际项目中应该根据人脸框面积取最大的一张或者干脆要求数据集照片均为单人正脸。运行后检查 csv 文件行数是否等于数据集照片数如果大量照片被跳过优先怀疑是图片里有头但脸太小或侧脸角度过大HOG 检测器对正面脸的召回率远高于侧脸。5.2 命令行模拟打卡流程识别和考勤建议分开验证。先跑一个纯命令行的脚本确认识别准确率达标再接 Django 视图。模拟一次打卡的完整命令链路如下# 激活虚拟环境 .\activate.bat # 运行识别脚本传入摄像头画面或一张测试图片 python recognize.py --input test_photo.jpg --csv feature_all.csv --threshold 0.6recognize.py 内部做的事情和我们第 3 章的 FaceRecognizer 类似但增加了 argparse 参数解析。下面是关键部分import argparse import cv2 from face_service import FaceRecognizer parser argparse.ArgumentParser(description人脸识别测试) parser.add_argument(--input, requiredTrue, help输入图片路径) parser.add_argument(--csv, defaultfeature_all.csv, help特征库CSV路径) parser.add_argument(--threshold, typefloat, default0.6, help识别阈值) args parser.parse_args() recognizer FaceRecognizer(args.csv, thresholdargs.threshold) frame cv2.imread(args.input) results recognizer.recognize(frame) print(识别结果:, results)如果识别结果显示 unknown但视觉上这个人确实在数据集里不要急着调阈值先用笔记本摄像头重新拍几张光线均匀、正脸的照片替换数据集的旧照片重新生成 CSV。数据质量对特征提取的影响远大于阈值调节空间。5.3 摄像头实时考勤终端把识别脚本和 Django 接口串起来就得到了一个完整的考勤终端。运行逻辑是打开摄像头循环读取帧每 5 帧做一次人脸检测检测到人脸且稳定跟踪超过 3 秒后执行一次识别并 POST 到 Django。加稳定跟踪的原因很直接不做跟踪的话同一个员工在视频流里会被连续识别多次导致产生多条重复打卡记录。最终整个流程跑起来的命令是# 终端 1启动 Django 服务 python manage.py runserver 0.0.0.0:8000 # 终端 2启动摄像头客户端 python camera_client.py --server http://127.0.0.1:8000/attendance/checkin/搜索热词里出现“宝塔部署django”“linux系统安装python”以及“python django搭建web项目”说明很多人在真实部署这一步卡住。Django 的 runserver 只适合开发调试生产环境要用 gunicorn 或 uwsgi。启动命令是gunicorn attendance_project.wsgi:application -b 0.0.0.0:8000 -w 4-w 4表示启动 4 个 worker 进程每个 worker 都是独立的 Python 进程。这台考勤机的内存如果只有 1GB建议改成-w 2因为 dlib 模型本身要占用几百兆内存worker 数过多会导致 OOM。如果你用的是宝塔面板在 Python 项目管理器里创建项目、指定 Python 版本后把启动命令填成上面的 gunicorn 命令即可。5.4 常见运行报错与排查对照表报错信息可能原因解决方法ModuleNotFoundError: No module named dlib虚拟环境未激活或 dlib 安装失败执行.\activate.bat后重新pip install dlib19.24.0Could not load shape_predictor_68_face_landmarks.dat模型文件路径不对或未下载检查文件是否在项目根目录路径改为绝对路径cv2.VideoCapture(0) 返回 False摄像头被其他程序占用关闭微信、浏览器等占用摄像头的程序或将设备编号 0 改为 1NameError: name FileNotFoundError is not definedPython 版本过旧确认使用的是 Python 3.x建议 3.8 以上中文名字在 CSV 里乱码编码问题读取 CSV 时指定encodinggbkadmin 后台打不开样式debug 模式未配静态文件开发环境确认django.contrib.staticfiles在 INSTALLED_APPS 中排错的核心思路是先区分是环境问题、数据问题还是代码问题。环境问题看 pip list 和 Python 版本数据问题看 feature_all.csv 的行数与识别率代码问题看报错堆栈的最后一行。资源包里的详细文档应该包含了这些常见错误但实际运行时新问题不可避免掌握了上面的对照逻辑换个报错也能自己定位。6. 进阶技巧用 feature_all.csv 做增量更新与识别性能优化到这个阶段基本的考勤功能已经完整跑通。最后给你两个这套系统上值得做的进阶优化能直接提升工程完成度也在答辩或简历里更有说头。第一个技巧是增量添加新员工不需要重新全量生成 CSV。新员工照片单独放到dataset_new/新员工名/下生成该员工的特征后追加写入原 CSV 即可import csv import cv2 import dlib import numpy as np def append_employee_to_csv(name, image_path, csv_path): detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) face_rec dlib.face_recognition_model_v1(dlib_face_recognition_resnet_model_v1.dat) img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces detector(gray, 1) if len(faces) 0: raise ValueError(未检测到人脸) shape predictor(gray, faces[0]) desc face_rec.compute_face_descriptor(img, shape) desc np.array(desc) with open(csv_path, a, encodingutf-8, newline) as f: writer csv.writer(f) writer.writerow([name] desc.tolist())追加前先检查 CSV 里是否已经存在同名员工。如果存在最好把旧记录删掉再追加或者对同一员工的多个特征做平均否则比对时同一员工有多行特征距离计算虽然不受影响但员工列表会重复。增量更新的好处是新增员工不需要重新遍历整个数据集处理时间从分钟级降到秒级。第二个优化是识别速度。dlib 的 HOG 检测器在 CPU 上处理单帧约 100 到 200 毫秒加上特征提取和对 CSV 的 128 维距离计算整体吞吐量有限。如果摄像头画面里有多个员工同时出现逐个人脸计算特征耗时会线性增长考勤终端会明显卡顿。常用的提速手段是把检测分辨率降下来对原始帧先缩小再检测scale_factor 0.5 small_frame cv2.resize(frame, None, fxscale_factor, fyscale_factor) gray_small cv2.cvtColor(small_frame, cv2.COLOR_BGR2GRAY) faces detector(gray_small, 1) # 检测到的人脸坐标要放大回去对应原图位置 faces [dlib.rectangle( int(face.left() / scale_factor), int(face.top() / scale_factor), int(face.right() / scale_factor), int(face.bottom() / scale_factor) ) for face in faces]注意dlib.rectangle的参数是 int 类型缩放计算后必须转 int否则类型错误会导致后续predictor调用失败。分辨率降一半检测耗时会降到原来的四分之一左右代价是极小的人脸可能漏检。考勤场景里员工离摄像头通常 0.5 到 1.5 米人脸占画面比例足够大缩小检测完全够用。另一个优化点是利用上一帧的检测结果在下一帧只在上一次人脸框附近扩大 20% 的区域内检测这个用 dlib 的 correlation_tracker 或自研滑动窗口都能实现。最后提一个容易被忽视的细节feature_all.csv 是纯文本、明文存储任何人打开都能看到员工姓名和特征向量。虽然这些数据不直接等于可还原的人脸照片但涉及员工隐私实际部署时应该把 CSV 文件权限设置为仅考勤机器能读取Django 后台不要在浏览器里直接暴露下载入口。到这里这套基于 Python、Django、OpenCV 和 dlib 的考勤系统从环境搭建到识别、考勤记录再到性能优化的完整闭环已经全部走通剩下的就交给你的实际数据去验证。本文还有配套的精品资源点击获取