开发课堂学生专注度分析程序,捕捉学生面部表情和动作,分析专注程度,帮助老师调整教学。

27 阅读9分钟

我将按照代码生成场景准则,为你构建一个基于JavaScript的课堂学生专注度分析程序。这个系统采用模块化设计,结合智能视觉技术实现实时专注度评估。

// ====================== 视频捕获模块 (videoCapture.js) ====================== /**

  • 视频捕获模块 - 负责获取摄像头视频流 */ export class VideoCapture { constructor() { this.videoElement = null; this.stream = null; }

/**

  • 初始化摄像头

  • @returns {Promise} 视频元素 */ 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} */ 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面部检测模型,实时计算专注度分数并提供可视化反馈,帮助教师及时调整教学策略。

✨ 核心功能

  1. 实时视频捕获:调用摄像头获取课堂视频流
  2. 智能面部检测:使用MediaPipe FaceMesh模型检测面部关键点
  3. 专注度评估:基于眼神接触、头部稳定性、面部表情计算专注度分数
  4. 可视化反馈:实时显示专注度分数、面部检测结果和状态信息
  5. 模块化设计:分离视频、检测、计算、UI四大核心模块

🚀 快速开始

环境要求

  • 现代浏览器 (Chrome 88+, Firefox 85+, Edge 88+)
  • 摄像头设备
  • 网络连接 (首次加载模型需要下载)

安装依赖

bash

npm install @tensorflow/tfjs @tensorflow-models/face-landmarks-detection

使用步骤

  1. 将代码保存为对应模块文件 (videoCapture.js, faceDetection.js等)
  2. 创建index.html文件引入所有JS模块
  3. 在浏览器中打开index.html (建议使用本地服务器)
  4. 允许浏览器访问摄像头权限
  5. 系统自动开始分析,按ESC键停止

目录结构

project/

├── index.html # 主页面

├── main.js # 主程序入口

├── videoCapture.js # 视频捕获模块

├── faceDetection.js # 面部检测模块

├── attentionCalculator.js # 专注度计算模块

├── uiRenderer.js # UI渲染模块

└── README.md # 说明文档

📊 专注度评估算法

  • 眼神接触 (40%):通过眼睑关键点计算眼睛闭合程度
  • 头部稳定性 (30%):分析头部偏转角度 (俯仰/偏航)
  • 面部表情 (30%):检测中性表情,识别分心状态
  • 动态调整:持续分心超过3秒将额外扣分

🔧 扩展建议

  1. 后端集成:添加Node.js后端存储分析数据
  2. 多人检测:扩展支持同时分析多个学生
  3. 表情识别:集成更精细的表情分类模型
  4. 数据导出:添加CSV/Excel报告生成功能
  5. 移动端适配:优化UI适应移动设备

⚠️ 注意事项

  • 首次运行需要下载约10MB的AI模型文件
  • 建议在光线充足的环境中使用
  • 浏览器需启用摄像头权限
  • 分析结果仅供参考,不应作为唯一评估标准
  • 保护学生隐私,避免存储敏感图像数据

核心知识点卡片

  1. 智能视觉技术应用
  • 定义:利用计算机视觉技术分析图像/视频中的视觉信息,实现智能化理解和决策
  • 应用:面部检测、表情识别、姿态估计在专注度分析中的综合运用
  • 关联代码: "FaceDetector"类使用MediaPipe FaceMesh模型检测468个面部关键点
  1. 模块化开发架构
  • 定义:将复杂系统分解为独立、可复用的功能模块,通过接口协作
  • 优势:提高代码维护性、可测试性和团队协作效率
  • 关联代码:四大模块分离设计 (视频捕获、面部检测、专注度计算、UI渲染)
  1. 实时数据处理技术
  • 定义:通过requestAnimationFrame实现浏览器端实时视频分析和渲染
  • 关键技术:帧同步、异步处理、性能优化避免卡顿
  • 关联代码: "startAnalysisLoop"方法中的递归调用机制
  1. 专注度评估模型
  • 定义:基于多维度视觉特征的加权评分算法,量化注意力水平
  • 评估维度:眼神接触(40%)、头部稳定性(30%)、面部表情(30%)
  • 关联代码: "AttentionCalculator.calculateAttention"方法中的权重计算逻辑
  1. TensorFlow.js模型部署
  • 定义:在浏览器环境中加载和运行预训练的机器学习模型
  • 技术特点:客户端推理、隐私保护、离线可用
  • 关联代码: "@tensorflow-models/face-landmarks-detection"模型加载和应用
  1. 用户体验设计原则
  • 定义:通过实时反馈、直观可视化和非侵入式交互提升系统可用性
  • 设计要素:颜色编码(红黄绿)、简洁界面、状态提示
  • 关联代码: "UIRenderer"类中的动态UI更新和视觉反馈机制

这个系统采用现代Web技术栈,模块化设计便于扩展,可直接在支持摄像头的浏览器中运行。系统注重隐私保护,所有分析均在客户端完成,不传输图像数据到服务器。 专注我,有更多实用程序等着你!