Skip to content

Commit 0eb36e5

Browse files
author
Eaglearn Developer
committed
minor patch
1 parent f2dfdca commit 0eb36e5

5 files changed

Lines changed: 60 additions & 49 deletions

File tree

README.md

Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Eaglearn adalah sistem monitoring fokus real-time untuk aktivitas belajar/kerja
1414
- Head pose (yaw/pitch/roll) + rule-based stress/confusion/drowsiness
1515
- Focus score + time tracking (focused/unfocused time)
1616
- Calibration gaze (wizard) + kompensasi head pose sederhana
17+
- Opsi pembalikan sumbu Y gaze (`eye_tracking.invert_y`) untuk device tertentu
1718
- Logging metrik sesi (JSONL) + tombol “Simpan Log”
1819
- Mode pause (privacy)
1920

@@ -43,6 +44,12 @@ Skenario pre-release beta memakai `.venv_gpu` untuk menghindari mismatch environ
4344
Aplikasi berjalan di:
4445
- Web: `http://localhost:8080`
4546

47+
Alternatif minimal:
48+
49+
```powershell
50+
.\run_app.bat
51+
```
52+
4653
## Instalasi Lengkap
4754

4855
Lihat [INSTALL.md](file:///d:/Eaglearn-Project/INSTALL.md).
@@ -52,6 +59,10 @@ Lihat [INSTALL.md](file:///d:/Eaglearn-Project/INSTALL.md).
5259
- Konfigurasi utama: [config.yaml](file:///d:/Eaglearn-Project/config.yaml)
5360
- Contoh env: [.env.example](file:///d:/Eaglearn-Project/.env.example)
5461

62+
Pengaturan penting:
63+
- Jika arah top/bottom terbalik: ubah `eye_tracking.invert_y` (default: `true`)
64+
- Threshold fokus: `focus.focused_threshold` dan `focus.distracted_threshold`
65+
5566
## API (Ringkas)
5667

5768
- `POST /api/session/start`
@@ -64,6 +75,15 @@ WebSocket:
6475
- `state_update`
6576
- `frame_update`
6677

78+
## Logging
79+
80+
File yang disimpan di folder `logs/`:
81+
- `app_YYYYMMDD.log`: log aplikasi Flask (rotating)
82+
- `metrics_<session_id>.jsonl`: snapshot metrik per ~1 detik (bisa diunduh via `GET /api/logs/metrics/download`)
83+
84+
File non-log yang relevan:
85+
- `calibrations/<user_id>.json`: data kalibrasi gaze
86+
6787
## Panduan Darurat (Troubleshooting Flow)
6888

6989
```text
@@ -93,62 +113,25 @@ Dokumen operasional VLM: [docs/VLM_OPERATIONAL.md](file:///d:/Eaglearn-Project/d
93113
- TensorFlow GPU di Windows sering tidak tersedia (gpus: `[]`), sementara PyTorch CUDA tetap bisa aktif.
94114
- VLM bersifat opsional dan membutuhkan dependency tambahan (`transformers`).
95115

96-
## Kontribusi
97-
98-
- PR dan issue diterima. Sertakan langkah reproduksi, log, dan spesifikasi perangkat.
99-
100-
## Lisensi
101-
102-
Lihat [LICENSE](file:///d:/Eaglearn-Project/LICENSE).
103-
- Check confidence threshold in config
104-
- See logs for error messages
105-
106-
## Requirements
107-
108-
### Core Dependencies
109-
110-
- Python 3.11+
111-
- Flask 3.0+
112-
- Flask-SocketIO 5.3+
113-
- OpenCV 4.8+
114-
- MediaPipe 0.10+
115-
116-
### ML Dependencies
117-
118-
- DeepFace 0.0.79+ (emotion detection)
119-
- TensorFlow 2.15+ (DeepFace backend)
120-
- PyTorch + torchvision (EfficientNet)
121-
122-
### Optional
123-
124-
- CUDA-capable GPU (for acceleration)
125-
- POSTER++ weights (for 90% accuracy emotion detection)
126-
127116
## Development
128117

129118
### Running Tests
130119

131120
```bash
132-
pytest tests/
121+
pytest -q
133122
```
134123

135124
### Code Quality
136125

137126
```bash
138-
# Format code
139-
black app.py mediapipe_processors/
140-
141-
# Lint code
142-
pylint app.py
127+
ruff check .
128+
mypy app.py improved_webcam_processor.py state_manager.py mediapipe_processors/face_mesh_processor.py
143129
```
144130

145-
## License
131+
## Kontribusi
146132

147-
See LICENSE file.
133+
- PR dan issue diterima. Sertakan langkah reproduksi, log, dan spesifikasi perangkat.
148134

149-
## Acknowledgments
135+
## Lisensi
150136

151-
- [MediaPipe](https://google.github.io/mediapipe/) - Face & pose detection
152-
- [DeepFace](https://github.com/serengil/deepface) - Emotion recognition
153-
- [PyTorch](https://pytorch.org/) - EfficientNet models
154-
- [POSTER++](https://github.com/AnnamTk/POSTER) - SOTA emotion detection (optional)
137+
Lihat [LICENSE](file:///d:/Eaglearn-Project/LICENSE).

calibration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ def calculate_calibration(self) -> Dict:
168168
"scale_factor": scale_factor,
169169
"screen_width": expected_screen_width,
170170
"screen_height": expected_screen_height,
171+
"invert_y": bool(self.config.get("eye_tracking", "invert_y", default=False)),
171172
"user_id": getattr(self, "current_user", "default"),
172173
"calibrated_at": datetime.now().isoformat(),
173174
"num_points": len(self.calibration_points),

config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ eye_tracking:
146146
gaze_scale_x: 800 # Pixels per gaze unit
147147
gaze_scale_y: 450 # Pixels per gaze unit
148148
sensitivity_threshold: 0.18 # Lower = more sensitive
149+
invert_y: true
149150

150151
# Gaze Smoothing (reduce jitter)
151152
enable_smoothing: true

improved_webcam_processor.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,19 @@ def __init__(self, state, socketio=None):
7272
try:
7373
existing_calibration = self.calibration.load_calibration("default")
7474
if isinstance(existing_calibration, dict):
75+
invert_y = bool(config.get("eye_tracking", "invert_y", default=False))
76+
calibration_invert_y = bool(existing_calibration.get("invert_y", False))
7577
with self.state.lock:
7678
self.state.calibration_applied = True
7779
self.state.calibration_gaze_offset_x = float(
7880
existing_calibration.get("gaze_offset_x", 0.0) or 0.0
7981
)
80-
self.state.calibration_gaze_offset_y = float(
82+
offset_y = float(
8183
existing_calibration.get("gaze_offset_y", 0.0) or 0.0
8284
)
85+
if invert_y and (not calibration_invert_y):
86+
offset_y = -offset_y
87+
self.state.calibration_gaze_offset_y = offset_y
8388
self.state.calibration_scale_factor = float(
8489
existing_calibration.get("scale_factor", 1.0) or 1.0
8590
)
@@ -98,13 +103,31 @@ def __init__(self, state, socketio=None):
98103
self.state.calibration_head_compensation_yaw_gain = (
99104
existing_calibration.get("head_compensation_yaw_gain", None)
100105
)
101-
self.state.calibration_head_compensation_pitch_gain = (
102-
existing_calibration.get("head_compensation_pitch_gain", None)
103-
)
106+
pitch_gain = existing_calibration.get("head_compensation_pitch_gain", None)
107+
if invert_y and (not calibration_invert_y) and pitch_gain is not None:
108+
try:
109+
pitch_gain = -float(pitch_gain)
110+
except Exception:
111+
pass
112+
self.state.calibration_head_compensation_pitch_gain = pitch_gain
104113
mapping = existing_calibration.get("screen_mapping") or {}
105114
if isinstance(mapping, dict) and "x" in mapping and "y" in mapping:
106115
self.state.calibration_screen_mapping_x = mapping.get("x")
107-
self.state.calibration_screen_mapping_y = mapping.get("y")
116+
mapping_y = mapping.get("y")
117+
if (
118+
invert_y
119+
and (not calibration_invert_y)
120+
and isinstance(mapping_y, list)
121+
and len(mapping_y) >= 3
122+
):
123+
try:
124+
ay = float(mapping_y[0])
125+
by = float(mapping_y[1])
126+
cy = float(mapping_y[2])
127+
mapping_y = [ay, -by, cy]
128+
except Exception:
129+
pass
130+
self.state.calibration_screen_mapping_y = mapping_y
108131
except Exception:
109132
pass
110133

mediapipe_processors/face_mesh_processor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ def _extract_face_metrics(self, landmarks, state):
472472
else 0.0
473473
)
474474

475+
if bool(self.config.get("eye_tracking", "invert_y", default=False)):
476+
raw_gaze_y = -raw_gaze_y
477+
475478
if hasattr(state, "calibration_applied") and state.calibration_applied:
476479
try:
477480
base_yaw = float(getattr(state, "calibration_head_yaw", 0.0) or 0.0)

0 commit comments

Comments
 (0)