WIUT Hackathon 2026 · Computer Vision track

One camera.
Every violation
on the clock.

We watch a fixed CCTV view of a busy Tashkent junction, report every traffic event as a time segment with its class, and raise a causal risk score before an accident can start.

4sample videos, 4K · 29.97 fps
18.4minutes of footage
3,151road users tracked
150events detected
The junction from the camera, with the scene map drawn over it: crossings, stop line, median, approach lanes and junction area.
The scene map, drawn once on a reference view. Every video is registered to it automatically.

Problem and approach

Detector, tracker, and rules that know this junction

Every class in the brief is defined by where a road user is, where it moves, and what the signal shows. So we detect and track everything, map it onto a hand-drawn scene map, and apply one explicit rule per class. Learned parts do perception; rules do the traffic law.

Pipeline: video, registration, detection and tracking, signal lamp, trajectories, rules, segments, events; causal risk branch for Part B. 4K video 29.97 fps Registration SIFT homography YOLO11s + ByteTrack every 3rd frame · learned Signal lamp reader same decoding pass Trajectories reference coords Rules per class scene map + phase Events [start, end, label] Part B · causal risk own detector + tracker, first-frame registration, time-to-collision Risk per frame P(accident ≤ 5 s)

Why this design

Registration first. The camera is fixed within a video, but the samples are framed differently: samples 03 and 04 sit about 40 px off and 2 % zoomed out compared with 01 and 02. Each video's median background is matched to the reference view with SIFT + RANSAC, so the scene map is drawn only once.

A signal we can actually see. The vehicle heads on the gantry face away from the camera. A pedestrian head on the left pole faces it. We read its two lamps from a tiny ROI and checked the mapping against every stop-line crossing: main-road traffic moves while this lamp is green.

Few classes, carefully. Score A is a macro average over every class in the ground truth or in our predictions, so a class we predict that never occurs costs a whole class. We only emit classes whose rule we checked by eye on the samples.

The lamp that gives us the phase

Close-up of the pedestrian signal head on the left pole, red lamp lit.
Colour-difference score of the lit lamp is about 100; a dark lamp scores about 0. Read at 10 Hz in the same pass as the detector.

One rule per class

ClassRuleKind
failure_to_yieldVehicle drives through a crossing (front entering to rear leaving) while a pedestrian on that crossing is within ~90 px of its path.rule
stop_lineVehicle stops past the stop line, on the stop zone or crossing, during red. Ends when the signal turns green.rule
red_lightCrosses the stop line more than 4.5 s into red, not as part of a queue discharge, while other vehicles still wait, and drives on into the junction.rule
jaywalkingPedestrian clearly on the carriageway: inside the kerb, 30 px or more from every crossing, off islands. Scooter riders excluded; people crossing within 4 s of each other form one event, kept if it lasts 6 s or more.rule
illegal_u_turnHeading reverses more than 150°, and the reversal is made on a pedestrian crossing or across the solid median (Rules of the Road of Uzbekistan, clause 62).rule
stopped_vehicleStationary 10 s or longer on the carriageway, not explained by the signal, a queue, pedestrians ahead, or a turn gap.rule
wrong_wayMoving against a strongly one-directional cell of the flow map for 2.5 s and 80 px, outside the junction.learned map rule
accident · near_missContact with an impact-like deceleration, or hard braking toward a crossing road user with TTC under 1 s. Guarded against ID switches, occlusion and cars on opposite carriageways.rule
congestionEight or more vehicles crawling in the approach for 20 s and still crawling through a green phase.rule
DetectionYOLO11s COCO-pretrained, 960 px input, every 3rd frame (~10 fps), ByteTrack with a 4 s buffer.learned

Not predicted: illegal_turn, solid_line_crossing, road_obstacle, fire_smoke. We had no reliable signal for them on this camera.

EDA of the sample videos

What the camera sees

Findings from our own detections on the four samples. Several of them changed the solution.

The videos

VideoResolutionFileDurationLighting (mean V)VehiclesPeopleSignal: green / redRegistration

Brief says 25 fps; the files are 29.97 fps. Samples 01–03 are daylight; 04 is dusk (mean brightness about half). The signal cycle is about 30–35 s green and 42–46 s red for the main road.

Road users in view over time

Mean number of detected objects per frame in 5 s bins. The saw-tooth in cars is the signal cycle: the approach fills during red and empties on green.

Vehicle motion heatmap

Heatmap of moving vehicles over the reference view, brightest on the main carriageway and the U-turn loop.
All four videos. The bright arc right of the refuge is the U-turn loop: 11 U-turns in 18 minutes, 9 of them legal.

Pedestrian heatmap

Heatmap of pedestrians, concentrated on the three crossings and the paths between them.
People use the three crossings, and also cut through the junction between the main crossing and the islands. That shortcut is where jaywalking comes from.

Trajectories and lane directions

Vehicle trajectories coloured by direction, pedestrians in white.
Coloured by overall direction. Lower carriageway flows toward the camera through the signal; upper carriageway flows away; turns into the side road at bottom-left.

