BranchSight

Turns the CCTV cameras a restaurant or shop already has into live operational numbers, without identifying anyone.

What it does

Chains have cameras in every branch, but nobody watches them. BranchSight watches the feeds continuously and answers the questions a branch manager actually asks: how many people came in, how long the queue is, whether the counter has been left empty, and where customers spend their time.

Footfall

Counts people crossing a line drawn across the door, in each direction, minute by minute.

Queue and wait time

Measures how many people are waiting at the counter and how long they waited. Raises an alert if the queue stays too long.

Unattended counter

Alerts when nobody has been behind the counter for a set time during opening hours. It checks presence, never who.

Heatmap and dwell

Shows which parts of the floor people spend time in and the average time spent in each zone.

What it looks like

Live page: three camera feeds with people boxed and faces pixelated, count readouts under each feed, and an alert list on the right
Live view. Three demo cameras playing recorded public footage, analysed live. Faces are pixelated on the server before any frame is sent.
Zone editor: a queue area drawn as a yellow polygon over a blurred snapshot, with settings for the alert threshold
Zone editor. A manager draws the queue area with the mouse and sets the alert threshold. It applies to the running stream on save.
Dashboard: charts of footfall per minute and minutes the staff desk was empty
Dashboard. Per-minute footfall, queue length, wait time, unattended minutes, occupancy and the heatmap, with CSV export.

How it works

Each camera has its own worker. It always takes the newest frame, so delay never builds up. It finds people, follows each one across frames with a temporary ID, updates the counts, and pixelates faces before the frame goes to the browser. Only per-minute numbers and alert events are saved.

Privacy by design

Results

Every number here comes from a script in the repository and can be re-run. The weak results are reported along with the strong ones.

21.9 FPSfull pipeline, one camera, laptop CPU
1.76xfaster with ONNX Runtime than PyTorch on CPU
34.7MOTA on MOT17 at 960 px (24.3 at the default 640)
55automated tests, including a real-footage run

Tracking accuracy on MOT17

MOT17 is the standard public benchmark for following people in video, with every person hand-annotated. The system was run on all 7 training sequences and scored with an evaluator that matches the official TrackEval rules. The detector is the off-the-shelf nano model; it was not trained on this data.

Almost all errors are missed people (82,559 missed boxes against 2,299 false alarms), mostly small and distant figures in crowded scenes. Giving the model more pixels helps more than a bigger model, but costs 2.3x the compute. On the shop-entrance sequence the default setup scores MOTA 56.4.

Counting accuracy

Instead of counting by hand, the true number of line crossings was computed by running the same counting code over the benchmark's annotated tracks. That gave 76 real crossings on 8 lines across the three fixed-camera sequences.

SetupCrossings counted (true: 76)Total error
Nano model, 640 px (default)50-34%
Small model, 640 px69-9%
Nano model, 960 px74-3%

At 960 px the total is close, but individual lines are still off in both directions, so per-line accuracy is lower than the total suggests. The sample is small.

Speed

HardwareSetupTime per frame (median)
Laptop CPU (AMD Ryzen AI 9 HX 370)PyTorch, detection only51.1 ms
Laptop CPUONNX Runtime, detection only29.0 ms
Laptop CPUFull pipeline, one camera44.4 ms (21.9 FPS)
Laptop GPU (RTX 5070 Ti)PyTorch, network only, 1 frame18.9 ms
Laptop GPUPyTorch, network only, 3 cameras batched9.0 ms per frame
Cloud VM (2 shared vCPUs)Full pipeline, one camera274 ms (3.2 FPS)

Engineering

Limitations and next steps

Demo footage: MOT17 sequences from MOTChallenge (CC BY-NC-SA 3.0, non-commercial) and a Pexels stock clip, both with faces pixelated. The code is AGPL-3.0 because it uses Ultralytics YOLO.