0. How a run looks
Illustrative figures (not a live capture of a UD lot). Text burned into the pictures may be imperfect; read the captions here.
app.py. Left: your lot photo. Right: vehicle boxes. Bottom: count, occupancy, quiet/mixed/peak/stress.
1. Concept
Peak arrival and class-change windows fill University of Dubai lots. The measurement is: from one still photograph, how many vehicles are visible, and what fraction is that of the stall capacity you assign to that lot?
occupancy_pct = min(100, 100 * vehicle_count / stall_capacity)
vehicle_count is YOLO boxes whose COCO class is car, truck, bus, or motorcycle. stall_capacity is typed by the experimenter (whole lot or a counted subsection).
| occupancy_pct | flag |
|---|---|
| < 40 | Quiet |
| 40–74 | Mixed |
| 75–89 | Peak |
| ≥ 90 | Stress (treat as full / send overflow) |
Flags are lab heuristics, not facilities policy. Occlusion is part of the write-up. SmolVLM-500M adds two sentences on how full the lot looks. If caption and count disagree, record both.
2. What this experiment is not
- Not a live camera network.
- Not plate read, face ID, or enforcement.
- Not a model running inside this web page.
3. Materials (download from Hugging Face first)
| Item | Get it from |
|---|---|
Experiment files (app.py, requirements.txt, this page) |
Space tree/main · app.py · requirements.txt |
| Python 3.11+ | python.org or Microsoft Store Python 3.13 |
| Git (for clone) | git-scm.com/download/win |
huggingface_hub / hf CLI |
HF Hub CLI |
| PyTorch CUDA 12.8 | pytorch.org/get-started/locally · wheels cu128 |
| YOLOv8 nano | ultralytics · docs · yolov8n.pt auto-downloads |
| SmolVLM-500M-Instruct | HuggingFaceTB/SmolVLM-500M-Instruct |
| Gradio / Transformers | gradio.app · transformers |
| COCO classes | Ultralytics COCO detect |
| Photos | Your own UD lot stills or webcam. No gated third-party driving sets. |
4. Procedure — files from Hugging Face, not a private disk path
Do not depend on a path on one lab PC. Clone or download this Space into any working directory, then run there.
- Confirm Python and GPU
Need Python 3.11+ and an NVIDIA GPU. The reference machine is an RTX 5070 8 GB with CUDA 12.8 wheels.python --version nvidia-smi - Get the experiment from Hugging Face (pick one)
or, without git:git clone https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab cd Campus-Vision-Lab
Direct files: resolve/main/app.py, requirements.txt.python -m pip install -U huggingface_hub hf download BuildingTHEITGUY/Campus-Vision-Lab --repo-type space --local-dir Campus-Vision-Lab cd Campus-Vision-Lab - Virtual environment (once, inside the downloaded folder)
python -m venv .venv .venv\Scripts\python.exe -m pip install --upgrade pip - Install GPU PyTorch, then requirements
Plain.venv\Scripts\python.exe -m pip install torch torchvision --index-url https://download.pytorch.org/whl/cu128 .venv\Scripts\python.exe -m pip install -r requirements.txtpip install torchfrom PyPI may give a CPU wheel. - Check CUDA
On the lab 5070 you should see.venv\Scripts\python.exe -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'cpu')"True NVIDIA GeForce RTX 5070 Laptop GPU. - Start the app
Open the local URL printed in the terminal (usually.venv\Scripts\python.exe -u app.pyhttp://127.0.0.1:7860). First click downloadsyolov8n.ptand SmolVLM into the Hugging Face cache on that machine. - Field capture. Choose lot and window first (morning 07:30–09:00, class change, lunch, evening, weekend). Photograph so stalls and empty bays are both visible. Write the capacity you will type.
- UI: lot, window, capacity → photo or webcam → Estimate occupancy.
- Notebook: datetime, lot, window, capacity, counts, occupancy_pct, flag, caption, occlusion note. Repeat the same lot at a second window.
5. Code you should understand
Measurement in app.py:
VEHICLE = {"car", "truck", "bus", "motorcycle"}
for b in result.boxes:
name = result.names[int(b.cls.item())]
if name in VEHICLE:
counts[name] = counts.get(name, 0) + 1
total = sum(counts.values())
occupancy_pct = min(100.0, 100.0 * total / max(1, stall_capacity))
Detector without Gradio:
from ultralytics import YOLO
from PIL import Image
model = YOLO("yolov8n.pt")
im = Image.open("lot.jpg")
r = model.predict(im, verbose=False)[0]
VEHICLE = {"car", "truck", "bus", "motorcycle"}
n = sum(1 for b in r.boxes if r.names[int(b.cls)] in VEHICLE)
print("vehicles", n, "occupancy%", 100 * n / 40)
Optional caption:
from transformers import AutoProcessor, AutoModelForVision2Seq
import torch
from PIL import Image
mid = "HuggingFaceTB/SmolVLM-500M-Instruct"
proc = AutoProcessor.from_pretrained(mid)
model = AutoModelForVision2Seq.from_pretrained(mid, torch_dtype=torch.float16).cuda()
im = Image.open("lot.jpg")
messages = [{"role": "user", "content": [
{"type": "image"},
{"type": "text", "text": "In two sentences: empty, mixed, or packed? No plates. No faces."},
]}]
text = proc.apply_chat_template(messages, add_generation_prompt=True)
inp = proc(text=text, images=[im], return_tensors="pt")
inp = {k: v.cuda() if hasattr(v, "to") else v for k, v in inp.items()}
out = model.generate(**inp, max_new_tokens=80)
print(proc.batch_decode(out, skip_special_tokens=True)[0])
6. Files on this Space
7. Report checklist
- Two photos, same lot, two windows.
- Capacity, and how you got it.
- Counts, occupancy_pct, flag, caption.
- One limitation (occlusion, night, cropped aisle, …).
- One follow-up change.