Learned flow map

Arrows per 32 px cell showing the dominant driving direction; grey where directions are mixed.
Dominant direction per 32 px cell (grey = mixed). This is the only learned part of the rules: it defines the legal direction for wrong_way.

Vehicle speed

Speed of moving vehicles in reference pixels per second (1 px ≈ 9 cm near the stop line). Most moving traffic is at 40–120 px/s.

Pedestrian speed

Walking is 5–20 px/s. Tracks above ~45 px/s are riders; we use this to keep scooter riders out of jaywalking.

Findings that shaped the solution

FindingWhat we changed
Samples are framed differently (up to 40 px shift, 2 % zoom).Per-video SIFT registration to one reference view.
Vehicle signals face away; a pedestrian lamp faces the camera and runs in phase with main traffic.Signal phase from that lamp, verified against every stop-line crossing.
Legal clearing traffic crosses up to 3.9 s after the lamp turns red; queues move off up to 5 s before it turns green (sample 02).red_light needs 4.5 s into red, no group departure, and a waiting queue.
Scooter riders are detected as person and wait in traffic lanes.Rider test (bike box under the person, or vehicle speed).
U-turns around the median nose are common; almost all turn inside the junction, not on a crossing.Only U-turns on a crossing or across the median count as illegal (Uzbek rules, clause 62).
Normal traffic here constantly has short time-to-collision (turns across crossings).Risk calibrated on 8,271 samples of normal traffic; alarms only beyond anything seen there.
4K decoding dominates runtime.Single threaded decode pass, 1280 px detector input, time-budget guard.

Results on the sample videos

Every sample, annotated

Rendered with our own tooling: boxes, track tails, the stop line in the live signal colour, active events on top, the risk curve bottom-right. Click an event or the timeline to jump the video.

risk scorealarm threshold 0.5main greenmain red

Examples and honest failure cases

CaseWhat happenedStatus
Car runs the red (sample 01, 79 s)Crosses the stop line 19 s into red and drives through the junction while pedestrians are on the crossing.detected
Cars stop on the crossing during red (sample 04, 79–115 s)Two cars creep past the stop line and wait on the zebra until green.detected
Bus runs the red (sample 02, 58 s)Crosses 5.6 s into red while the lane beside it waits and pedestrians stand at the kerb.detected
Scooter rider waiting in a laneFirst version called him a jaywalker for 33 s. Fixed with the rider test.fixed
Two cars merged into one boxThe box "brakes" as it grows; looked like a near miss. Fixed with a rigid-box check.fixed
Cars on opposite carriageways overlap in the imageLooked like contact (accident). Fixed: pairs split by the median are ignored.fixed
Failure to yield next to people waiting on the kerb (samples 03–04)They stand on the painted end of the upper crossing, so a passing car counts. 19 of 34 predictions in 03–04 are wrong, mostly this.open
Cyclist on the zebra flagged as wrong-way (sample 04, 12.5 s)Detected as a motorcycle and moving across the lane's flow direction.open
Box jitter on a distant car flagged as near miss (sample 03, 282 s)At the top of the frame a car is ~20 px wide, and box jitter looks like hard braking.open
Queues discharging before the lamp turns greenWould be dozens of false red-light events. Handled by the group-departure test; the vehicle signal itself is never visible.mitigated

Live demo

Upload a clip, get the events back

Runs the same pipeline on a free cloud CPU. It accepts .mp4 up to 2 minutes and 200 MB, from this camera; other scenes run but the scene map won't fit them. Expect about 2–4 minutes for a 2-minute clip. Progress is shown while it runs.

If the frame is empty or asleep, open the demo in its own tab: junction-watch.streamlit.app. A sleeping app wakes up in about a minute.

Technical report

What worked, what did not, what next

Worked

Scene-specific rules over generic models. A hand-drawn scene map plus registration made every rule interpretable and debuggable frame by frame.

Reading the pedestrian lamp. A 9×9 px ROI gives the full signal phase for free and made stop_line and red_light possible.

Reviewing candidates by eye. Contact sheets around every candidate event found each failure mode listed in Results within minutes.

Calibrating risk on normal traffic. Mapping danger to probability through quantiles of 8,271 normal-traffic samples took false alarms from one every few seconds to none on the samples.

Did not work

Raw time-to-collision as risk. At this junction turns constantly cross pedestrians; the first risk score averaged 0.42 and alarmed every few seconds.

A fixed "near" radius for failure-to-yield. Reviewing 03–04 showed it confirms only 5 of 34 predictions: it cannot tell a person waiting at the kerb from one crossing.

Frame-to-frame kinematics. Near-miss and accident rules fired on tracker artefacts: ID switches, merged boxes, occlusion behind poles and buses.

The lamp as the whole truth. In sample 02 queues leave up to 5 s before the lamp turns green, so the red-light rule needed behavioural evidence as well.

Decoding 4K twice. Our first submission ran over the time budget on a T4 because of video decoding, not the model.

Next

