Watch every camera at once, miss nothing — live detection, structured incidents, and instant escalation.
EyeNet.mp4
You know that feeling? One guard staring at 8 camera feeds, alerts over phone calls / WhatsApp with zero traceability, and every one-frame false positive becomes a panic. EyeNet fixes that.
EyeNet is three small pieces that work together:
- A detection pipeline (
src/core/pipeline.py) — shared frame buffer, face + uniform + YOLOv8 hazard detectors, ByteTrack tracking. - An event bus + store (
src/core/event_bus.py,src/core/database.py) — priority queue, anomaly scoring, cooldowns, SQLite WAL persistence. - A Flask dashboard (
src/dashboard/app.py) — MJPEG video, SSE live alerts, history + ACK, hourly analytics, Twilio / SMTP escalation.
The trick is simple: capture once in SharedFrameBuffer and share it across threads. Every detection is tracked for 5 frames before it becomes an event, scored 0-100, then gated by per-severity cooldowns before it hits the DB + dashboard + SMS/email. So the dashboard only ever shows persistent, deduplicated incidents with snapshots.
Think CCTV DVR, but it actually watches back.
Humans can't monitor many cameras continuously — one leak / intruder / fire missed is everything — grep-ing a log file finds text, not evidence, and raw YOLO boxes without tracking mean alert spam, so teams fall back to ignoring the monitor wall.
- Shared primitives: one
SharedFrameBuffer, same frame for pipeline + MJPEG stream, no fighting over/dev/video0. - Faster response: detect once in
pipeline.py, reuse everywhere viaDetectionEventinstead of re-running models per consumer. - Stay in flow: severity-sorted SSE feed, filter by type/severity, snapshot thumbnails, ACK inline without tab-hopping.
No hosted demo — just Python and a webcam. For local dev, have Twilio / SMTP ready only if you want SMS/email.
Drop enrollment photos in students/ named by roll number, then build encodings:
python -m src.encoders.encode_faces
# students/22102027.jpg -> models/face_encodings.pklThe runtime matches with L2 distance against FACE_DISTANCE_THRESHOLD. Unknown faces become MEDIUM events.
python -m src.main
# Dashboard: http://localhost:5000
# login: admin / admin123 (only if you did NOT set ADMIN_PASSWORD_HASH)src/main.py wires it all: init_db() → SharedFrameBuffer.start() → EventBus(num_workers=2) → DetectionPipeline.start() → Flask in a thread + local cv2.imshow view. Press q to stop.
- Stay on
/→ live MJPEG + SSE alerts appear with snapshots fromdata/anomalies/ - Filter by type/severity →
GET /api/alerts?type=hazard&severity=3 - ACK what you handled →
PATCH /api/alerts/<id>/ack - Check health →
GET /api/metrics/latestpolls every 3s,GET /api/stats/hourlyfor last 24h
src/notifications/— SMS forunknown_face/hazard(score >= 25), email foruniform_violationto<roll>@mail.jiit.ac.in- No orgs / roles yet — one
adminuser, session-cookie auth (SECRET_KEY+ bcryptADMIN_PASSWORD_HASH) - No auto-delete — snapshots stay in
data/anomalies/, metrics older than 24h are pruned
See ## Usage below for the daily loop. Want Docker instead? See ## Contributing.
Available pages (auth required via login_required + Flask session):
/login— fixedadminusername, bcrypt verify againstADMIN_PASSWORD_HASH, falls back toadmin123/— ops dashboard, MJPEG + SSE + filters + ACK + metrics charts/video_feed— MJPEG stream, shared buffer when run viasrc/main.py, opens camera directly standalone/live_alerts— SSE stream, pipeline pushes viapush_event(event.to_dict())/api/alerts—GETlist?type=&severity=&since=&limit=(default 50, cap 500)/api/alerts/<id>/ack—PATCHacknowledge →{ "status": "ok" }/api/metrics/latest— latest{ fps, detection_count, face_count, processing_ms }/api/stats/hourly— counts per hour, last 24h
Behavior notes:
- Severity by construction —
CRITICAL: gun/fire/bomb/explosive,HIGH: knife/smoke/axe/sword,MEDIUM: unknown face,LOW: uniform + rest (DetectionPipeline._classify_severity). - Urgency model —
compute_anomaly_score()0-100 from base weight + confidence + track persistence, stored inmetadata_json. - All
/api/*routes require login session (SESSION_LIFETIME_HOURS, default 8). - Cooldowns stop spam —
CRITICAL: 1m,HIGH: 5m,MEDIUM: 10m,LOW: 24h + 5m local SMS cooldown per subtype.
Note
Snapshots are evidence. Every persisted alert stores image_path in data/anomalies/. ACK doesn't delete — query history via /api/alerts.
Catch an unknown visitor:
camera -> face_detector (0.5x downscaled) -> no match < FACE_DISTANCE_THRESHOLD
-> track 5 frames -> MEDIUM unknown_face, score 0-100
-> cooldown 10m -> insert_alert + SSE + Twilio SMS if score >= 25
-> appears in dashboard, filter type=unknown_face, ACK when handled
Confirm a hazard without false alarms:
YOLO yolov8m.pt -> keyword filter (knife/gun/fire/smoke/...) + area + per-class conf
-> HAZARD_CONSECUTIVE_FRAMES=5 -> track persists -> HIGH/CRITICAL
-> cooldown 5m/1m -> SMS to ADMIN_PHONE_NUMBERS
Enforce uniform compliance:
known face -> torso ROI -> HSV light-blue check
-> blue ratio low -> LOW uniform_violation
-> cooldown 24h -> SMTP to <roll>@mail.jiit.ac.in (fine Rs. 700)
src/main.py -> entrypoint: DB + buffer + bus + pipeline + dashboard
src/config.py -> central config loaded from .env
src/core/frame_buffer.py -> SharedFrameBuffer (one writer, many readers)
src/core/pipeline.py -> orchestrates face/uniform/hazard detection
src/core/tracker.py -> ByteTrack wrapper (or fallback IDs)
src/core/event_bus.py -> priority event bus with worker threads
src/core/cooldown.py -> per-severity cooldown policy
src/core/anomaly_scorer.py -> 0-100 urgency score
src/core/database.py -> SQLite schema + queries + WAL
src/core/logging_config.py -> rotating logs to file + console
src/detectors/face_detector.py -> face-recognition matching to encodings
src/detectors/uniform_detector.py -> HSV torso check
src/detectors/anomaly_detector.py -> YOLO hazard + temporal filtering
src/dashboard/app.py -> Flask UI + SSE + REST API
src/dashboard/auth.py -> bcrypt hash/verify, python -m src.dashboard.auth
src/dashboard/templates/ -> dashboard, login HTML
src/notifications/sms_sender.py -> Twilio SMS alerts
src/notifications/email_sender.py -> SMTP email alerts
src/encoders/encode_faces.py -> build models/face_encodings.pkl from students/
scripts/migrate_json_to_db.py -> migrate legacy alerts.json into SQLite
docker-compose.yml -> backend :5001 + frontend :5000 + /dev/video0 passthrough
Dockerfile.backend -> full app image (python:3.9-slim-bullseye)
Dockerfile.frontend -> dashboard-only image
requirements.txt -> Flask, opencv, ultralytics, face-recognition, twilio
.env.example -> camera + tuning + Twilio + SMTP + auth + DB + logging
students/ -> enrollment photos named by roll number
models/face_encodings.pkl -> generated face encodings artifact
Skipping the deep dive here on purpose. For security model, SQLite schema, session flow, detection tuning, challenges, and deployment — checkout Technical_Documentation.md.
git clone https://github.com/SplinterSword/EyeNet.git
cd EyeNetPrereqs: Python 3.9+ recommended, webcam (CAMERA_SOURCE=0), optional Twilio account + SMTP creds.
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# fill in SECRET_KEY + ADMIN_PASSWORD_HASH, Twilio/SMTP if needed
python -m src.encoders.encode_faces
python -m src.mainCondensed env — full table lives in Technical_Documentation.md:
# .env
CAMERA_SOURCE=0
FRAME_WIDTH=640
FRAME_HEIGHT=480
TARGET_FPS=15
FACE_DISTANCE_THRESHOLD=0.55
HAZARD_CONSECUTIVE_FRAMES=5
HAZARD_CONF_THRESHOLD=0.6
YOLO_HAZARD_MODEL=yolov8m.pt
YOLO_PERSON_MODEL=yolov8n.pt
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=
ADMIN_PHONE_NUMBERS=
SMTP_EMAIL=
SMTP_PASSWORD=
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SECRET_KEY=
ADMIN_PASSWORD_HASH=
SESSION_LIFETIME_HOURS=8
DB_PATH=data/eyenet.db
LOG_LEVEL=INFO
LOG_FILE=data/logs/eyenet.logGenerate admin hash with:
python -m src.dashboard.auth "your-password"Starts Flask on http://localhost:5000. Enroll a test face first — unknown-face SMS only fires if encodings exist to compare against.
Docker instead:
docker compose up --build
# backend -> http://localhost:5001, frontend -> http://localhost:5000
# requires Linux host for /dev/video0 passthrough + privileged: truepython -m compileall src
docker compose up --build # container bundle check
python scripts/migrate_json_to_db.py # only if migrating legacy data/logs/alerts.jsonFork the repo and open a PR to main. Keep it scoped — one feature / fix per PR.