ARTICLE DETAIL

资讯详情

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

Python人脸识别专注度检测源码实战:从摄像头到专注度分数

Python人脸识别专注度检测源码实战:从摄像头到专注度分数 简介这份源码包面向Python开发者与计算机视觉初学者提供一套可运行的人脸识别与专注度检测完整方案可用于人脸考勤打卡、课堂或办公场景的注意力分析等实践。包内共161个文件以93个png与12个jpg图像样本、22个py脚本、9个xml配置、5个dat模型文件及3个pt权重为主另含少量exe可执行程序与csv数据表压缩包约437.34MB涵盖从人脸检测、关键点定位到特征比对的全流程代码与模型资源。已有662人学习下载适合希望动手复现人脸识别项目、理解dlib与OpenCV配合方式的读者参考。源码涉及人脸检测、68点关键点、专注度判断、视频流处理、人脸比对与考勤记录、GUI界面及数据库存储等模块可帮助读者掌握特征向量计算、相似度度量与实时分析思路并借助现成模型文件快速搭建自己的检测环境。1. 从一张摄像头截图说起这套 Python 人脸识别与专注度检测源码到底能跑出什么很多人第一次接触「专注度检测」脑子里浮现的是脑电波头环或者眼动仪动辄几千上万。但如果你手里只有一台普通 USB 摄像头其实也能做出一个可用的版本——这正是这套 Python 人脸识别与专注度检测源码要解决的问题。它把「人脸检测 关键点定位 专注度打分」串成一条完整链路输入是摄像头画面或一段视频输出是每帧的人脸框、眼睛/头部姿态以及一个 0 到 1 之间的专注度分数。这套源码适合三类人一是做课程设计或毕设的学生需要一套能跑通、能改参数的完整流程二是想入门计算机视觉的 Python 开发者拿它当第一个「有业务含义」的练手项目三是做在线教育、驾驶监控、工位状态统计的从业者想先验证算法可行性再决定要不要上硬件。它不依赖专用设备核心依赖是 OpenCV、dlib 或 MediaPipe 这类常见库装好 Python 环境就能跑。需要先说明边界这套源码做的是「基于视觉的专注度估计」不是医学级注意力测量。它判断的是「你有没有看屏幕、头有没有偏、眼睛有没有长时间闭合」而不是你大脑里在想什么。理解这一点后面调参和看结果时才不会跑偏。2. 拆开看算法链路人脸检测、关键点与专注度打分怎么串起来2.1 为什么选 dlib OpenCV 这条经典组合这套源码的技术栈并不花哨主干是 OpenCV 负责视频读取和图像预处理dlib 负责人脸检测和 68 点关键点定位。选这条组合的理由很实际dlib 的 68 点模型shape_predictor_68_face_landmarks.dat是公开且稳定的关键点编号有明确语义比如 36–41 是左眼、42–47 是右眼、30 是鼻尖、8 是下巴。做专注度判断时你需要的正是这些语义明确的点。相比之下MediaPipe 更快、更轻但它的关键点编号体系和 dlib 不同网上大量现成的专注度计算代码是按 dlib 的 68 点写的。如果你拿到的源码里出现shape[36]、shape[45]这种下标基本可以确定是 dlib 体系。常见做法是先用 dlib 跑通逻辑等要上嵌入式或追求帧率时再考虑换 MediaPipe 或轻量模型。提示dlib 的 68 点模型文件需要单独下载源码包里通常会附带如果没有要确认版本匹配否则shape_predictor初始化会直接报错。2.2 从关键点算出三个核心指标专注度不是一个直接测量值而是由几个可观测指标加权得到的。这套源码里通常会出现三个量第一个是眼睛纵横比 EAREye Aspect Ratio。原理是眼睛睁开时上下眼睑关键点的垂直距离较大闭眼时这个距离变小。用公式表示对左眼取 36–41 六个点EAR (|p38-p42| |p39-p41|) / (2 * |p37-p40|)。当 EAR 低于某个阈值并持续若干帧就判定为闭眼或眨眼。第二个是头部姿态。用鼻尖、下巴、左右眼角这几个点结合 solvePnP 可以估算头部的俯仰、偏航、滚转三个角度。头低得太厉害通常意味着在看手机或走神。第三个是视线方向或面部朝向的粗略估计。有些实现会用瞳孔位置有些直接用头部偏航角代替。下面是一段典型的 EAR 计算代码你可以对照自己手里的源码看结构是否一致import numpy as np def eye_aspect_ratio(eye_points): # eye_points 是 6 个 (x, y) 坐标顺序为左角、上左、上右、右角、下右、下左 # 垂直距离上眼睑两点与下眼睑两点的距离 vertical_1 np.linalg.norm(eye_points[1] - eye_points[5]) vertical_2 np.linalg.norm(eye_points[2] - eye_points[4]) # 水平距离左右眼角距离 horizontal np.linalg.norm(eye_points[0] - eye_points[3]) # 加 1e-6 防止除零 ear (vertical_1 vertical_2) / (2.0 * horizontal 1e-6) return ear逻辑说明eye_points是从 dlib 68 点里切出来的 6 个点顺序必须和公式对应顺序错了 EAR 会完全失真。参数说明分母加1e-6是防止人脸侧转时水平距离趋近于零导致除零崩溃这是血泪经验不加的话某些帧会直接抛异常。2.3 专注度分数的加权与平滑拿到 EAR、头部角度之后源码一般会做归一化和加权。常见做法是EAR 低于阈值扣分头部偏航超过 ±20 度扣分俯仰超过 ±15 度扣分最后映射到 0–1。但直接逐帧算出来的分数会剧烈跳动所以必须做滑动窗口平滑比如取最近 30 帧的均值。from collections import deque class FocusScorer: def __init__(self, window30, ear_thresh0.21): self.window window self.ear_thresh ear_thresh self.history deque(maxlenwindow) def score(self, ear, yaw, pitch): # 眼睛分数EAR 高于阈值给满分低于阈值线性衰减 eye_score min(1.0, ear / self.ear_thresh) # 头部分数偏航和俯仰偏离越大扣分越多 head_score max(0.0, 1.0 - (abs(yaw) / 45.0 abs(pitch) / 30.0)) # 加权眼睛权重更高 raw 0.6 * eye_score 0.4 * head_score self.history.append(raw) return sum(self.history) / len(self.history)逻辑说明deque(maxlenwindow)自动维护固定长度队列不用手动裁剪。参数说明ear_thresh默认 0.21 是经验值但不同人眼型差异大戴眼镜的人 EAR 普遍偏低这个值要按实际画面调。yaw和pitch的单位是度除以 45 和 30 是把角度归一化到 0–1 区间。3. 把源码跑起来环境配置、模型加载与实时检测3.1 Python 环境与依赖安装的稳妥顺序这套源码对 Python 版本不算挑剔3.8 到 3.11 都能跑但 dlib 的安装是第一个坎。Windows 上直接pip install dlib经常因为缺少 CMake 和 Visual Studio 编译环境而失败。稳妥顺序是先装 CMake再装 dlib最后装 OpenCV。# 建议在虚拟环境里操作避免污染全局 python -m venv focus_env # Windows 激活 focus_env\Scripts\activate # macOS / Linux 激活 source focus_env/bin/activate # 先升级 pip老版本 pip 解析依赖容易出错 pip install --upgrade pip # 安装 CMakedlib 编译需要 pip install cmake # 安装 dlib如果这里卡住见 3.2 的替代方案 pip install dlib # 安装 OpenCV 和数值计算库 pip install opencv-python numpy逻辑说明把 cmake 单独拎出来先装是因为 dlib 的 setup 脚本会在编译阶段调用它混在一起装时 pip 的构建隔离有时会让 cmake 不可见。参数说明opencv-python是包含 GUI 功能的版本如果你在无桌面的服务器上跑换成opencv-python-headless可以省掉一堆图形库依赖。3.2 dlib 装不上时的两条退路如果pip install dlib反复失败不要死磕。第一条退路是用 condaconda install -c conda-forge dlibconda 有预编译包省去本地编译。第二条退路是改用 MediaPipe它的安装几乎不会失败代价是关键点编号要重新映射。# MediaPipe 替代方案的关键点映射示例 import mediapipe as mp mp_face_mesh mp.solutions.face_mesh # MediaPipe 左眼上下点索引和 dlib 不同 LEFT_EYE_TOP 159 LEFT_EYE_BOTTOM 145 LEFT_EYE_LEFT 33 LEFT_EYE_RIGHT 133 def ear_mediapipe(landmarks, w, h): top np.array([landmarks[LEFT_EYE_TOP].x * w, landmarks[LEFT_EYE_TOP].y * h]) bottom np.array([landmarks[LEFT_EYE_BOTTOM].x * w, landmarks[LEFT_EYE_BOTTOM].y * h]) left np.array([landmarks[LEFT_EYE_LEFT].x * w, landmarks[LEFT_EYE_LEFT].y * h]) right np.array([landmarks[LEFT_EYE_RIGHT].x * w, landmarks[LEFT_EYE_RIGHT].y * h]) return np.linalg.norm(top - bottom) / (np.linalg.norm(left - right) 1e-6)逻辑说明MediaPipe 返回的是归一化坐标要乘以画面宽高还原成像素坐标。参数说明索引 159、145、33、133 是 MediaPipe Face Mesh 里左眼对应的点右眼要换成另一组索引具体查官方拓扑图。这套映射一旦写错EAR 会算出离谱的值表现为分数一直为 0 或一直为 1。3.3 主循环读帧、检测、打分、显示把前面几块拼起来主循环结构大致如下。这段代码是整套源码的心脏理解它之后改任何参数都有方向。import cv2 import dlib detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) scorer FocusScorer(window30, ear_thresh0.21) cap cv2.VideoCapture(0) # 0 表示默认摄像头 while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) for face in faces: shape predictor(gray, face) # 转成 numpy 方便计算 points np.array([[p.x, p.y] for p in shape.parts()]) left_eye points[36:42] right_eye points[42:48] ear (eye_aspect_ratio(left_eye) eye_aspect_ratio(right_eye)) / 2.0 # 头部姿态这里省略 solvePnP 细节返回 yaw 和 pitch yaw, pitch estimate_head_pose(points, frame.shape) focus scorer.score(ear, yaw, pitch) cv2.putText(frame, fFocus: {focus:.2f}, (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2) cv2.imshow(Focus Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()逻辑说明detector(gray, 0)的第二个参数是上采样次数0 表示不放大速度快但小脸可能漏检改成 1 能提升召回率但帧率下降。参数说明cv2.waitKey(1)里的 1 是毫秒值越小循环越快但太小在某些系统上窗口不响应一般 1 到 30 之间。ord(q)是退出键调试时很实用。4. 避坑与排查跑这套源码最容易翻车的五个地方4.1 摄像头打开成功但画面全黑现象cap.read()返回 True但frame全是零imshow出来一片黑。原因摄像头被其他程序占用或者索引不对。有些笔记本内置摄像头是 0外接 USB 摄像头是 1但顺序不固定。解决把VideoCapture(0)依次改成 1、2 试或者用cap.isOpened()先判断。另外某些系统上首次调用摄像头有权限弹窗没点允许就会拿到黑帧。4.2 关键点模型加载报错现象运行到dlib.shape_predictor(...)时抛RuntimeError: Unable to open shape_predictor_68_face_landmarks.dat。原因模型文件路径不对或者文件根本没下载。解决确认文件在当前工作目录或者写绝对路径。注意这个模型文件有几十 MB如果源码包里的是几 KB 的文件那是下载失败的占位文件要重新获取。4.3 专注度分数一直为 0 或一直为 1现象不管怎么动分数不动。原因EAR 阈值和实际画面不匹配或者关键点索引取错。解决先把 EAR 实时打印出来正常睁眼时应该在 0.25 到 0.35 之间闭眼时降到 0.15 以下。如果打印出来是 0.01 或 5.0 这种离谱值就是索引错了。戴眼镜的人 EAR 整体偏低阈值要从 0.21 往下调到 0.18 左右。4.4 帧率低到个位数现象画面卡顿分数更新很慢。原因dlib 的 HOG 检测器在每帧全图检测分辨率越高越慢。解决把画面缩到 640×480 再检测或者改成每 3 帧检测一次人脸中间帧复用上一次的人脸框。这是常见做法能显著提升流畅度代价是人脸快速移动时框会滞后。4.5 多人画面下分数串台现象画面里有两个人分数在两个人之间乱跳。原因源码默认只处理faces列表里的第一个或最后一个没有按人绑定状态。解决给每个人脸分配一个 ID可以用简单的 IOU 跟踪每个 ID 维护独立的FocusScorer实例。如果只是单人场景可以在检测到多张脸时直接跳过或只取面积最大的那张。5. 进阶玩法把专注度分数变成可用的数据跑通实时显示只是第一步真正有价值的是把分数沉淀成数据。我一般会加一个 CSV 记录模块每秒钟写一行包含时间戳、人脸数、平均专注度。这样一段 45 分钟的网课下来就能画出专注度曲线看出哪个时间段走神最多。import csv import time class FocusLogger: def __init__(self, pathfocus_log.csv): self.file open(path, w, newline) self.writer csv.writer(self.file) self.writer.writerow([timestamp, face_count, avg_focus]) self.last_write 0 def log(self, face_count, avg_focus): now time.time() # 每秒最多写一次避免文件爆炸 if now - self.last_write 1.0: self.writer.writerow([f{now:.2f}, face_count, f{avg_focus:.3f}]) self.file.flush() self.last_write now逻辑说明flush()保证程序被强制中断时数据不丢这是后悔药级别的细节。参数说明1.0是写入间隔秒数做长时统计够用要做精细分析可以降到 0.2。另一个进阶方向是阈值自适应。固定阈值在不同光照、不同人之间表现差异很大可以在程序启动时先采集 5 秒「正常注视」的 EAR 均值以此为基准动态设定阈值比如取基准的 70%。这样换个人、换个房间不用手动改代码。验证方法上建议做一次对照实验让同一个人分别「正常看屏幕」和「低头看手机」各 30 秒看两种状态下平均分是否有明显区分。如果区分度不到 0.2说明权重或阈值需要调。我自己的习惯是每次改完参数都强制走一遍这个对照不然很容易被某一帧的偶然高分骗过去。希望这套源码和上面的调参思路能帮你少走几个弯路。本文还有配套的精品资源点击获取
返回列表