Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Xander44-4/traffic_reducer/llms.txt

Use this file to discover all available pages before exploring further.

Traffic Reducer is built from two cooperating Python modules: video_processor.py, which owns the TrafficCamera class and all computer-vision work, and app.py, a Flask application that exposes a REST API and streams annotated video to the browser. Together they form a closed loop — raw camera pixels enter one end and green-light decisions come out the other, updated multiple times per second without blocking the web server.

High-level data flow

Every processed frame travels through the following stages before the dashboard updates:
Video source (YouTube live / local file)


FFmpeg subprocess  ──(raw BGR frames)──▶  OpenCV VideoCapture


Background daemon thread  (_process_stream)

        ├──▶  ThreadPoolExecutor  ──▶  YOLOv8 inference  (_run_yolo)
        │              │
        │              ▼
        │         Zone vehicle counts + pedestrian count + emergency flag
        │              │
        │              ▼
        │         traffic_state dict  (thread-safe, lock-protected)

        ├──▶  _draw_overlay  ──▶  JPEG encode  ──▶  traffic_state['frame']


Flask app  (app.py)
        ├──  GET  /video_feed  ──▶  MJPEG stream  ──▶  <img> in dashboard
        ├──  GET  /stats       ──▶  JSON counts, phase, priority
        └──  POST /predict     ──▶  JSON green-light decision

Component breakdown

TrafficCamera class (video_processor.py)

TrafficCamera is the heart of the system. At construction time it loads the YOLOv8 model, defines the four directional zone polygons, and allocates a ThreadPoolExecutor(max_workers=1) for asynchronous inference. A single background daemon thread — started by camera.start() — runs _process_stream() in an infinite loop, handling three operating modes:
ModeSourceNotes
idleNoneDisplays a placeholder frame; no inference
youtubeYouTube live URL via yt-dlp + FFmpeg subprocessTargets 25 FPS at 854 × 480
localLocal MP4 file via cv2.VideoCapturePlayback speed controlled by local_speed multiplier
The central state object traffic_state is a plain dict protected by a threading.Lock:
self.traffic_state = {
    'norte': 0, 'sur': 0, 'este': 0, 'oeste': 0,
    'pedestrians': 0,
    'emergency': False,
    'phase': 'INIT',
    'frame': self._placeholder_frame("Selecciona una fuente para comenzar")
}
All Flask endpoints read from this dict via camera.get_counts() and camera.get_frame(), which both acquire the lock, copy the data, and release — keeping I/O threads independent of the inference thread.

Flask app (app.py)

The Flask application is thin by design. It instantiates one TrafficCamera at module load time and exposes the following REST surface:
MethodEndpointPurpose
GET/Renders templates/index.html (Jinja2)
GET/video_feedStreams MJPEG from generate_frames()
GET/statsReturns live counts, phase, priority, emergency flag
POST/predictRuns majority-rule decision; optionally reads live counts
GET/simulateReturns random counts 0–80 for UI testing
POST/set_sourceSwitches camera mode (youtube / local)
POST/set_speedAdjusts local video playback speed multiplier
GET/source_statusReports active mode and local-file availability
The generate_frames() generator yields MJPEG boundary-delimited JPEG bytes directly from traffic_state['frame'], keeping the stream latency under one frame.

Web dashboard (templates/index.html + static/script.js)

The browser dashboard never holds server-side state. It polls two endpoints on fixed timers:
  • /stats every 1 500 ms — updates the per-direction vehicle counts, pedestrian count, emergency indicator, and intersection graphic.
  • /predict every 2 000 ms (live mode only) — fetches the current green-light decision and animates the signal display. In manual mode, the frontend calls /predict immediately whenever a slider value changes.

Threading model

Traffic Reducer runs three concurrent execution contexts at all times:
Main thread  ──────────────────────  Flask (Werkzeug WSGI server)
                                       handles all HTTP requests

Daemon thread  ────────────────────  _process_stream()
                                       reads frames, submits YOLO jobs,
                                       draws overlays, encodes JPEGs

ThreadPoolExecutor (max_workers=1)    _run_yolo()
                                       YOLOv8 forward pass on each new frame
The executor’s single worker means inference never queues: if the previous YOLO job has not finished when the next frame arrives, _process_stream draws the overlay using last_yolo_data (the most recently completed result) and skips submitting a new job until the executor is free. This prevents unbounded queue growth under high frame rates.

Frame caching

For local video playback, _process_stream keeps a yolo_cache dict keyed by cv2.CAP_PROP_POS_FRAMES (the integer frame index returned by OpenCV):
if mode == 'local' and cur_idx in self.yolo_cache:
    self.last_yolo_data = self.yolo_cache[cur_idx]
else:
    # submit a new YOLO job and store result under cur_idx
This means a frame that has already been analysed — for example when playback loops back to the start of the file — is served from the cache with zero inference overhead. The cache is cleared automatically whenever the source mode is switched via set_mode().

Explore the concepts

Vehicle Detection

How YOLOv8 detects vehicles, assigns them to directional zones, and draws the annotated overlay.

Signal Decision

The majority-rule algorithm that picks which lane gets the green light each cycle.

Priority System

Pedestrian and emergency-vehicle overrides that supersede normal signal decisions.

Architecture

You are here — the full system architecture and threading model.

Build docs developers (and LLMs) love