☰
基于Python+OpenCV+Dlib的人脸识别考勤系统实战与调参指南
2026/9/28 22:32:17 网站建设 项目流程

简介:面向计算机相关专业毕业生与实战学习者,这是一份基于Python、OpenCV和dlib开发的人脸识别考勤打卡系统完整源码与项目说明包。项目由导师指导并获98分评审,功能覆盖录入人脸信息、人脸识别打卡、上下班时间设置、打卡日志导出等模块,配有图形化界面,可直接作为毕业设计、课程设计或期末大作业的参考。资源共657个文件,压缩包约196.2MB,文件类型非常齐全:除501个Python源码文件外,还包含可直接运行的exe、模型权重pth、动态库dll/pyd、依赖配置文件、bat启动脚本以及docx项目说明文档等,环境依赖已在requirement.txt中列出,建议使用Python3.8与PyCharm运行。项目说明中还展示了首页、录入人脸等模块截图,便于对照操作。目前已有394人学习使用,适合需要快速上手OpenCV/dlib人脸识别流程、希望获得完整可扩展考勤系统代码的学习者。

1. 课程大作业做个人脸识别考勤系统,到底值不值得

基于Python+OpenCV+Dlib库实现的人脸识别考勤打卡系统,是计算机、物联网、人工智能课程里出现频率最高的一类课程大作业。它看起来只是把摄像头打开、框出人脸、比一下特征、写一条记录,但真正动手才发现:环境装不上、识别不准、一开摄像头就崩溃、交上去被老师问两句就露怯。我把从项目结构到参数调优、从环境配置到踩坑排查的完整路径讲透,适合课程设计、毕业设计、公司内部demo,也适合想拿源码快速改造成自己作品的开发者。这里不假设你有现成的依赖,所有命令都按可复现的方式给出来。

2. Dlib和OpenCV在考勤系统里的分工:先明白谁在干重活

很多人拿到这类课程大作业的第一反应是“OpenCV做人脸识别”,这是最常见的误区。OpenCV在项目里更像一个搬运工和画框员,真正的识别逻辑落在Dlib上。搞清楚这一点,后面调参、改代码、回答老师提问都会顺很多。

2.1 一条完整的人脸识别考勤链路

一套离线考勤系统的标准流程可以拆成六步:打开摄像头取帧、检测画面里的人脸、定位人脸关键点、提取128维人脸特征、和已注册的特征库做距离比对、把通过比对的记录写入考勤文件。前两步由OpenCV和Dlib配合完成,第三步开始基本是Dlib的地盘。

具体来说,OpenCV的VideoCapture从摄像头拿到的是BGR顺序的彩色帧,Dlib的检测器和特征模型只接受8位灰度图或RGB图。所以每一帧都要先做一次cvtColor转换,再传给Dlib的get_frontal_face_detector。检测到的人脸框是一个dlib.rectangle对象,包含left、top、right、bottom四个坐标,后续画框、裁剪都要靠它。

下一步是shape_predictor_68_face_landmarks,它会在人脸框内标出68个关键点,包括眉毛、眼睛、鼻子、嘴巴和下巴轮廓。这一步不直接参与判断“你是谁”,但非常关键。人脸姿态稍有倾斜,特征就会跑偏,有了关键点才能做对齐,把歪着的脸“掰正”再提特征。Dlib的face_recognition_model_v1接收对齐后的人脸,输出一个128维的浮点向量。之后用欧氏距离衡量当前向量和已注册向量的相似程度,距离越小越可能是同一个人。

考勤记录一般不会直接写进数据库,课程大作业用CSV最省事。每通过一次比对,就往attendance.csv里追加一行:姓名、时间、距离值。这个文件可以直接用Excel打开,老师检查也方便。

2.2 为什么选Dlib而不是纯OpenCV或深度学习框架

人脸识别门禁机这类商用设备内部通常是专用芯片加深度网络方案,离线识别、防伪能力都很强,但课程大作业没法直接复现那套东西。纯OpenCV自带的人脸识别算法主要有EigenFace、FisherFace、LBPH,它们训练速度快,但换角度、换光线就明显掉链子,做宿舍考勤还可以,放到教室不同座位位置去识别,误报率会高到没法看。