Fix failure-to-yield first. Count only pedestrians who are walking on the crossing, not standing at its end; then train a small classifier on track pairs (distance, speeds, position on the crossing) from our labels to replace the fixed radius. A quick test of the walking condition removed many of the kerb cases, but it moved other events around, so we did not ship it untested.

Minimum object size for kinematic rules. Ignore near_miss and wrong_way candidates on boxes that are too small to measure speed reliably, and use a person-on-bike test before wrong_way.

Accident data. Fine-tune a clip classifier on public crash datasets (DoTA, CCD) to confirm contact candidates and to feed Part B.

GPU decoding. NVDEC would remove most of the runtime and allow 30 fps tracking.

Lane-level map. Draw lane lines and solid markings to add solid_line_crossing and illegal_turn.

Evaluation

We have two kinds of evidence, and we keep them apart. (1) Independent labels: our teammates labelled sample_01 and sample_02 by hand (13 events) without looking at the model output, and we score them with the official evaluate.py. (2) Review of our own predictions: for sample_03 and sample_04, every event the model predicted (46) was checked by eye. That gives precision per class, not a score, because a review of predictions cannot count missed events.

0.04Score A on the independent labels (01–02)
0.17class-agnostic F1 at tIoU 0.3 (01–02)
8 / 46predictions confirmed correct (03–04)
24 / 46predictions confirmed wrong (03–04)

1 · Independent labels, samples 01–02

ClassF1 (mean over tIoU 0.3/0.5/0.7)TP / FP / FN at 0.5Reading
jaywalking0.402 / 4 / 2Boundaries match well when we agree (241.9–253.0 s against the label 241.2–253.9 s).
failure_to_yield0.000 / 42 / 2Different unit of event: the labels mark one 38–52 s block per video, we report each car–pedestrian conflict. Also our least precise class (see 2).
congestion0.000 / 0 / 4Different definition: the labels count red-light queues; our rule requires the queue to persist through a green phase.
illegal_u_turn0.000 / 1 / 1The labelled U-turn is made inside the junction, which we treat as legal under clause 62.
road_obstacle0.000 / 0 / 2Not predicted (class removed).
stop_line, red_light—0 / 6 / 0, 0 / 3 / 0Not in the labels. Checked by eye: the sample_01 red-light run at 79 s is real (a car crosses 19 s into red while people are on the crossing).

What the low score means: most of the gap is disagreement about what one event is (a block versus each conflict, a queue versus congestion), not events nobody saw. With 13 labelled events the number is also very noisy. We did not tune thresholds to these labels. We changed only the minimum jaywalking length and two real bugs (below).

2 · Review of every predicted event, samples 03–04

Method: three zoomed frames per event (start, middle, end), with the vehicle and nearby people boxed. AI-assisted, checked by the team. C = correct, U = unclear at this resolution, W = wrong. Verdicts per event: review_03_04.json.

ClassPredictedC / U / WPrecision (confirmed – with unclear)Main error
stop_line43 / 1 / 075 % – 100 %—
failure_to_yield345 / 10 / 1915 % – 44 %People waiting at the kerb end of the upper crossing stand on its painted edge, so a car passing them counts as a conflict.
jaywalking60 / 3 / 30 % – 50 %People stepping between the two traffic islands next to the side-road zebra, 30–50 px outside our crossing polygon.
near_miss10 / 0 / 10 %A distant car at the top of the frame: box jitter at small size looked like hard braking.
wrong_way10 / 0 / 10 %A cyclist crossing on the upper zebra, detected as a motorcycle, moving "against" the flow of the lane.
A car turns through the side-road crossing while pedestrians walk on it.
Correct · sample 03, 53.6 s: a car turns through the side-road crossing while people walk on it.
A car passes the upper crossing while people wait on the kerb.
Wrong · sample 03, 130.7 s: the people are waiting on the kerb, not crossing. This is the most common failure_to_yield error.
A cyclist on the upper crossing flagged as wrong-way.
Wrong · sample 04, 15 s: a cyclist on the zebra, flagged as a wrong-way motorcycle.

Bugs the evaluation found (fixed)

Walkers treated as riders. Found through the labels: brisk walkers near the camera were taken for scooter riders and dropped from jaywalking. The rider test now uses the median speed.

Camera drift and the signal lamp. Found in our review: the camera in sample_01 pans about 8 px during its first 20 s, so the lamp ROI (placed from the first frame) slid off the lamp and the whole video read as red. The reader now re-registers at 2, 5, 10, 20, 40 s and then every 60 s.

Team

Team

Computer-vision lead

Mekhroj

  • Road-analysis pipeline: detection, tracking, registration
  • Signal-phase reader and scene map
  • Event rules, Part B risk score, evaluation

Frontend lead

Jamshid

  • Website design and layout
  • Sample-video results pages
  • Interactive timelines

Web and demo engineer

Nizomiddin

  • Live demo app and deployment
  • EDA charts
  • Hosting and links

A project we are proud of

An AI camera system that takes attendance automatically: classroom cameras, face detection and recognition in Python, attendance written to a register without anyone calling names.