Spaces:
Sleeping
Sleeping
Commit Β·
518de6c
1
Parent(s): 0616f67
fix: hybird not functioning. Change back from xgboost+geo to MLP+geo for hybird
Browse files
checkpoints/hybrid_focus_config.json
CHANGED
|
@@ -1,14 +1,10 @@
|
|
| 1 |
{
|
| 2 |
-
"use_xgb":
|
| 3 |
"w_mlp": 0.3,
|
| 4 |
-
"w_xgb": 0.3,
|
| 5 |
"w_geo": 0.7,
|
| 6 |
-
"threshold": 0.
|
| 7 |
"use_yawn_veto": true,
|
| 8 |
"geo_face_weight": 0.7,
|
| 9 |
"geo_eye_weight": 0.3,
|
| 10 |
-
"mar_yawn_threshold": 0.55
|
| 11 |
-
"metric": "f1",
|
| 12 |
-
"combiner": "logistic",
|
| 13 |
-
"combiner_path": "hybrid_combiner.joblib"
|
| 14 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"use_xgb": false,
|
| 3 |
"w_mlp": 0.3,
|
|
|
|
| 4 |
"w_geo": 0.7,
|
| 5 |
+
"threshold": 0.35,
|
| 6 |
"use_yawn_veto": true,
|
| 7 |
"geo_face_weight": 0.7,
|
| 8 |
"geo_eye_weight": 0.3,
|
| 9 |
+
"mar_yawn_threshold": 0.55
|
|
|
|
|
|
|
|
|
|
| 10 |
}
|
src/components/FocusPageLocal.jsx
CHANGED
|
@@ -43,14 +43,14 @@ const MODEL_INFO = {
|
|
| 43 |
hybrid: {
|
| 44 |
label: 'Hybrid',
|
| 45 |
tagline: 'Best overall β combines ML with geometric scoring',
|
| 46 |
-
how: 'Fuses
|
| 47 |
accuracy: 'N/A',
|
| 48 |
f1: '0.8409',
|
| 49 |
auc: 'N/A',
|
| 50 |
-
threshold: '0.
|
| 51 |
evaluation: 'LOPO tuning (9 participants, 144K frames)',
|
| 52 |
features: '10 features: head deviation, face score, eye scores (EAR), gaze offset, pitch, horizontal gaze, PERCLOS',
|
| 53 |
-
strengths: 'Most robust across different people.
|
| 54 |
badge: 'Recommended',
|
| 55 |
},
|
| 56 |
xgboost: {
|
|
@@ -109,10 +109,6 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
|
|
| 109 |
const [calibration, setCalibration] = useState(null);
|
| 110 |
const [l2csBoost, setL2csBoost] = useState(false);
|
| 111 |
const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
|
| 112 |
-
const [showEyeGazeModal, setShowEyeGazeModal] = useState(false);
|
| 113 |
-
const [eyeGazeDontShow, setEyeGazeDontShow] = useState(
|
| 114 |
-
() => localStorage.getItem('focusguard_eyegaze_noshowalert') === 'true'
|
| 115 |
-
);
|
| 116 |
|
| 117 |
const localVideoRef = useRef(null);
|
| 118 |
const displayCanvasRef = useRef(null);
|
|
@@ -331,20 +327,7 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
|
|
| 331 |
|
| 332 |
const handleEyeGazeToggle = async () => {
|
| 333 |
const next = !l2csBoost;
|
| 334 |
-
|
| 335 |
-
// Show the warning/calibration modal before enabling
|
| 336 |
-
setShowEyeGazeModal(true);
|
| 337 |
-
return;
|
| 338 |
-
}
|
| 339 |
-
await applyEyeGazeChange(next, true);
|
| 340 |
-
};
|
| 341 |
-
|
| 342 |
-
const handleEyeGazeModalAction = async (withCalibration) => {
|
| 343 |
-
if (eyeGazeDontShow) {
|
| 344 |
-
localStorage.setItem('focusguard_eyegaze_noshowalert', 'true');
|
| 345 |
-
}
|
| 346 |
-
setShowEyeGazeModal(false);
|
| 347 |
-
await applyEyeGazeChange(true, withCalibration);
|
| 348 |
};
|
| 349 |
|
| 350 |
const handleStart = async () => {
|
|
@@ -700,91 +683,9 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
|
|
| 700 |
return null;
|
| 701 |
};
|
| 702 |
|
| 703 |
-
const renderEyeGazeModal = () => {
|
| 704 |
-
if (!showEyeGazeModal) return null;
|
| 705 |
-
return (
|
| 706 |
-
<div className="focus-flow-overlay" style={{ zIndex: 2000 }}>
|
| 707 |
-
<div className="focus-flow-card">
|
| 708 |
-
<div className="focus-flow-header">
|
| 709 |
-
<div>
|
| 710 |
-
<div className="focus-flow-eyebrow">Eye Gaze Tracking</div>
|
| 711 |
-
<h2>Before you enable</h2>
|
| 712 |
-
</div>
|
| 713 |
-
<div className="focus-flow-icon">
|
| 714 |
-
<svg width="96" height="96" viewBox="0 0 96 96" aria-hidden="true">
|
| 715 |
-
<ellipse cx="48" cy="48" rx="38" ry="24" fill="none" stroke="#007BFF" strokeWidth="5" />
|
| 716 |
-
<circle cx="48" cy="48" r="13" fill="none" stroke="#007BFF" strokeWidth="5" />
|
| 717 |
-
<circle cx="48" cy="48" r="5" fill="#007BFF" />
|
| 718 |
-
</svg>
|
| 719 |
-
</div>
|
| 720 |
-
</div>
|
| 721 |
-
|
| 722 |
-
<p className="focus-flow-lead">
|
| 723 |
-
Eye gaze tracking runs an additional deep neural network (L2CS-Net) alongside your current model.
|
| 724 |
-
Please read the notes below before proceeding.
|
| 725 |
-
</p>
|
| 726 |
-
|
| 727 |
-
<div className="focus-flow-grid">
|
| 728 |
-
<article className="focus-flow-panel focus-flow-panel-warn">
|
| 729 |
-
<h3>Performance impact</h3>
|
| 730 |
-
<p>Enabling eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, consider disabling it.</p>
|
| 731 |
-
</article>
|
| 732 |
-
<article className="focus-flow-panel">
|
| 733 |
-
<h3>Calibration (recommended)</h3>
|
| 734 |
-
<p>For best accuracy, calibrate by looking at 9 screen positions one at a time, followed by 1 validation point. The whole process takes about 30 seconds.</p>
|
| 735 |
-
</article>
|
| 736 |
-
</div>
|
| 737 |
-
|
| 738 |
-
<div className="focus-flow-steps">
|
| 739 |
-
<div className="focus-flow-step">
|
| 740 |
-
<div className="focus-flow-step-number">1</div>
|
| 741 |
-
<div className="focus-flow-step-copy">
|
| 742 |
-
<h3>Click "Start Calibration"</h3>
|
| 743 |
-
<p>A dot will appear on screen. Look directly at it and keep your gaze steady. It will cycle through 9 positions then show a final validation dot.</p>
|
| 744 |
-
</div>
|
| 745 |
-
</div>
|
| 746 |
-
<div className="focus-flow-step">
|
| 747 |
-
<div className="focus-flow-step-number">2</div>
|
| 748 |
-
<div className="focus-flow-step-copy">
|
| 749 |
-
<h3>Or skip for now</h3>
|
| 750 |
-
<p>Click "Skip" to enable eye gaze tracking without calibrating. You can recalibrate at any time using the "Recalibrate" button during a session.</p>
|
| 751 |
-
</div>
|
| 752 |
-
</div>
|
| 753 |
-
</div>
|
| 754 |
-
|
| 755 |
-
<label className="eye-gaze-modal-checkbox">
|
| 756 |
-
<input
|
| 757 |
-
type="checkbox"
|
| 758 |
-
checked={eyeGazeDontShow}
|
| 759 |
-
onChange={(e) => setEyeGazeDontShow(e.target.checked)}
|
| 760 |
-
/>
|
| 761 |
-
Don't show this again
|
| 762 |
-
</label>
|
| 763 |
-
|
| 764 |
-
<div className="focus-flow-footer">
|
| 765 |
-
<button
|
| 766 |
-
type="button"
|
| 767 |
-
className="focus-flow-secondary"
|
| 768 |
-
onClick={() => handleEyeGazeModalAction(false)}
|
| 769 |
-
>
|
| 770 |
-
Skip
|
| 771 |
-
</button>
|
| 772 |
-
<button
|
| 773 |
-
className="focus-flow-button"
|
| 774 |
-
onClick={() => handleEyeGazeModalAction(true)}
|
| 775 |
-
>
|
| 776 |
-
Start Calibration
|
| 777 |
-
</button>
|
| 778 |
-
</div>
|
| 779 |
-
</div>
|
| 780 |
-
</div>
|
| 781 |
-
);
|
| 782 |
-
};
|
| 783 |
-
|
| 784 |
return (
|
| 785 |
<main id="page-b" className="page" style={pageStyle}>
|
| 786 |
{renderIntroCard()}
|
| 787 |
-
{renderEyeGazeModal()}
|
| 788 |
|
| 789 |
<section id="display-area" className="focus-display-shell">
|
| 790 |
<video
|
|
|
|
| 43 |
hybrid: {
|
| 44 |
label: 'Hybrid',
|
| 45 |
tagline: 'Best overall β combines ML with geometric scoring',
|
| 46 |
+
how: 'Fuses MLP predictions (30%) with geometric face/eye scores (70%). Uses a weighted blend tuned with LOPO evaluation.',
|
| 47 |
accuracy: 'N/A',
|
| 48 |
f1: '0.8409',
|
| 49 |
auc: 'N/A',
|
| 50 |
+
threshold: '0.35',
|
| 51 |
evaluation: 'LOPO tuning (9 participants, 144K frames)',
|
| 52 |
features: '10 features: head deviation, face score, eye scores (EAR), gaze offset, pitch, horizontal gaze, PERCLOS',
|
| 53 |
+
strengths: 'Most robust across different people. LOPO mean F1 is 0.8409 at w_mlp=0.3, w_geo=0.7.',
|
| 54 |
badge: 'Recommended',
|
| 55 |
},
|
| 56 |
xgboost: {
|
|
|
|
| 109 |
const [calibration, setCalibration] = useState(null);
|
| 110 |
const [l2csBoost, setL2csBoost] = useState(false);
|
| 111 |
const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
const localVideoRef = useRef(null);
|
| 114 |
const displayCanvasRef = useRef(null);
|
|
|
|
| 327 |
|
| 328 |
const handleEyeGazeToggle = async () => {
|
| 329 |
const next = !l2csBoost;
|
| 330 |
+
await applyEyeGazeChange(next, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
};
|
| 332 |
|
| 333 |
const handleStart = async () => {
|
|
|
|
| 683 |
return null;
|
| 684 |
};
|
| 685 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 686 |
return (
|
| 687 |
<main id="page-b" className="page" style={pageStyle}>
|
| 688 |
{renderIntroCard()}
|
|
|
|
| 689 |
|
| 690 |
<section id="display-area" className="focus-display-shell">
|
| 691 |
<video
|
src/components/Help.jsx
CHANGED
|
@@ -52,7 +52,7 @@ function Help() {
|
|
| 52 |
|
| 53 |
<section className="help-section">
|
| 54 |
<h2>Available Models</h2>
|
| 55 |
-
<p><strong>Hybrid</strong> <em>(Recommended)</em>:
|
| 56 |
<p><strong>XGBoost:</strong> Gradient-boosted tree model using 10 selected features. Highest raw accuracy (95.87% pooled, LOPO AUC 0.8695). Strong on tabular data with fast inference.</p>
|
| 57 |
<p><strong>MLP:</strong> Two-layer neural network (10β64β32 neurons) trained with PyTorch. Good balance of speed and accuracy (92.92% pooled, LOPO AUC 0.8624). Fastest inference.</p>
|
| 58 |
<p><strong>Geometric:</strong> Rule-based scoring using head pose and eye openness. No ML model needed β lightweight fallback when model checkpoints are unavailable. LOPO F1: 0.8195.</p>
|
|
@@ -66,13 +66,13 @@ function Help() {
|
|
| 66 |
<p>The <strong>Eye Gaze</strong> button enables L2CS-Net, a deep neural network that estimates your gaze direction from the eye region. It runs alongside your selected base model and can improve focus detection accuracy.</p>
|
| 67 |
<p style={{ marginTop: '8px' }}><strong>Performance note:</strong> Eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, disable it.</p>
|
| 68 |
<h3 style={{ marginTop: '14px', fontSize: '1rem' }}>Calibration</h3>
|
| 69 |
-
<p>For best accuracy, calibrate when
|
| 70 |
<ol>
|
| 71 |
-
<li>
|
| 72 |
<li>Look directly at each dot as it appears on screen β there are <strong>9 calibration points</strong> across the screen</li>
|
| 73 |
<li>A final <strong>validation point</strong> confirms accuracy before calibration is applied</li>
|
| 74 |
</ol>
|
| 75 |
-
<p>You can
|
| 76 |
</section>
|
| 77 |
|
| 78 |
<section className="help-section">
|
|
@@ -134,8 +134,8 @@ function Help() {
|
|
| 134 |
<p>The face mesh overlay updates each time the server returns a detection result. The camera feed itself renders locally. Any visible lag depends on network latency and server processing time. Reducing the frame rate slider can help if lag is noticeable.</p>
|
| 135 |
</details>
|
| 136 |
<details>
|
| 137 |
-
<summary>The Hybrid model doesn't seem to work differently from
|
| 138 |
-
<p>The Hybrid model
|
| 139 |
</details>
|
| 140 |
</section>
|
| 141 |
|
|
@@ -143,7 +143,7 @@ function Help() {
|
|
| 143 |
<h2>Technical Info</h2>
|
| 144 |
<p><strong>Face Detection:</strong> MediaPipe Face Mesh (478 landmarks)</p>
|
| 145 |
<p><strong>Feature Extraction:</strong> Head pose (yaw/pitch/roll), EAR, MAR, gaze offset, PERCLOS, blink rate β 10 features selected via LOFO analysis</p>
|
| 146 |
-
<p><strong>ML Models:</strong> PyTorch MLP (10β64β32β2), XGBoost (600 trees), Geometric (rule-based), Hybrid (
|
| 147 |
<p><strong>Eye Gaze:</strong> L2CS-Net (ResNet50 backbone, trained on Gaze360) with 9-point polynomial calibration</p>
|
| 148 |
<p><strong>Storage:</strong> SQLite database (sessions, events, settings)</p>
|
| 149 |
<p><strong>Framework:</strong> FastAPI + React (Vite) + WebSocket</p>
|
|
|
|
| 52 |
|
| 53 |
<section className="help-section">
|
| 54 |
<h2>Available Models</h2>
|
| 55 |
+
<p><strong>Hybrid</strong> <em>(Recommended)</em>: Blends MLP predictions (30%) with geometric face/eye scoring (70%) using a weighted average tuned with LOPO evaluation. Most robust across different people. LOPO F1: 0.8409.</p>
|
| 56 |
<p><strong>XGBoost:</strong> Gradient-boosted tree model using 10 selected features. Highest raw accuracy (95.87% pooled, LOPO AUC 0.8695). Strong on tabular data with fast inference.</p>
|
| 57 |
<p><strong>MLP:</strong> Two-layer neural network (10β64β32 neurons) trained with PyTorch. Good balance of speed and accuracy (92.92% pooled, LOPO AUC 0.8624). Fastest inference.</p>
|
| 58 |
<p><strong>Geometric:</strong> Rule-based scoring using head pose and eye openness. No ML model needed β lightweight fallback when model checkpoints are unavailable. LOPO F1: 0.8195.</p>
|
|
|
|
| 66 |
<p>The <strong>Eye Gaze</strong> button enables L2CS-Net, a deep neural network that estimates your gaze direction from the eye region. It runs alongside your selected base model and can improve focus detection accuracy.</p>
|
| 67 |
<p style={{ marginTop: '8px' }}><strong>Performance note:</strong> Eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, disable it.</p>
|
| 68 |
<h3 style={{ marginTop: '14px', fontSize: '1rem' }}>Calibration</h3>
|
| 69 |
+
<p>For best accuracy, calibrate when Eye Gaze is active:</p>
|
| 70 |
<ol>
|
| 71 |
+
<li>Start a session, then click the <strong>"Recalibrate"</strong> button that appears in the model strip when Eye Gaze is on</li>
|
| 72 |
<li>Look directly at each dot as it appears on screen β there are <strong>9 calibration points</strong> across the screen</li>
|
| 73 |
<li>A final <strong>validation point</strong> confirms accuracy before calibration is applied</li>
|
| 74 |
</ol>
|
| 75 |
+
<p>You can recalibrate at any time using the "Recalibrate" button, which appears in the model strip when Eye Gaze is on and a session is running.</p>
|
| 76 |
</section>
|
| 77 |
|
| 78 |
<section className="help-section">
|
|
|
|
| 134 |
<p>The face mesh overlay updates each time the server returns a detection result. The camera feed itself renders locally. Any visible lag depends on network latency and server processing time. Reducing the frame rate slider can help if lag is noticeable.</p>
|
| 135 |
</details>
|
| 136 |
<details>
|
| 137 |
+
<summary>The Hybrid model doesn't seem to work differently from MLP β why?</summary>
|
| 138 |
+
<p>The Hybrid model blends MLP (30%) and geometric (70%) scores using a fixed weighted average. Because its geometric component dominates, it tends to be more conservative than raw MLP β especially when head pose or eye openness signals are borderline. It is tuned to be the most consistent across different people rather than the most aggressive.</p>
|
| 139 |
</details>
|
| 140 |
</section>
|
| 141 |
|
|
|
|
| 143 |
<h2>Technical Info</h2>
|
| 144 |
<p><strong>Face Detection:</strong> MediaPipe Face Mesh (478 landmarks)</p>
|
| 145 |
<p><strong>Feature Extraction:</strong> Head pose (yaw/pitch/roll), EAR, MAR, gaze offset, PERCLOS, blink rate β 10 features selected via LOFO analysis</p>
|
| 146 |
+
<p><strong>ML Models:</strong> PyTorch MLP (10β64β32β2), XGBoost (600 trees), Geometric (rule-based), Hybrid (MLP 30% + Geometric 70% weighted blend)</p>
|
| 147 |
<p><strong>Eye Gaze:</strong> L2CS-Net (ResNet50 backbone, trained on Gaze360) with 9-point polynomial calibration</p>
|
| 148 |
<p><strong>Storage:</strong> SQLite database (sessions, events, settings)</p>
|
| 149 |
<p><strong>Framework:</strong> FastAPI + React (Vite) + WebSocket</p>
|