Dlib的位置很“学院派”:它不像TensorFlow或PyTorch那样要求你自己搭网络、准备数据集训练,官方已经把训练好的模型打包好了。检测用HOG特征加线性分类器的经典方案,特征提取用预训练的ResNet网络,普通笔记本CPU就能实时跑,不需要GPU。这对课程大作业来说是决定性的优势,因为大多数实验室机器没有NVIDIA显卡,深度学习方案装CUDA就把人劝退了。

Dlib的代价是模型文件体积不小,项目说明里一般会要求单独下载两个模型文件:shape_predictor_68_face_landmarks.dat和dlib_face_recognition_resnet_model_v1.dat。第一个约几十MB,第二个也差不多。它们属于Dlib官方发布的预训练模型,不随pip包自动下载,需要手动放进models目录。这也是很多源码包跑不起来的头号原因——代码路径里写了模型文件名,但实际文件没放到位。

选型时我还会看另一个维度:离线能力。课程答辩现场经常没网,或者老师要求不能连外网,Dlib方案所有计算都在本机完成,不会因为断网导致演示翻车。相比之下一些在线人脸识别API虽然识别率更高,但本质是把你录的视频帧传到服务器,课程作业里使用会涉及隐私和网络依赖问题,稳妥起见不建议作为主方案。

2.3 OpenCV在项目里的真实职责不只是“打开摄像头”

把OpenCV理解成图像封装层更准确。它管四件事:摄像头帧获取、BGR到RGB的颜色空间转换、图像缩放、结果可视化。实时考勤如果直接跑1280x720的原始帧,Dlib检测一次可能耗掉几百毫秒,画面会卡到没法用。常见的做法是先把帧缩小到宽度480或640,再交给Dlib,识别完把检测框坐标按比例映射回原图画框。这个缩放比例要记在项目说明里,不然换个人改代码,框的位置会对不上。

摄像头索引也是个容易忽略的点。笔记本自带摄像头一般是0,外接USB摄像头可能是1也可能是2。验证当前环境能不能读到帧,我习惯先跑一段最小代码:

import cv2 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("摄像头索引0打不开,试试传1") else: ok, frame = cap.read() if ok: print("读到帧,尺寸:", frame.shape) else: print("摄像头能打开,但读不到帧") cap.release()

这里第0个参数“0”就是摄像头索引。如果输出“摄像头索引0打不开”,把0改成1再试。有些USB摄像头驱动需要等一秒钟才出画面,可以在cap.open之后加个cv2.waitKey(1000)热身,但这句不能放在循环外太久,否则画面会冻住。测试通过之后再进入下一步。

2.4 检测器参数和特征距离的基本逻辑

Dlib的get_frontal_face_detector在调用时可以传第二个参数,表示图像金字塔的上采样次数。传0表示只在原始尺寸检测,传1表示把图像放大一倍再检测,用来捕捉更小的人脸。实时场景里这个值不要超过2,否则CPU会直接拉满,帧率掉到个位数。

特征比对的距离也有讲究。两个128维向量之间的欧氏距离通常落在0到1.2之间,同一个人的不同照片距离一般在0.35到0.55左右,不同人一般超过0.6。很多源码把阈值写死成0.5,项目说明里也会告诉你“低于0.5判为本人”。但这个值不是万能的,侧脸、逆光、戴眼镜都会让距离变大,所以实际部署时阈值是一个必须手工调的参数,我一般放在配置区单独定义,而不是让代码里到处都是裸的0.5。

3. 从项目结构到核心代码:把考勤打卡逻辑拆成可复现的模块

课程大作业的源码包拿到手之后,第一件事绝对不是打开main.py按F5,而是先看项目说明里的目录结构。人脸识别考勤系统的代码量不大,但依赖外部文件多:模型文件、人脸注册照片、特征库、考勤记录。任何一环缺失,程序都跑不起来。

3.1 项目目录怎么组织:源码、模型、人脸库、说明文档分开

一份标准的attendance_system项目,目录结构一般是这样的:

attendance_system/ ├── main.py # 摄像头实时打卡入口 ├── register.py # 人脸注册脚本 ├── utils.py # 特征读取、比对、日志函数 ├── models/ │ ├── shape_predictor_68_face_landmarks.dat │ └── dlib_face_recognition_resnet_model_v1.dat ├── faces/ │ ├── 张三_01.jpg │ ├── 张三_02.jpg │ └── 李四_01.jpg ├── known_faces.pkl # 注册后生成的特征库 ├── attendance.csv # 考勤记录 └── README.md # 项目说明

models目录放Dlib模型文件,faces目录放注册用的单人照片,照片命名规则是“姓名_编号.jpg”。known_faces.pkl是运行register.py之后生成的特征库二进制文件,attendance.csv是打卡记录。项目说明里一般会强调模型文件不放进下载链接,因为体积太大,要求使用者去Dlib官网自己下载。这里不要跳过,没有这两个模型文件,程序会在初始化阶段直接报错。

如果源码只有几百行但项目说明写得厚,不要觉得是凑字数。README里真正有价值的信息是这三条:Dlib模型的获取方式、Python版本要求、先注册再打卡的运行顺序。把这三点核对完,再开始改代码。

3.2 人脸注册:把“谁是谁”存出特征库

注册阶段做的事很简单:遍历faces目录下的照片,对每张照片检测人脸、提取128维特征,用pickle序列化到known_faces.pkl。我的常用写法如下:

# register.py import os import pickle import cv2 import dlib import numpy as np detector = dlib.get_frontal_face_detector() sp = dlib.shape_predictor("models/shape_predictor_68_face_landmarks.dat") facerec = dlib.face_recognition_model_v1("models/dlib_face_recognition_resnet_model_v1.dat") def get_face_feature(image_path): img = cv2.imread(image_path) if img is None: print(f"无法读取图片: {image_path}") return None rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) faces = detector(rgb, 1) if len(faces) == 0: print(f"未检测到人脸: {image_path}") return None # 注册照片要求单人,直接取检测到的第一张脸 shape = sp(rgb, faces[0]) descriptor = facerec.compute_face_descriptor(rgb, shape) return np.array(descriptor) if __name__ == "__main__": known_faces = {} for filename in os.listdir("faces"): if not filename.lower().endswith((".jpg", ".jpeg", ".png")): continue # 文件名格式: 姓名_编号.jpg name = filename.split("_")[0] feat = get_face_feature(os.path.join("faces", filename)) if feat is not None: known_faces.setdefault(name, []).append(feat) print(f"已注册 {name},特征维度 {feat.shape}") if not known_faces: print("没有注册到任何有效人脸,请检查faces目录和模型文件路径") else: with open("known_faces.pkl", "wb") as f: pickle.dump(known_faces, f) print("特征库已保存到 known_faces.pkl")

detector(rgb, 1)里的第二个参数1,表示图像金字塔上采样1次,能稍微增强对小脸的检测能力。这里不传2的原因很简单:注册照片通常是大头照,没必要放大两倍增加计算量。compute_face_descriptor返回的是dlib格式的向量,转成numpy数组方便后面算欧氏距离。

每个同学只放一张照片注册出来的特征库很容易出问题,因为人脸特征对角度和光线敏感。常见做法是每个同学放2到3张不同角度的照片,known_faces[name]是一个列表,后续比对时该同学的所有特征都参与计算,取最小的距离。

3.3 实时打卡:摄像头循环里比对距离与写CSV

实时打卡的代码结构是“初始化模型 → 加载特征库 → 循环读帧 → 检测、提取、比对 → 画框写日志”。下面这段是可运行的主程序骨架:

