Analyse uploaded footage or inspect live camera frames. Object labels and model scores are observations—not proof of danger or a guarantee of safety.
Camera frames are sent to your configured video service only after you start. They are analysed in memory; this mode does not record a video or detect crimes.
Your camera is off.
Frames are sampled sequentially, up to once per second. Actual speed depends on hardware. A displayed score is not an accuracy measurement.
Test automatic assignment to the nearest fictional station. No real station is contacted and no emergency service is dispatched.
No demonstration event submitted.
For a real deployment: validate a cabin-specific event model, calibrate escalation thresholds and agree a response workflow with the department. The current object detector does not recognise assault.
Drop a video here or click to browse
MP4, MOV, AVI — the pipeline will detect, track, and export JSON
The video layer and SVG HUD layer are synchronized through video.currentTime. The overlay is a transparent div positioned absolutely over the video element. On each requestAnimationFrame, the renderer reads video.currentTime, looks up the closest track frame, interpolates positions, and updates box positions via CSS transforms.
Standard element with native controls. The timeupdate event and requestAnimationFrame loop drive the overlay sync.
A div with pointer-events: none sits on top. Individual detection boxes have pointer-events: auto so they remain interactive.
Each frame, the renderer finds track observations whose timestamp t is closest to video.currentTime and interpolates between the two nearest samples.
The Python backend decodes the video with OpenCV, runs YOLOv8 detection on sampled frames, applies a ByteTrack-inspired IoU tracker for identity persistence, normalizes all coordinates to 0..1, and exports a JSON payload.
cv2.VideoCapture reads frames. Only 1 in every sample_rate frames is processed to balance accuracy and speed.
Each sampled frame is passed to the YOLOv8 model. Detections below conf_threshold are discarded. COCO class labels are attached.
A two-stage IoU matcher assigns detections to existing tracks. High-confidence detections match first (IoU ≥ 0.5), then low-confidence (IoU ≥ 0.3). Unmatched detections spawn new tracks.
Box coordinates are divided by video dimensions to produce 0..1 values. This makes the payload resolution-independent.
The ai-video-tracks.v1 payload is written to disk. The frontend loads it via fetch() and renders overlays.
The ai-video-tracks.v1 payload defines a stable interface between the Python backend and the JavaScript frontend. All coordinates are normalized 0..1. Timestamps are in seconds, aligned to video frame rate.
The renderer runs on requestAnimationFrame, reads video.currentTime, performs binary search on each track's frame array, interpolates between the two nearest observations, and positions SVG boxes via CSS transforms.
Each track's frames array is sorted by t. A binary search finds the two frames bracketing video.currentTime.
Between two frame samples, box position is linearly interpolated: pos = f0 + (f1 - f0) * alpha where alpha = (now - t0) / (t1 - t0).
Each box is a div positioned with left, top, width, height as percentages. This avoids SVG reflow overhead.
Boxes are created when a track first appears and reused across frames. When a track has no observation near currentTime, its box is hidden via display: none.
Detection boxes are not passive overlays — they are interactive DOM elements. Hover, keyboard focus, and click all produce meaningful responses.
On hover, the box scales to 1.02 with a 150ms ease transition. A glow ring appears via box-shadow. The label becomes fully opaque.
Boxes are tabindex="0". :focus-visible shows a blue outline. Arrow keys move between boxes; Enter selects.
Clicking a box dispatches a det-select custom event with the track ID. The sidebar highlights the selected track and the info panel shows its details.
When prefers-reduced-motion: reduce is active, all transitions and transforms are disabled. Boxes appear instantly without animation.
sample_rate=5 processes 6 samples/sec in a 30fps file. Fast motion can be missed between samples; evaluate sampling against the events you need to observe.
0.4 is the current pilot threshold, not a validated optimum. Measure missed objects and false detections on representative footage before choosing a deployment threshold.
A track may be retained internally for 30 sampled updates; it is hidden while unmatched. Three consecutive matches are required before display. At 30fps with sample_rate=5, 30 updates represent about five seconds.
In production, the JSON payload is served from the same origin as the video. Cross-origin setups require Access-Control-Allow-Origin headers.
Uploads are capped at 100 MB; inference is serialised and temporary media is removed. The local pilot does not provide process sandboxing or production tenant isolation.
More objects, more sampled frames and longer clips produce larger results. Measure payload size and processing latency using your own footage.
Set up file upload endpoint. Validate file type and size. Store in temp directory. Return a job ID.
Run pipeline.py as a background task. Stream progress via WebSocket or polling. Store output JSON alongside the video.
Load video + JSON. Initialize renderer. Wire up controls (play, seek, conf filter, labels toggle). Test on real footage.
Test with diverse videos (night, rain, crowded). Verify interpolation smoothness. Deploy behind Nginx with CORS headers. Monitor payload sizes.