Real-time detection of a person entering a defined danger zone, built as a full computer-vision pipeline rather than a single model: detection → tracking → temporal filtering → zone geometry. Runs in real time on CPU, targeting edge / industrial-safety deployment.
Thesis: a production-ready vision solution requires more than a neural network. The interesting engineering lives in the layers around the detector — tracking, temporal filtering, geometry, and failure analysis — and this project is built to show exactly those.
Server-room clip: person enters the equipment aisle → ENTER event → sustained alarm. Static
camera, dark blue lighting, ~13 FPS on CPU. Box and zone turn red on intrusion.
| Layer | What it does | Why it's needed |
|---|---|---|
| Detection + tracking | YOLOv8n persons, Ultralytics native ByteTrack, persist=True |
one noisy detection per frame → stable per-person identity |
| Geometry | polygon zone, foot-point (bottom-centre) test | the foot is the ground contact — a far better "is the person in the zone" test than the box centre |
| Filtering / logic | per-track hysteresis state machine (enter/exit debounce) | turns flickery per-frame booleans into stable ENTER/EXIT events; survives short detection gaps (occlusion) |
Why "more than a neural network": the naive version — raise an alarm the instant any box touches the polygon — flickers on a single missed frame, false-alarms on a single spurious box, and uses the wrong point of the body. Each of those is fixed by a layer, not by a better model. See the failure-case analysis for concrete breakages and which layer owns each fix.
- Failure-case analysis — real breakages on real footage (camera motion drifting a fixed zone; boundary flicker), each mapped to the owning layer (geometry / filtering / model). Honest: includes a "concern that did not break" (low light held 150/150 frames).
- CPU latency benchmark — PyTorch vs ONNX Runtime, measured. Finding: for a model this small, plain ONNX export gives no speedup on CPU; the real levers (INT8, OpenVINO) are named in the roadmap. The point is the method — measure, don't assume.
- Source-agnostic input — one
FrameSourceabstraction over video files, webcams, and MOT-style image sequences, so the same pipeline runs on stock footage, a laptop webcam, or a public dataset. - Per-stage timings collected in the pipeline, feeding the benchmark for free.
- C++ core — the latency-critical post-model logic (zone geometry + hysteresis state
machine) ported to dependency-free C++17, with a cross-language parity test asserting it emits
the exact same
ENTER/EXITstream as the production Python core.
pip install -r requirements.txt- Draw a danger zone on the first frame (saved as JSON, one per clip):
py -m src.draw_zone --source data/clip.mp4 --out configs/clip.json- Run the pipeline (
--source= video file, MOT image dir, orwebcam):
py -m src.run --source data/clip.mp4 --zone configs/clip.json --displayWrite an annotated video instead of a live window, optionally downscaling for CPU throughput:
py -m src.run --source data/clip.mp4 --zone configs/clip.json --resize-width 1280 --output outputs/demo.mp4Swap in an exported ONNX model with --model yolov8n.onnx. Benchmark the runtimes:
py -m scripts.benchmark --source data/clip.mp4 --frames 150 --models yolov8n.pt yolov8n.onnxsrc/frame_source.py source-agnostic frames: video / webcam / MOT image dir
src/zone.py zone geometry, foot-point-in-polygon
src/track_state.py per-track hysteresis state machine (anti-flicker)
src/pipeline.py detection+tracking -> zone -> state, with per-stage timings
src/annotate.py rendering (zone, boxes coloured by state, HUD)
src/draw_zone.py interactive polygon editor -> configs/*.json
src/run.py CLI entry point
scripts/benchmark.py PyTorch vs ONNX CPU latency benchmark
scripts/parity_check.py C++ vs Python event-stream parity test
cpp/ C++17 port of the zone + hysteresis core (zone_monitor)
docs/ failure-case analysis, benchmark, demo GIF, pipeline diagram
Tracked in ROADMAP.md. Honestly-labelled next steps (for future development): ground-plane homography so the zone lives in world coordinates and survives camera motion; Kalman smoothing of the foot point; INT8 / OpenVINO for a real CPU speedup; a pedestrian-dataset (MOT) variant for quantitative evaluation; and — building on the C++ core — a full C++ inference path (ONNX Runtime C++ / TensorRT) for Jetson-class deployment.
MIT — see LICENSE.

