EyeNet is a camera-first, real-time surveillance system intended for campus / institutional environments. It continuously processes a live video feed and converts computer-vision detections into actionable incidents: persisted audit records, real-time dashboards, and automated notifications.
At its core, EyeNet is not “just object detection”—it is an end-to-end incident pipeline:
- Capture frames once and share them safely across multiple consumers.
- Run multiple CV detectors per frame (face recognition, uniform compliance, hazard detection).
- Track detections across frames to reduce noise.
- Convert persistent detections into prioritized events.
- Enforce notification cooldown policies to prevent alert spam.
- Persist alerts + operational metrics to SQLite (WAL enabled for concurrent reads).
- Provide a web dashboard with video streaming, live alerts via SSE, historical queries, acknowledgements, and basic analytics.
Campus security and discipline teams typically face these pain points:
- Humans cannot monitor many cameras continuously.
- Alerts are often unstructured (phone calls / WhatsApp) and lack traceability.
- False positives are common; systems must enforce temporal persistence and cooldowns.
- Admins need real-time visibility and a reviewable history (with snapshots).
EyeNet addresses this by turning raw video into structured, queryable incidents, with optional automated SMS/email escalation.
- Security operations (unknown visitors, weapons/hazards, fire/smoke detection)
- Campus administration / discipline (uniform compliance)
- Developers / researchers building real-time CV pipelines with dashboards and event-driven notification delivery
The DetectionPipeline (src/core/pipeline.py) runs in its own thread and processes frames from a shared buffer:
-
Face recognition (
src/detectors/face_detector.py)- Loads enrolled face encodings from
models/face_encodings.pkl. - Detects faces and computes encodings per frame.
- Matches each face against known encodings using L2 distance.
- Uses
FACE_DISTANCE_THRESHOLDto classify known vs unknown.
- Loads enrolled face encodings from
-
Uniform compliance for known faces (
src/detectors/uniform_detector.py)- For known faces only, estimates a torso ROI below the face bounding box.
- Converts ROI to HSV and checks for light-blue fabric using a fixed HSV threshold.
- If blue pixel ratio is below the threshold, emits a uniform violation detection.
-
Hazard detection (YOLOv8) (
src/detectors/anomaly_detector.py)- Uses Ultralytics
YOLOwith weights configured viaYOLO_HAZARD_MODEL. - Filters detections by a hazard keyword allow-list (e.g.
knife,gun,fire,smoke,sword,axe, …). - Enforces minimum bounding-box area ratio to reduce tiny false positives.
- Applies per-class confidence thresholds (
HAZARD_CLASS_THRESHOLDS) plus a default threshold. - Applies temporal persistence: a hazard is only “confirmed” after being observed for
HAZARD_CONSECUTIVE_FRAMES.
- Uses Ultralytics
The pipeline feeds detections into an object tracker (src/core/tracker.py):
- Uses
supervision.ByteTrackif installed. - Assigns persistent track IDs across frames.
- Alerts are emitted only when a track persists for at least
min_frames(default5) and only once per track.
This reduces jitter and prevents one-frame anomalies from generating immediate incidents.
EyeNet uses an in-process asynchronous event bus (src/core/event_bus.py):
- Events are represented as immutable
DetectionEventobjects. - The bus uses a priority queue:
- Higher severities dispatch first (
CRITICAL>HIGH>MEDIUM>LOW).
- Higher severities dispatch first (
- Multiple background workers dispatch to registered handlers.
-
Severity classification is rule-based (
DetectionPipeline._classify_severity):CRITICAL:gun,fire,bomb,explosiveHIGH:knife,smoke,axe,swordMEDIUM:unknown(unknown faces)LOW: uniform violations and other low-risk detections
-
Anomaly urgency score (
src/core/anomaly_scorer.py):- Produces a 0–100 score from:
- base weight (by event/item)
- detection confidence
- track persistence duration
- Persisted into alert metadata as
anomaly_score.
- Produces a 0–100 score from:
To prevent alert spam, EyeNet implements cooldowns at two levels:
-
System-wide cooldown (
src/core/cooldown.py)- Per-severity cooldown windows:
CRITICAL: 1 minuteHIGH: 5 minutesMEDIUM: 10 minutesLOW: 24 hours
- Cooldowns key off event identity (
DetectionEvent.cooldown_key) and/or track id.
- Per-severity cooldown windows:
-
SMS sender local cooldown (
src/notifications/sms_sender.py)- Maintains an in-memory cooldown keyed by event subtype (e.g.
dangerous_item_knife). - Default window: 5 minutes.
- Maintains an in-memory cooldown keyed by event subtype (e.g.
EyeNet persists operational history to SQLite (src/core/database.py):
- Uses a thread-local connection (
threading.local()). - Enables
PRAGMA journal_mode=WALandbusy_timeout=5000for better concurrent read behavior. - Stores:
- Alerts (incidents)
- Students (reserved table for enrollment metadata)
- Metrics (pipeline health snapshots)
The dashboard (src/dashboard/app.py) is a Flask application providing:
-
Login session auth (
/login)- Username is fixed to
admin. - Password verification uses
ADMIN_PASSWORD_HASH(bcrypt) if configured. - If not configured, falls back to an insecure dev default (
admin123).
- Username is fixed to
-
Live video streaming (
/video_feed)- Streams MJPEG frames.
- Uses the same shared camera buffer when launched via
src/main.py. - Can run standalone and open camera directly as a fallback.
-
Live alerts via SSE (
/live_alerts)- Server-Sent Events stream.
- Pipeline pushes alert events to a queue (
push_event).
-
Alert management UI (
templates/dashboard.html)- Filters by type/severity.
- Shows snapshot thumbnails.
- Supports ACK button per alert.
-
Metrics polling (
/api/metrics/latest)- Dashboard polls every 3 seconds.
-
Hourly analytics (
/api/stats/hourly)- Aggregates alerts per hour for the last 24 hours.
-
SMS (Twilio) (
src/notifications/sms_sender.py)- Sent for:
unknown_face(if anomaly score >= 25)hazard(if anomaly score >= 25)
- Sent to all numbers in
ADMIN_PHONE_NUMBERS.
- Sent for:
-
Email (SMTP) (
src/notifications/email_sender.py)- Sent for
uniform_violation. - In
src/main.py, student email is derived as:<roll>@mail.jiit.ac.in
- Requires SMTP configuration via environment variables.
- Sent for
The pipeline maintains a metrics dict (FPS, detection counts, latency) and writes snapshots every 5 seconds:
metricstable gets:fps,detection_count,face_count,processing_ms
- Metrics older than 24 hours are pruned automatically.
scripts/migrate_json_to_db.py imports legacy data/logs/alerts.json into the SQLite alerts table.
- Python (Docker images use
python:3.9-slim-bullseye) - Flask (dashboard + REST API)
- OpenCV (
opencv-python) for camera capture, encoding, and rendering
- Ultralytics YOLOv8 (
ultralytics) for hazard detection - face-recognition (dlib-based) for face embeddings + matching
- NumPy for vector math
- supervision (optional) for ByteTrack-based tracking
torch,torchvision,torchaudioare included as dependencies (required by YOLO/Ultralytics)
- SQLite (
sqlite3stdlib) with WAL mode
- Twilio SMS (
twilio) - SMTP for email (
smtplibstdlib)
- bcrypt for admin password hashing/verification
- Flask sessions (cookie-based) using
SECRET_KEY
- Docker:
Dockerfile.backendfor the full appDockerfile.frontendfor dashboard-only container (note: still Python/Flask; not a JS frontend)
docker-compose.ymlfor local container orchestration and camera device passthrough
flowchart LR
CAM[Camera / Video Source] --> FB[SharedFrameBuffer]
FB --> PIPE[DetectionPipeline]
PIPE -->|detections| TRK[ObjectTracker]
TRK -->|persistent track| EV[DetectionEvent]
EV --> BUS[EventBus (priority queue)]
BUS --> DBH[DB Handler]
DBH --> SQLITE[(SQLite: alerts, metrics)]
BUS --> NTFY[Notification Handler]
NTFY --> TW[Twilio SMS]
NTFY --> SMTP[SMTP Email]
BUS --> SSEH[SSE Handler]
SSEH --> DASH[Flask Dashboard]
FB --> DASH
- Single camera reader:
SharedFrameBufferprevents multiple components from fighting over/dev/video0. - Threaded isolation: capture, detection, metrics writer, and dashboard run in separate threads.
- Async-ish event handling:
EventBusdecouples detection from persistence/notification latency. - WAL-mode SQLite: designed for concurrent reads from the dashboard while writes happen from the pipeline.
EyeNet/
src/
main.py # System entrypoint (pipeline + event bus + dashboard)
config.py # Central config loaded from .env
core/
frame_buffer.py # Shared camera capture (one writer, many readers)
pipeline.py # Orchestrates face/uniform/hazard detection
tracker.py # ByteTrack wrapper (or fallback IDs)
event_bus.py # Priority event bus with worker threads
cooldown.py # Per-severity cooldown policy
anomaly_scorer.py # 0-100 urgency score
database.py # SQLite schema + queries + WAL
logging_config.py # Rotating logs to file + console
detectors/
face_detector.py # face-recognition matching to enrolled encodings
uniform_detector.py # HSV torso check for uniform compliance
anomaly_detector.py # YOLO hazard detector + temporal filtering
dashboard/
app.py # Flask UI + SSE + REST API
auth.py # bcrypt hash/verify utilities
templates/ # HTML templates (dashboard, login, etc.)
notifications/
sms_sender.py # Twilio SMS alerts
email_sender.py # SMTP email alerts
encoders/
encode_faces.py # Build models/face_encodings.pkl from students/ images
realtime/ # Legacy monolithic realtime script (historical)
utils/ # Legacy utilities
scripts/
migrate_json_to_db.py # Migrate legacy alerts.json into SQLite
data/
eyenet.db # Default SQLite DB location
anomalies/ # Snapshots saved on events
logs/ # Rotating log files / legacy JSON logs
students/ # Enrollment photos named by roll number
models/
face_encodings.pkl # Generated face encodings artifact
docker-compose.yml
Dockerfile.backend
Dockerfile.frontend
requirements.txt
.env.example
- Python 3.9+ recommended
- A video source (default is webcam
CAMERA_SOURCE=0) - Optional:
- Twilio account for SMS
- SMTP credentials for uniform violation emails
python -m venv venv
source venv/bin/activatepip install -r requirements.txtCopy .env.example to .env and fill in values.
cp .env.example .env- Place images in
students/named by roll number:students/22102027.jpg
- Generate encodings:
python -m src.encoders.encode_facesThis writes models/face_encodings.pkl.
python -m src.main- Dashboard:
http://localhost:5000 - Default login:
- username:
admin - password:
admin123(only if you did not setADMIN_PASSWORD_HASH)
- username:
If you previously logged to data/logs/alerts.json:
python scripts/migrate_json_to_db.pyAll configuration is loaded via src/config.py (python-dotenv loads .env).
CAMERA_SOURCE- Integer camera index (default
0)
- Integer camera index (default
FRAME_WIDTHFRAME_HEIGHTTARGET_FPS
FACE_DISTANCE_THRESHOLD- Face recognition match threshold (lower is stricter)
HAZARD_CONSECUTIVE_FRAMES- Number of consecutive frames required before confirming a hazard
HAZARD_CONF_THRESHOLD- Default YOLO confidence threshold (used as fallback)
YOLO_PERSON_MODEL- Present in config but not required by the current pipeline
YOLO_HAZARD_MODEL- Path to YOLO hazard weights (default
yolov8m.pt)
- Path to YOLO hazard weights (default
TWILIO_ACCOUNT_SIDTWILIO_AUTH_TOKENTWILIO_PHONE_NUMBERADMIN_PHONE_NUMBERS- Comma-separated list, e.g.
+15550001111,+15550002222
- Comma-separated list, e.g.
SMTP_EMAILSMTP_PASSWORDSMTP_HOST(defaultsmtp.gmail.com)SMTP_PORT(default587)
SECRET_KEY- Flask session secret key
ADMIN_PASSWORD_HASH- bcrypt hash used to validate the admin password
SESSION_LIFETIME_HOURS(default8)
Generate a bcrypt hash with:
python -m src.dashboard.auth "your-password"DB_PATH(defaultdata/eyenet.db)
LOG_LEVEL(defaultINFO)LOG_FILE(defaultdata/logs/eyenet.log)
All API endpoints are served by the dashboard app (src/dashboard/app.py).
Authentication: all /api/* endpoints require a valid login session.
Query persisted alerts.
Query params:
type(string) — filter byevent_typeseverity(int) — minimum severity (1..4)since(string) — filter by timestamp lower bound (passed directly into SQL)limit(int) — default50, capped at500
Returns: JSON array of alert rows.
Marks an alert as acknowledged.
Returns:
{ "status": "ok" }Returns the most recent pipeline metrics row.
Returns counts per hour for alerts in the last 24 hours.
MJPEG stream of live frames.
Server-Sent Events stream. Each message is JSON encoded.
The schema is created by init_db() in src/core/database.py.
id(PK)timestamp(TEXT, default localtime)event_type(TEXT)severity(INTEGER)description(TEXT)track_id(INTEGER)image_path(TEXT)metadata_json(TEXT)acknowledged(INTEGER boolean)
Indexes:
timestampevent_typeseverity
id(PK)timestampfpsdetection_countface_countprocessing_ms
Present in schema for enrollment metadata:
roll(PK)name,email,encoding_path,registered_at
Note: current runtime face recognition reads from models/face_encodings.pkl, not from the students table.
- Session-based auth via Flask cookies.
- Protected routes use the
login_requireddecorator. - Authentication logic:
- Username must be
admin. - Password is verified against
ADMIN_PASSWORD_HASHvia bcrypt. - If no hash is configured, the dashboard logs a warning and falls back to
admin/admin123.
- Username must be
Current implementation uses a single admin role. There are no multi-role permissions yet.
This repository implements computer-vision inference workflows, not LLM/agent orchestration.
- Face recognition uses precomputed embeddings (
face-recognition). - Hazard detection uses a YOLOv8 model (
ultralytics).
There is no prompt orchestration, tool-calling, vector database, or retrieval-augmented generation in the inspected code.
docker-compose.yml defines two services:
-
backend- Builds from
Dockerfile.backend - Mounts
./dataand./srcinto the container - Exposes container port
5000as host5001 - Passes through
/dev/video0(requires Linux host) - Runs in privileged mode for camera access
- Builds from
-
frontend- Builds from
Dockerfile.frontend - Exposes
5000:5000 - Note: this is still Flask (dashboard-only), not a JS SPA.
- Builds from
Run:
docker compose up --build- Camera passthrough and
privileged: trueare host-specific. .envis gitignored; inject env vars via your deployment system.- Consider running behind a reverse proxy and terminating TLS externally.
- SharedFrameBuffer: one camera reader thread + lock-protected copies.
- Downscaled face recognition: face detection/encoding runs on a 0.5× resized frame.
- Temporal persistence for hazards (
HAZARD_CONSECUTIVE_FRAMES). - Tracking-based alert emission: only alert after
min_framespersistence per track. - Cooldown policies: prevent repeated notifications for the same ongoing incident.
- SQLite WAL: improves concurrent read/write behavior when dashboard is querying.
- Rotating logs:
RotatingFileHandlerlimits log file growth.
- Real-time concurrency: separating capture, detection, notification, persistence, and UI threads.
- Noise reduction: combining tracking + temporal hazard filtering.
- Operational reliability: WAL-mode DB access, busy timeouts, and cooldown policies.
- Live UI updates: SSE stream for alerts + MJPEG for video feed.
- Replace in-process
Queuefor SSE with a multi-process safe broker (Redis/pubsub) if scaling beyond a single process. - Add proper user management (roles, per-route permissions).
- Persist student enrollment into
studentstable and bind face encodings to DB. - Add rate limiting on API routes.
- Add structured schema migrations (Alembic is overkill for SQLite but still possible).
- Add an alert review workflow (notes, assignment, escalation state machine).
- Replace snapshot
image_pathwith signed URLs if storing in object storage. - Add GPU/accelerator selection and model warmup to reduce first-inference latency.
Insert screenshots/gifs here:
docs/screenshots/dashboard.pngdocs/screenshots/live-alerts.pngdocs/screenshots/hazard-detection.pngdocs/screenshots/uniform-violation.png
- Create a feature branch.
- Keep changes focused (detector logic, dashboard, or infra).
- Prefer adding small, testable modules under
src/core/src/detectors. - Avoid committing:
.env- camera snapshots in
data/ - large model weights (already present in repo; consider Git LFS for long-term)
No license file was found in the repository at the time of inspection. If you intend this project to be open source, add a LICENSE file (e.g., MIT/Apache-2.0) and update this section.