A 2-tier prototype built for (Ministry of Railways). It watches a CCTV video feed, detects people/trains with YOLO + ByteTrack, applies geometric safety rules with Shapely, and automatically emails Police / RPF / Station staff when something dangerous happens.
railway-safety/
├── cv-engine/ # Python 3.11+ — AI Vision Engine
│ ├── app.py Main detection + tracking + alerting loop
│ ├── zone_config.py Restricted-zone polygon definition (Shapely)
│ ├── alert_manager.py Cooldown logic + async HTTP dispatch to backend
│ ├── calibrate_zone.py Click-to-trace tool for setting the zone on real footage
│ ├── requirements.txt
│ └── .env.example
└── backend/ # Node.js + Express — Alert Dispatcher
├── server.js REST API: contacts + trigger-alert + history
├── db.js SQLite schema + seed data (better-sqlite3)
├── mailer.js Nodemailer HTML email templates
├── package.json
└── .env.example
┌────────────────────┐ POST /api/trigger-alert ┌──────────────────────┐
│ Python CV Engine │ ─────────────x-api-key header───────▶ │ Node.js Backend │
│ (YOLO + ByteTrack │ │ (Express + Nodemailer│
│ + Shapely zones) │ ◀───────────200 OK / count────────────│ + SQLite) │
└─────────┬───────────┘ └──────────┬────────────┘
│ cv2.imshow │ SMTP
▼ ▼
Annotated video HUD Police / RPF / Station Manager
(bounding boxes, zone, inboxes
warning banners)
| Alert Type | Severity | Trigger Condition | Cooldown |
|---|---|---|---|
RESTRICTED_ZONE |
CRITICAL | Any tracked person's foot-point falls inside the track polygon | 30s |
OVERCROWDED |
HIGH | total_people_in_frame > CROWD_CAPACITY_LIMIT |
45s |
TRAIN_DANGER |
CRITICAL | Train detected (COCO class 6) AND ≥1 person is inside the restricted zone | 15s |
SESSION_SUMMARY |
auto (CRITICAL/HIGH/MEDIUM/LOW) | Fired once, when the video ends — naturally, on 'q', or Ctrl+C | n/a — fires once per run |
Cooldowns are tracked per alert type in alert_manager.py so a person standing
in the danger zone for 2 minutes triggers one email every 30s, not one every frame.
SESSION_SUMMARY is different from the other three — it isn't a real-time danger
alert, it's a final report. When the video finishes (end of file, 'q' pressed,
or Ctrl+C), app.py first prints a full summary to the terminal — frames processed,
peak crowd size, frames with a zone intrusion, unique violator track IDs, whether a
train was ever seen, and how many real-time alerts were sent — and only then emails
that same report to every contact subscribed to SESSION_SUMMARY. Its severity is
set automatically from what happened during the run (CRITICAL if train+person danger
occurred, HIGH if any zone intrusion occurred, MEDIUM if only overcrowding occurred,
LOW if the session was clean).
- Python 3.11+
- Node.js 18+
- A Gmail account with an App Password (not your normal password) — see step 3.
- A sample CCTV-style video file (any
.mp4with people/trains works; place it incv-engine/).
cd backend
npm install
cp .env.example .envOpen .env and fill in:
SMTP_USER— your Gmail addressSMTP_PASS— your 16-character Gmail App PasswordINTERNAL_API_KEY— any secret string (must match the CV engine's.env)
cd ../cv-engine
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .envOpen .env and set:
VIDEO_SOURCE— path to your demo video, e.g.sample_video.mp4NODE_BACKEND_URL—http://localhost:5000(default, matches the backend)INTERNAL_API_KEY— must be identical to the backend's.env
- Go to https://myaccount.google.com/security and enable 2-Step Verification if not already on.
- Go to https://myaccount.google.com/apppasswords
- Create an app password named "Railway Safety Alerts", copy the 16-character code.
- Paste it into
backend/.envasSMTP_PASS(no spaces).
The default polygon in zone_config.py is a generic trapezoid for a 1280x720 frame.
To trace the real track boundary on your footage:
cd cv-engine
python calibrate_zone.py --source sample_video.mp4Click 4+ points around the track, press s, and copy the printed coordinates into
zone_config.py (ZONE_POLYGON_POINTS), or pass them at runtime with --zone.
cd backend
npm startYou should see:
[MAILER] SMTP connection verified. Ready to send alert emails.
Listening on http://localhost:5000
cd cv-engine
source venv/bin/activate
python app.py --source sample_video.mp4A window opens showing the video with:
- Green boxes = safe people, Red boxes = people inside the restricted zone
- Orange boxes = detected trains
- Yellow outline = restricted track polygon
- Top-left HUD = live people count, zone violators, overcrowding status, FPS
- Bottom red/orange banner = active CRITICAL/HIGH alert
Press q to quit, p to pause.
Whenever a rule fires (and isn't on cooldown), the CV engine POSTs to
http://localhost:5000/api/trigger-alert, and the backend emails every contact
subscribed to that alert type. Watch the backend terminal for delivery logs.
# List all emergency contacts
curl http://localhost:5000/api/contacts
# Add a new contact
curl -X POST http://localhost:5000/api/contacts \
-H "Content-Type: application/json" \
-d '{"name":"Constable P. Singh","email":"constable@example.com","role":"POLICE","subscriptions":["RESTRICTED_ZONE","TRAIN_DANGER"]}'
# Manually fire a test alert (simulates what app.py sends)
curl -X POST http://localhost:5000/api/trigger-alert \
-H "Content-Type: application/json" \
-H "x-api-key: sih1349-secret-key-change-me" \
-d '{"alertType":"RESTRICTED_ZONE","severity":"CRITICAL","message":"Test intrusion alert.","location":"Platform 1","metadata":{"totalPeople":3,"peopleInZone":1}}'
# View recent incident history
curl http://localhost:5000/api/alerts/historyCROWD_CAPACITY_LIMITincv-engine/.envcontrols the overcrowding threshold (demo default: 5; set to 30+ for a real platform).- Swap
MODEL_PATHtoyolo11n.pt,yolo11s.pt, etc. for a speed/accuracy tradeoff — smallernmodels run faster on CPU, which is ideal for a live demo. --saveflag onapp.pywrites the fully annotated video to disk for offline review/judging.--no-displayruns headless (useful if deploying the CV engine on a server without a monitor).- All cooldowns, thresholds, and the backend URL are configurable via
.env— no code edits needed for a live demo.