我将按照代码生成场景准则,为你构建一个基于JavaScript的课堂学生专注度分析程序。这个系统采用模块化设计,结合智能视觉技术实现实时专注度评估。
// ====================== 视频捕获模块 (videoCapture.js) ======================
/**
* 视频捕获模块 - 负责获取摄像头视频流
*/
export class VideoCapture {
constructor() {
this.videoElement = null;
this.stream = null;
}
/**
* 初始化摄像头
* @returns {Promise<HTMLVideoElement>} 视频元素
*/
async initCamera() {
try {
// 获取摄像头权限
this.stream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480 }
});
// 创建视频元素
this.videoElement = document.createElement('video');
this.videoElement.srcObject = this.stream;
this.videoElement.autoplay = true;
this.videoElement.playsInline = true;
// 等待视频加载完成
await new Promise((resolve) => {
this.videoElement.onloadedmetadata = resolve;
});
return this.videoElement;
} catch (error) {
throw new Error(`摄像头初始化失败: ${error.message}`);
}
}
/**
* 停止视频流
*/
stopCamera() {
if (this.stream) {
this.stream.getTracks().forEach(track => track.stop());
this.stream = null;
}
if (this.videoElement) {
this.videoElement.srcObject = null;
this.videoElement = null;
}
}
}
// ====================== 面部检测模块 (faceDetection.js) ======================
import * as tf from '@tensorflow/tfjs';
import { FaceLandmarksDetector } from '@tensorflow-models/face-landmarks-detection';
/**
* 面部检测模块 - 使用TensorFlow.js进行面部特征检测
*/
export class FaceDetector {
constructor() {
this.model = null;
this.isModelLoaded = false;
}
/**
* 加载面部检测模型
* @returns {Promise<void>}
*/
async loadModel() {
try {
this.model = await FaceLandmarksDetector.load(
FaceLandmarksDetector.SupportedPackages.mediapipeFacemesh
);
this.isModelLoaded = true;
console.log('面部检测模型加载完成');
} catch (error) {
throw new Error(`模型加载失败: ${error.message}`);
}
}
/**
* 检测面部特征
* @param {HTMLVideoElement} video - 视频元素
* @returns {Promise<Object|null>} 面部特征数据
*/
async detectFace(video) {
if (!this.isModelLoaded) {
throw new Error('模型未加载');
}
try {
const predictions = await this.model.estimateFaces({
input: video,
returnTensors: false,
flipHorizontal: false,
predictIrises: true
});
if (predictions.length === 0) {
return null; // 未检测到面部
}
const face = predictions[0];
return {
landmarks: face.keypoints,
boundingBox: face.box,
confidence: face.confidence
};
} catch (error) {
console.error('面部检测错误:', error);
return null;
}
}
/**
* 计算眼睛闭合程度 (0-1, 1表示完全闭合)
* @param {Array} landmarks - 面部关键点
* @returns {number} 眼睛闭合程度
*/
calculateEyeClosure(landmarks) {
// 左眼关键点索引 (MediaPipe FaceMesh)
const leftEyeUpper = landmarks[159];
const leftEyeLower = landmarks[145];
const rightEyeUpper = landmarks[386];
const rightEyeLower = landmarks[374];
// 计算左右眼垂直距离
const leftEyeDist = Math.abs(leftEyeUpper.y - leftEyeLower.y);
const rightEyeDist = Math.abs(rightEyeUpper.y - rightEyeLower.y);
// 平均眼距作为基准 (经验值)
const avgEyeDist = 0.02;
// 归一化闭合程度
const leftClosure = Math.min(leftEyeDist / avgEyeDist, 1);
const rightClosure = Math.min(rightEyeDist / avgEyeDist, 1);
return (leftClosure + rightClosure) / 2;
}
/**
* 计算头部偏转角度 (度)
* @param {Array} landmarks - 面部关键点
* @returns {Object} 头部姿态 {pitch, yaw, roll}
*/
calculateHeadPose(landmarks) {
// 使用鼻尖、下巴、左右眼角计算头部姿态
const noseTip = landmarks[1];
const chin = landmarks[175];
const leftEye = landmarks[33];
const rightEye = landmarks[263];
// 简化的姿态估计 (实际应用中可使用solvePnP算法)
const dx = rightEye.x - leftEye.x;
const dy = rightEye.y - leftEye.y;
const dz = rightEye.z - leftEye.z;
const yaw = Math.atan2(dy, dx) * (180 / Math.PI);
const pitch = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) * (180 / Math.PI);
return { pitch: Math.abs(pitch), yaw: Math.abs(yaw), roll: 0 };
}
}
// ====================== 专注度计算模块 (attentionCalculator.js) ======================
/**
* 专注度计算模块 - 基于面部特征计算专注度分数
*/
export class AttentionCalculator {
constructor() {
// 专注度评估参数
this.params = {
eyeClosureThreshold: 0.3, // 眼睛闭合阈值 (超过此值视为闭眼)
headPoseThreshold: 25, // 头部偏转阈值 (度)
distractionDuration: 3000, // 分心持续时间阈值 (毫秒)
weights: {
eyeContact: 0.4, // 眼神接触权重
headStability: 0.3, // 头部稳定性权重
facialExpression: 0.3 // 面部表情权重
}
};
this.distractionStartTime = null;
this.currentAttentionScore = 100;
}
/**
* 计算实时专注度分数 (0-100)
* @param {Object} faceData - 面部特征数据
* @param {number} timestamp - 当前时间戳
* @returns {number} 专注度分数
*/
calculateAttention(faceData, timestamp) {
if (!faceData) {
// 未检测到面部,持续扣分
return Math.max(0, this.currentAttentionScore - 2);
}
const { landmarks } = faceData;
// 1. 眼神接触评估 (40%)
const eyeClosure = this.calculateEyeClosure(landmarks);
const eyeContactScore = eyeClosure < this.params.eyeClosureThreshold ? 100 :
Math.max(0, 100 - (eyeClosure - this.params.eyeClosureThreshold) * 200);
// 2. 头部稳定性评估 (30%)
const headPose = this.calculateHeadPose(landmarks);
const headStabilityScore = (headPose.pitch < this.params.headPoseThreshold &&
headPose.yaw < this.params.headPoseThreshold) ? 100 :
Math.max(0, 100 - (Math.max(headPose.pitch, headPose.yaw) - 20) * 2);
// 3. 面部表情评估 (30%) - 简化为中性表情为专注
const expressionScore = this.evaluateFacialExpression(landmarks);
// 综合计算
const attentionScore = (
eyeContactScore * this.params.weights.eyeContact +
headStabilityScore * this.params.weights.headStability +
expressionScore * this.params.weights.facialExpression
);
// 分心状态跟踪
this.trackDistraction(attentionScore < 60, timestamp);
return Math.round(Math.max(0, Math.min(100, attentionScore)));
}
/**
* 跟踪分心持续时间
* @param {boolean} isDistracted - 是否分心
* @param {number} timestamp - 当前时间戳
*/
trackDistraction(isDistracted, timestamp) {
if (isDistracted) {
if (!this.distractionStartTime) {
this.distractionStartTime = timestamp;
} else if (timestamp - this.distractionStartTime > this.params.distractionDuration) {
// 持续分心超过阈值,额外扣分
this.currentAttentionScore = Math.max(0, this.currentAttentionScore - 5);
}
} else {
this.distractionStartTime = null;
// 恢复专注,缓慢加分
this.currentAttentionScore = Math.min(100, this.currentAttentionScore + 1);
}
}
/**
* 评估面部表情 (简化版)
* @param {Array} landmarks - 面部关键点
* @returns {number} 表情分数 (0-100)
*/
evaluateFacialExpression(landmarks) {
// 实际应用中可使用表情识别模型
// 这里简化为中性表情检测 (嘴角、眉毛位置)
const mouthCornerLeft = landmarks[61];
const mouthCornerRight = landmarks[291];
const eyebrowInnerLeft = landmarks[107];
const eyebrowInnerRight = landmarks[336];
// 计算嘴角和眉毛的水平偏移
const mouthSpread = Math.abs(mouthCornerLeft.x - mouthCornerRight.x);
const browHeight = (eyebrowInnerLeft.y + eyebrowInnerRight.y) / 2;
const eyeLevel = (landmarks[33].y + landmarks[263].y) / 2;
const browElevation = Math.abs(browHeight - eyeLevel);
// 中性表情评分
let expressionScore = 80; // 基础分
if (mouthSpread > 0.1) expressionScore -= 20; // 笑或说话
if (browElevation > 0.02) expressionScore -= 15; // 惊讶或困惑
return Math.max(0, expressionScore);
}
/**
* 计算眼睛闭合程度 (内部方法)
*/
calculateEyeClosure(landmarks) {
const leftEyeUpper = landmarks[159];
const leftEyeLower = landmarks[145];
const rightEyeUpper = landmarks[386];
const rightEyeLower = landmarks[374];
const leftDist = Math.abs(leftEyeUpper.y - leftEyeLower.y);
const rightDist = Math.abs(rightEyeUpper.y - rightEyeLower.y);
const avgDist = (leftDist + rightDist) / 2;
// 归一化 (经验值)
return Math.min(avgDist / 0.025, 1);
}
/**
* 计算头部姿态 (内部方法)
*/
calculateHeadPose(landmarks) {
const noseTip = landmarks[1];
const leftEye = landmarks[33];
const rightEye = landmarks[263];
const dx = rightEye.x - leftEye.x;
const dy = rightEye.y - leftEye.y;
const dz = (rightEye.z || 0) - (leftEye.z || 0);
const yaw = Math.atan2(dy, dx) * (180 / Math.PI);
const pitch = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy)) * (180 / Math.PI);
return { pitch: Math.abs(pitch), yaw: Math.abs(yaw), roll: 0 };
}
}
// ====================== UI渲染模块 (uiRenderer.js) ======================
/**
* UI渲染模块 - 负责界面展示和交互
*/
export class UIRenderer {
constructor() {
this.container = null;
this.videoElement = null;
this.canvasElement = null;
this.statsElement = null;
this.initUI();
}
/**
* 初始化UI界面
*/
initUI() {
// 创建主容器
this.container = document.createElement('div');
this.container.style.cssText = `
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: #f0f2f5; font-family: Arial, sans-serif; z-index: 9999;
`;
// 视频容器
const videoContainer = document.createElement('div');
videoContainer.style.cssText = 'position: relative; width: 640px; margin: 20px auto;';
// 视频元素
this.videoElement = document.createElement('video');
this.videoElement.style.cssText = 'width: 100%; border-radius: 8px; background: #000;';
// 画布元素 (用于绘制检测结果)
this.canvasElement = document.createElement('canvas');
this.canvasElement.style.cssText = 'position: absolute; top: 0; left: 0; pointer-events: none;';
this.canvasElement.width = 640;
this.canvasElement.height = 480;
videoContainer.appendChild(this.videoElement);
videoContainer.appendChild(this.canvasElement);
// 统计信息面板
this.statsElement = document.createElement('div');
this.statsElement.style.cssText = `
width: 640px; margin: 10px auto; padding: 15px;
background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);
`;
// 专注度显示
this.attentionDisplay = document.createElement('div');
this.attentionDisplay.style.cssText = `
font-size: 24px; font-weight: bold; margin-bottom: 10px;
color: ${this.getAttentionColor(100)};
`;
this.attentionDisplay.textContent = '专注度: --%';
// 状态指示器
this.statusIndicator = document.createElement('div');
this.statusIndicator.style.cssText = 'font-size: 14px; color: #666;';
this.statusIndicator.textContent = '状态: 初始化中...';
this.statsElement.appendChild(this.attentionDisplay);
this.statsElement.appendChild(this.statusIndicator);
// 添加到容器
this.container.appendChild(videoContainer);
this.container.appendChild(this.statsElement);
// 添加到页面
document.body.appendChild(this.container);
}
/**
* 更新视频画面
* @param {HTMLVideoElement} video - 视频元素
*/
updateVideo(video) {
this.videoElement.srcObject = video.srcObject;
this.canvasElement.width = video.videoWidth;
this.canvasElement.height = video.videoHeight;
}
/**
* 更新专注度显示
* @param {number} score - 专注度分数 (0-100)
*/
updateAttentionScore(score) {
this.attentionDisplay.textContent = `专注度: ${score}%`;
this.attentionDisplay.style.color = this.getAttentionColor(score);
}
/**
* 更新状态信息
* @param {string} status - 状态文本
* @param {string} color - 状态颜色
*/
updateStatus(status, color = '#333') {
this.statusIndicator.textContent = `状态: ${status}`;
this.statusIndicator.style.color = color;
}
/**
* 绘制面部检测结果
* @param {Object} faceData - 面部特征数据
*/
drawFaceDetection(faceData) {
const ctx = this.canvasElement.getContext('2d');
ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
if (!faceData) return;
const { landmarks, boundingBox } = faceData;
// 绘制边界框
ctx.strokeStyle = '#00ff00';
ctx.lineWidth = 2;
ctx.strokeRect(
boundingBox.topLeft[0], boundingBox.topLeft[1],
boundingBox.bottomRight[0] - boundingBox.topLeft[0],
boundingBox.bottomRight[1] - boundingBox.topLeft[1]
);
// 绘制关键点 (简化)
ctx.fillStyle = '#ff0000';
for (let i = 0; i < Math.min(landmarks.length, 10); i++) {
const point = landmarks[i];
ctx.beginPath();
ctx.arc(point.x, point.y, 2, 0, 2 * Math.PI);
ctx.fill();
}
}
/**
* 根据专注度获取颜色
* @param {number} score - 专注度分数
* @returns {string} 颜色代码
*/
getAttentionColor(score) {
if (score >= 80) return '#28a745'; // 绿色 - 专注
if (score >= 60) return '#ffc107'; // 黄色 - 一般
return '#dc3545'; // 红色 - 不专注
}
/**
* 显示错误信息
* @param {string} message - 错误信息
*/
showError(message) {
this.updateStatus(message, '#dc3545');
}
/**
* 移除UI界面
*/
destroy() {
if (this.container && this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
}
}
// ====================== 主程序模块 (main.js) ======================
import { VideoCapture } from './videoCapture.js';
import { FaceDetector } from './faceDetection.js';
import { AttentionCalculator } from './attentionCalculator.js';
import { UIRenderer } from './uiRenderer.js';
/**
* 主程序 - 协调各模块工作
*/
class AttentionAnalysisApp {
constructor() {
this.videoCapture = new VideoCapture();
this.faceDetector = new FaceDetector();
this.attentionCalculator = new AttentionCalculator();
this.uiRenderer = new UIRenderer();
this.animationId = null;
this.startTime = Date.now();
}
/**
* 启动应用程序
*/
async start() {
try {
// 1. 初始化摄像头
this.uiRenderer.updateStatus('正在启动摄像头...', '#007bff');
const video = await this.videoCapture.initCamera();
this.uiRenderer.updateVideo(video);
// 2. 加载面部检测模型
this.uiRenderer.updateStatus('正在加载AI模型...', '#007bff');
await this.faceDetector.loadModel();
// 3. 启动实时分析循环
this.uiRenderer.updateStatus('分析中...', '#28a745');
this.startAnalysisLoop(video);
} catch (error) {
console.error('启动失败:', error);
this.uiRenderer.showError(error.message);
}
}
/**
* 启动实时分析循环
* @param {HTMLVideoElement} video - 视频元素
*/
startAnalysisLoop(video) {
const analyzeFrame = async () => {
try {
// 检测面部
const faceData = await this.faceDetector.detectFace(video);
// 计算专注度
const currentTime = Date.now();
const attentionScore = this.attentionCalculator.calculateAttention(faceData, currentTime);
// 更新UI
this.uiRenderer.updateAttentionScore(attentionScore);
this.uiRenderer.drawFaceDetection(faceData);
// 继续下一帧
this.animationId = requestAnimationFrame(analyzeFrame);
} catch (error) {
console.error('分析错误:', error);
this.uiRenderer.showError('分析过程出错');
}
};
// 开始循环
analyzeFrame();
}
/**
* 停止应用程序
*/
stop() {
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
this.videoCapture.stopCamera();
this.uiRenderer.destroy();
console.log('应用程序已停止');
}
}
// 页面加载完成后启动应用
document.addEventListener('DOMContentLoaded', () => {
const app = new AttentionAnalysisApp();
// 添加键盘快捷键 (ESC停止)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
app.stop();
}
});
// 启动应用
app.start();
});
README文件
课堂学生专注度分析系统
📖 项目简介
本项目是基于JavaScript的智能视觉技术应用,通过分析学生的面部表情和动作来评估课堂专注度。系统集成TensorFlow.js面部检测模型,实时计算专注度分数并提供可视化反馈,帮助教师及时调整教学策略。
✨ 核心功能
-
**实时视频捕获**:调用摄像头获取课堂视频流
-
**智能面部检测**:使用MediaPipe FaceMesh模型检测面部关键点
-
**专注度评估**:基于眼神接触、头部稳定性、面部表情计算专注度分数
-
**可视化反馈**:实时显示专注度分数、面部检测结果和状态信息
-
**模块化设计**:分离视频、检测、计算、UI四大核心模块
🚀 快速开始
环境要求
-
现代浏览器 (Chrome 88+, Firefox 85+, Edge 88+)
-
摄像头设备
-
网络连接 (首次加载模型需要下载)
安装依赖
bash
npm install @tensorflow/tfjs @tensorflow-models/face-landmarks-detection
使用步骤
-
将代码保存为对应模块文件 (videoCapture.js, faceDetection.js等)
-
创建index.html文件引入所有JS模块
-
在浏览器中打开index.html (建议使用本地服务器)
-
允许浏览器访问摄像头权限
-
系统自动开始分析,按ESC键停止
目录结构
project/
├── index.html # 主页面
├── main.js # 主程序入口
├── videoCapture.js # 视频捕获模块
├── faceDetection.js # 面部检测模块
├── attentionCalculator.js # 专注度计算模块
├── uiRenderer.js # UI渲染模块
└── README.md # 说明文档
📊 专注度评估算法
-
**眼神接触 (40%)**:通过眼睑关键点计算眼睛闭合程度
-
**头部稳定性 (30%)**:分析头部偏转角度 (俯仰/偏航)
-
**面部表情 (30%)**:检测中性表情,识别分心状态
-
**动态调整**:持续分心超过3秒将额外扣分
🔧 扩展建议
-
**后端集成**:添加Node.js后端存储分析数据
-
**多人检测**:扩展支持同时分析多个学生
-
**表情识别**:集成更精细的表情分类模型
-
**数据导出**:添加CSV/Excel报告生成功能
-
**移动端适配**:优化UI适应移动设备
⚠️ 注意事项
-
首次运行需要下载约10MB的AI模型文件
-
建议在光线充足的环境中使用
-
浏览器需启用摄像头权限
-
分析结果仅供参考,不应作为唯一评估标准
-
保护学生隐私,避免存储敏感图像数据
核心知识点卡片
- 智能视觉技术应用
-
定义:利用计算机视觉技术分析图像/视频中的视觉信息,实现智能化理解和决策
-
应用:面部检测、表情识别、姿态估计在专注度分析中的综合运用
-
关联代码:
"FaceDetector"类使用MediaPipe FaceMesh模型检测468个面部关键点
- 模块化开发架构
-
定义:将复杂系统分解为独立、可复用的功能模块,通过接口协作
-
优势:提高代码维护性、可测试性和团队协作效率
-
关联代码:四大模块分离设计 (视频捕获、面部检测、专注度计算、UI渲染)
- 实时数据处理技术
-
定义:通过requestAnimationFrame实现浏览器端实时视频分析和渲染
-
关键技术:帧同步、异步处理、性能优化避免卡顿
-
关联代码:
"startAnalysisLoop"方法中的递归调用机制
- 专注度评估模型
-
定义:基于多维度视觉特征的加权评分算法,量化注意力水平
-
评估维度:眼神接触(40%)、头部稳定性(30%)、面部表情(30%)
-
关联代码:
"AttentionCalculator.calculateAttention"方法中的权重计算逻辑
- TensorFlow.js模型部署
-
定义:在浏览器环境中加载和运行预训练的机器学习模型
-
技术特点:客户端推理、隐私保护、离线可用
-
关联代码:
"@tensorflow-models/face-landmarks-detection"模型加载和应用
- 用户体验设计原则
-
定义:通过实时反馈、直观可视化和非侵入式交互提升系统可用性
-
设计要素:颜色编码(红黄绿)、简洁界面、状态提示
-
关联代码:
"UIRenderer"类中的动态UI更新和视觉反馈机制
这个系统采用现代Web技术栈,模块化设计便于扩展,可直接在支持摄像头的浏览器中运行。系统注重隐私保护,所有分析均在客户端完成,不传输图像数据到服务器。
关注我,有更多实用程序等着你!