# main.py import os import csv import time import pickle from datetime import datetime import cv2 import dlib import numpy as np detector = dlib.get_frontal_face_detector() sp = dlib.shape_predictor("models/shape_predictor_68_face_landmarks.dat") facerec = dlib.face_recognition_model_v1("models/dlib_face_recognition_resnet_model_v1.dat") if not os.path.exists("known_faces.pkl"): raise SystemExit("找不到known_faces.pkl,请先运行python register.py") with open("known_faces.pkl", "rb") as f: known_faces = pickle.load(f) def match_face(feature, threshold=0.5): best_name = "unknown" best_dist = 1.0 for name, feats in known_faces.items(): for feat in feats: dist = np.linalg.norm(feature - feat) if dist < best_dist: best_dist = dist best_name = name if best_dist < threshold: return best_name, best_dist return "unknown", best_dist # 记录每个人最近一次打卡时间,防止同一人连续重复打卡 last_record_time = {} cap = cv2.VideoCapture(0) if not cap.isOpened(): raise SystemExit("摄像头打开失败,请检查摄像头索引或权限") while True: ret, frame = cap.read() if not ret: break rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) faces = detector(rgb, 1) for face in faces: shape = sp(rgb, face) feature = np.array(facerec.compute_face_descriptor(rgb, shape)) name, dist = match_face(feature, threshold=0.5) x1, y1, x2, y2 = face.left(), face.top(), face.right(), face.bottom() if name != "unknown": now = time.time() if now - last_record_time.get(name, 0) > 60: last_record_time[name] = now with open("attendance.csv", "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([name, datetime.now().strftime("%Y-%m-%d %H:%M:%S"), f"{dist:.4f}"]) print(f"打卡成功: {name} 距离 {dist:.4f}") color = (0, 255, 0) if name != "unknown" else (0, 0, 255) cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) cv2.putText(frame, f"{name} {dist:.2f}", (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 2) cv2.imshow("Attendance", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

match_face里的threshold默认0.5,这个值是整个系统最核心的敏感参数。欧氏距离越小表示越像,0.5在实际项目里属于比较宽松的档位,如果同一个人因为逆光被拒识,可以放宽到0.55甚至0.6;如果出现两个人互相认错,就往回调到0.45或0.4。last_record_time是防止重复打卡的关键,它记录每个名字最近一次成功写入的时间,60秒内不会再次写入。这个时间窗口可以根据课程需求改,比如老师要求下课才算考勤,可以改成7200秒。

运行main.py前要确保已经执行过register.py,因为main.py启动时会直接加载known_faces.pkl,找不到文件就退出。这属于源码包里最常见的“运行顺序”问题,代码本身没错,是用户没有按项目说明的先后步骤执行。

4. 运行步骤与关键参数调整:从Python环境到Dlib安装

环境配置是这类项目劝退率最高的环节。很多同学不是代码能力不行,而是卡在Python版本、OpenCV安装和Dlib编译这三件事。这里按我实践下来最顺的顺序来走。

4.1 环境搭建:安装顺序和常见坑

人脸识别考勤系统依赖三个核心包:opencv-python、dlib、numpy。课程设计要求Python版本在3.8到3.10之间最稳,尤其是Dlib,在Python 3.12以上经常没有预编译wheel,pip装到一半就开始编译源码,一编译就是半小时起步。

安装命令我一般是这样:

pip install numpy pip install opencv-python pip install cmake pip install dlib

先装numpy和opencv,再装cmake,最后装dlib。cmake不是直接给Python代码用的,但Windows环境下Dlib如果走源码编译,必须靠cmake生成构建文件。如果pip安装dlib时报错缺少“visual studio c++ build tools”,需要先去安装Visual Studio Build Tools,勾选“使用C++的桌面开发”工作负载,装完再执行一次pip install dlib。

OpenCV的安装相比之下很省心,pip包名是opencv-python,import语句是cv2。有些教程让你安装opencv-contrib-python,那个人脸识别门禁机项目里可能用到SIFT之类扩展算法,这个课程设计用不到,装基础版就行。

验证安装是否成功的命令很简单:

python -c "import cv2; print(cv2.__version__)" python -c "import dlib; print(dlib.__version__)"

如果第一行报ModuleNotFoundError: No module named 'cv2',一般是当前命令行激活的虚拟环境和你执行脚本的环境不是同一个。VSCode里常见的问题是按了右上角运行按钮,但右下角选择的Python解释器和终端里使用的不是同一个。这个坑和Windows环境变量无关,纯粹是IDE解释器选择问题。

4.2 三个必调参数:上采样次数、特征距离阈值、打卡间隔

项目跑通之后,真正影响效果的是三个参数。我把它们单独列出来,因为课程大作业答辩时老师大概率会问“你这两个数为什么这么设”。

参数代码位置作用经验值
检测上采样次数detector(rgb, n) 的第二个参数决定多小的脸能被检测到0或1,实时场景不要超过2
特征距离阈值match_face(feature, threshold=0.5)判定“是不是本人”的界限0.4到0.55之间
打卡时间间隔last_record_time 判断条件里的60防止同一人短时间重复打卡30到7200秒

