Adaptive traffic-signal control driven by per-lane vehicle density.
Most Indian intersections still rely on fixed-timer traffic lights that allocate the same green-light duration regardless of actual traffic. The result is predictable: empty roads get the same time as jammed ones, queues stretch, fuel is burned, and tempers flare.
This project replaces the dumb timer with a simple idea:
Look at each lane, estimate how much traffic is waiting, and split the cycle time proportionally.
It started as a 2016 B.Tech capstone in Java/Swing. This is the modern Python
rewrite — cleaner, faster, and actually runnable. The original code is preserved
in legacy-java/ for posterity.
- Two density estimators, behind a common interface:
- Classical — OpenCV Canny + morphology → edge-pixel ratio as density proxy. Zero-config, fast, ~30 lines of code.
- YOLOv8 — Ultralytics
yolov8ncounts cars, motorcycles, buses, and trucks. Downloads a ~6 MB checkpoint on first run.
- A proportional signal allocator with configurable cycle time and per-lane min/max green bounds.
- A Streamlit web UI for live demos (upload 4 lane photos, see annotated results + bar charts + green-time table).
- A CLI (
python -m its …) for batch use. - Unit tests + CI, so it keeps working.
streamlit run app.pyPick a mode in the sidebar, choose bundled samples or upload 4 lane images, hit Simulate:
| Classical (edge density) | YOLOv8 (vehicle detection) |
|---|---|
![]() |
![]() |
$ python -m its --mode yolo sample_images/lane_1.jpg sample_images/lane_2.jpg \
sample_images/lane_3.jpg sample_images/lane_4.jpg
=== Intelligent Transportation System — mode=yolo ===
Lane Vehicles Density Green (s)
----------------------------------------
lane_1 17 0.850 45
lane_2 13 0.650 34
lane_3 7 0.350 18
lane_4 9 0.450 23
----------------------------------------
Total 120s (cycle=120s)The busiest lane (17 vehicles) gets 45 seconds of green; the emptiest (7 vehicles) gets 18 seconds. A fixed-timer controller would have given them 30 s each.
git clone https://github.com/aniruddhgoteti/Intelligent-Transportation-System.git
cd Intelligent-Transportation-System
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Web UI
streamlit run app.py
# or CLI
python -m its --mode classical sample_images/lane_1.jpg sample_images/lane_2.jpg \
sample_images/lane_3.jpg sample_images/lane_4.jpgRequires Python 3.10+. YOLOv8 weights (yolov8n.pt, ~6 MB) are fetched by
Ultralytics on first use and cached locally.
┌───────────┐ ┌────────────────┐ ┌──────────────┐ ┌──────────────┐
│ Lane image│→→→│ Density module │→→→│ Density │→→→│ Signal │
│ (BGR np) │ │ classical/yolo │ │ ∈ [0, 1] │ │ allocator │
└───────────┘ └────────────────┘ └──────────────┘ └──────┬───────┘
↓
green times (seconds)
Classical density (its/density/classical.py)
- Convert to grayscale and Gaussian-blur (σ ≈ 1.2).
- Canny edge detection with hysteresis thresholds 80 / 200.
- One iteration of morphological dilation to join fragmented edges.
density = count_nonzero(edges) / edges.size→ already normalized to[0, 1].
This faithfully captures the intent of the original B.Tech project's
BgEdgeDetect + TrafficDensity pipeline (~2,000 lines of hand-rolled Java
pixel math), now ~30 lines of OpenCV.
YOLO density (its/density/yolo.py)
- Lazy-load
yolov8n.ptfrom Ultralytics (cached after first run). - Keep only COCO vehicle classes:
car,motorcycle,bus,truck. density = min(total_vehicles / SATURATION, 1.0)withSATURATION = 20by default.
Signal allocator (its/signal.py)
Given a density vector d₁…dₙ and a cycle C, each lane gets
gᵢ = round(C · dᵢ / Σd) seconds, then each gᵢ is clamped to
[min_green, max_green] and the residual is redistributed in density-descending
order so the total hits C exactly. Falls back to an equal split when every
density is zero.
.
├── app.py # Streamlit UI
├── its/
│ ├── __main__.py # `python -m its`
│ ├── cli.py # argparse entrypoint
│ ├── signal.py # green-time allocator
│ └── density/
│ ├── classical.py # OpenCV edge density
│ └── yolo.py # YOLOv8 vehicle counter
├── tests/ # pytest suite
├── sample_images/ # 4 curated lane photos (lane_1..4.jpg)
├── docs/screenshots/ # README imagery
├── legacy-java/ # original 2016 Swing implementation (archived)
├── BTECH Report.docx # original report
├── pyproject.toml
├── requirements.txt
└── .github/workflows/ci.yml
| Layer | Library |
|---|---|
| Image I/O & classical CV | OpenCV 4.8+, NumPy |
| Detection | Ultralytics YOLOv8 |
| UI | Streamlit |
| Testing | pytest |
| CI | GitHub Actions |
pytest -qTwelve tests cover the classical density estimator (zero/noise/grayscale/bounds) and the signal allocator (equal split, proportional allocation, clamping, feasibility guards). YOLO is not exercised in CI to keep it fast.
- Real-time video input (OpenCV
VideoCapture+ sliding-window detection) - Emergency-vehicle priority (siren detection → force green)
- Pedestrian crossing integration
- Multi-intersection coordination (green waves)
- Edge deployment notes (Jetson Nano / Raspberry Pi 5)
The original 2016 Java/Swing implementation is archived under
legacy-java/. The full B.Tech report is at
BTECH Report.docx.
MIT © Aniruddh Goteti.

