Local classroom engagement monitor. Runs entirely on your laptop — no cloud AI, no student accounts, no face database. Just a webcam, a CV pipeline, and a dashboard.
- Detection: YOLO26n (Ultralytics) — person + phone detection, picked for fastest CPU inference in the YOLO26 family
- Drowsiness: MediaPipe FaceMesh — eye aspect ratio (EAR) per face
- Backend: FastAPI + SQLite
- Frontend: plain HTML/CSS/JS (3 screens)
opencv-python and MediaPipe link against system OpenGL/video libraries
that aren't always present on a fresh Linux install. On Debian/Ubuntu:
sudo apt update
sudo apt install -y python3-venv python3-dev libgl1 libglib2.0-0 v4l-utilslibgl1/libglib2.0-0— without these you'll hit an import error likeImportError: libGL.so.1: cannot open shared object file.v4l-utils— gives youv4l2-ctl --list-devicesto confirm your webcam's index/path before running the app.
Make sure your user can access the camera device:
groups $USER # check whether 'video' is listed
sudo usermod -aG video $USER # if not — then log out and back inpython -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txtFirst run will auto-download yolo26n.pt (~a few MB) from Ultralytics.
uvicorn main:app --reloadOpen http://localhost:8000 in Chrome and allow webcam access.
If the webcam doesn't open, or opens the wrong camera (common on laptops
with more than one /dev/video* node, e.g. an IR camera or a virtual one
from OBS), find the right index with v4l2-ctl --list-devices and set it
explicitly:
CAMERA_INDEX=1 uvicorn main:app --reload- Click Start class → backend opens the webcam and starts the CV loop in a background thread.
- Each frame: YOLO26n finds people and phones; MediaPipe finds faces and computes eye-openness per face.
- A face is marked:
- Drowsy if eyes stay closed for ~15 consecutive frames
- Distracted if a detected phone box overlaps that face's region
- Engaged otherwise
- Live counts stream to the dashboard every second via
/api/stats. - On a new disengagement (with a 10s cooldown per reason), a snapshot is
saved to
snapshots/and logged inattensense.db— viewable on the Disengaged students screen.
EAR_THRESHOLDinmain.py(default0.21) — lower if it fires too easily, raise if it misses closed eyes. Test against your own webcam lighting before the demo.DROWSY_FRAMES(default15, ~0.5s) — raise for fewer false positives.- If CPU inference feels slow, downscale the frame before passing to YOLO
(
cv2.resize(frame, (640, 480))) — accuracy trade-off, but AttenSense doesn't need pixel-perfect boxes, just presence/absence.
- One camera = one "room" view; it doesn't re-identify specific students across frames, only faces present in the current frame.
- Lighting and camera angle affect EAR accuracy — recalibrate the threshold in your actual demo room beforehand.