检测上采样次数传2不是不行,但Dlib的CPU占用会成倍上涨。笔记本风扇开始咆哮之后,摄像头帧率会掉到10帧以下,演示效果很差。我的习惯是固定为1,然后用缩放原始帧来控制检测难度,也就是把视频帧分辨率控制在640x480以内。

特征距离阈值需要结合你们班同学的实际人脸数据来调。第一次运行可以先保持0.5,让几个人轮流测一遍,把输出到控制台的“打卡成功: 张三 距离 0.4821”这类日志收集起来,找到同一人和不同人的距离分界线。如果不同人的最小距离都比同一个人的最大距离大很多,说明这个阈值很安全,甚至可以紧缩到0.45提高辨识度。如果两个人距离特别接近,比如0.55附近都有人,那就不能全靠调阈值,要给他们多注册几张照片。

打卡时间间隔在课程演示场景里往往设成30秒或60秒,方便现场连续演示多次。如果做成真实考勤系统,间隔应该改成你定义的最短考勤周期,比如一节课只能打一次卡,就设成3600秒。注意这个参数是按“名字”单独计数的,班上几十个同学互不影响。

4.3 按“先注册再打卡”的顺序运行

任何一个人脸识别考勤系统,运行顺序都是固定的:先注册,后打卡。注册脚本扫描faces目录生成known_faces.pkl,主程序加载这个文件开始识别,缺少的环节直接报错退出。

python register.py python main.py

register.py运行完之后,控制台会依次打印“已注册 张三,特征维度 (128,)”这类信息。如果某个同学的照片没检测到脸,会打印“未检测到人脸”并跳过,任务结束后这个人的名字不会出现在known_faces.pkl里。上课时他刷脸就会显示unknown,这时候不要怀疑算法,回faces目录检查照片是不是太模糊或者侧脸太大。

main.py启动后如果摄像头画面正常且能画出人脸框,说明环境链路已经通了。接下来再把摄像头对着注册过的同学,距离值稳定低于阈值时就会提示打卡成功,attendace.csv里出现新增记录。

5. 人脸识别考勤系统常见问题与排查:5个翻车现场

课程大作业的源码跑不通,大概率不是代码逻辑问题,而是环境、路径、数据这三个维度出了岔子。这里盘点五个高频翻车现场,都按现象、原因、解决三步写。

5.1 ModuleNotFoundError: No module named 'dlib'

现象:代码第一行import dlib就报错,提示No module named 'dlib',或者pip install dlib装到一半出现一堆红色报错。

原因:当前Python环境没有安装Dlib,或者Python版本太新,Dlib没有对应的预编译wheel包,pip尝试从源码编译又缺编译器。后者在Windows上最常见,报错信息里通常包含visual studio相关的字段。

解决:优先把Python版本切到3.9或3.10,重新建一个虚拟环境。在虚拟环境里依次执行pip install cmake、pip install dlib,如果还失败,就去安装Visual Studio Build Tools并勾选“使用C++的桌面开发”组件,再回来重装。千万不要用Anaconda的base环境硬怼,那个环境里装了很多无关包,依赖冲突会掩盖真实错误。

5.2 摄像头打开就黑屏或卡死

现象:main.py运行后窗口弹出来了,但画面是黑的,程序卡在那里不动,或者过几秒直接无响应。

原因:摄像头索引不对,摄像头被其他软件占用,或者代码里没有正确释放摄像头导致下次启动失败。VSCode的调试器、腾讯会议、系统相机应用都可能占用摄像头,Windows下摄像头设备一旦被独占打开,OpenCV就取不到帧。

解决:先跑前面那段最小摄像头测试代码,确认索引0能不能出帧。如果不行,换成索引1。代码跑完后一定执行cap.release(),否则下次运行会报“摄像头已打开”。如果还是黑屏,检查系统隐私设置里是否允许桌面应用使用摄像头。还有一个容易忽略的点:有些USB摄像头初次连接需要等两秒才输出画面,可以在cap.open之后加一个time.sleep(2)或者连续读几帧丢弃,再进入主循环。

5.3 识别准确率低,换个人就认不出

现象:注册的时候明明成功了,但运行main.py时,自己的脸被标成unknown,或者同宿舍两个人的名字来回乱跳。

原因:特征库样本太少,人脸没有对齐,阈值设得太严或太松。如果一个人只注册了一张照片,稍微换个角度距离就超过阈值,自然认不出来。两个人的特征距离本来就很接近时,阈值0.5会让系统在两个名字之间摇摆,这也是人脸识别算法常见的先天不足。

解决:每个同学至少放三张照片注册,分别是正面、轻微左转、轻微右转,并保证光线均匀。如果照片是在实验室拍的,打卡时也尽量让被测人脸处在相似光照条件。距离阈值先调宽到0.6,看是否每个人都能正确匹配,再逐步缩到0.5以下。不要为了追求“严格”把阈值压到0.3,那样会出现大量拒真,演示现场会很尴尬。

5.4 打卡记录重复写入,一分钟打十次卡

现象:attendance.csv里同一个名字、同一分钟内出现好几条几乎一样的时间记录,间隔只有一两秒。

原因:识别循环是逐帧执行的,只要这个人在画面里没有离开,每一帧都会比对成功并执行写入。源码里没有做时间窗口限制或者时间窗口设置不当,就会出现重复打卡。

解决:在写入之前加一个时间间隔判断。标准做法是维护一个字典,键是姓名,值是最近一次成功写入的time.time()时间戳,每次写入前检查当前时间减去上次时间是否大于规定间隔。间隔值不能太小,否则排队刷脸时同一个人会被连续打两次卡;但也不能太大,否则演示时想展示重复打卡效果得等很久。课程小班上课场景设60秒比较合适。

5.5 路径写死,换电脑就崩

现象:在自己电脑上跑得好好的,拷到实验室电脑上运行,提示找不到shape_predictor_68_face_landmarks.dat,或者报错“No such file or directory”。

原因:代码里写死了模型的绝对路径,比如“C:/Users/你的名字/Desktop/attendance_system/models/...”,换电脑后这个路径不存在。还有一种是使用相对路径但没搞清“当前工作目录”,在VSCode里启动时当前目录可能是项目根目录,但双击运行或从别的目录进入就会失效。

解决:所有模型路径和文件输出路径都用基于当前文件所在目录的相对路径,靠os.path.dirname和__file__组合:

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_DIR = os.path.join(BASE_DIR, "models") SHAPE_PREDICTOR_PATH = os.path.join(MODEL_DIR, "shape_predictor_68_face_landmarks.dat")

这样不管项目文件夹放在桌面还是D盘,也不管终端的工作目录是哪,代码都会从自身所在的目录往下找models。同样,known_faces.pkl和attendance.csv的路径也建议用BASE_DIR拼接。换电脑时整个项目文件夹拷贝带走,只要内部结构不变,路径就不会断。

6. 把课程设计做成能演示的系统:活体检测与GUI验证技巧

6.1 加一个人眼纵横比判断活体

答辩时老师最常问的问题之一就是“照片能不能骗过你的系统”。Dlib的68点关键点里包含左右眼各6个关键点,可以算人眼纵横比EAR。当真人眨眼时EAR会瞬间减小再恢复,而照片打印件的EAR几乎恒定,用这个最简单的方法就能拦住静态照片。计算逻辑不复杂:取眼睛三个垂直方向的距离平均值,除以水平方向距离,EAR小于0.25视为闭眼,连续多帧闭眼再判定为活体。

6.2 用日志和画面文字辅助演示

把比对距离直接画在视频画面上,比只显示名字更有说服力。在main.py里保留那行putText,显示“姓名 0.42”这样的实时距离,老师一眼就能看到阈值在起作用。同时在控制台打印每次写入CSV的行内容,重要参数和错误用清晰的print输出,避免代码黑盒运行。我一般还会把阈值和打卡间隔写在项目说明的README最前面,方便现场讲解。

6.3 我的习惯

做完功能之后,我会刻意用极端条件测一遍:逆光、侧脸、戴帽子、手机拍电脑屏幕。这四种情况至少会暴露两个问题,要么检测不到脸,要么距离超标。把这些测试结果和调整记录写进项目说明里,反而比一个“完美运行”的Demo更让老师认可。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询