00 How a run looks
Illustrative figures (not a live capture of a UD lot). Prefer the captions below if burned-in text in the images is imperfect.
app.py. Left: lot photo. Right: vehicle boxes. Bottom: count, occupancy, Quiet / Mixed / Peak / Stress.
01 Concept
Peak arrival and class-change windows fill University of Dubai lots. From one still photograph: how many vehicles are visible, and what fraction is that of the stall capacity you assign?
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 | ops reading |
|---|---|---|
| < 40 | Quiet | Spare capacity |
| 40–74 | Mixed | Normal load |
| 75–89 | Peak | Few stalls left |
| ≥ 90 | Stress | Treat as full / send overflow |
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.
02 What this experiment is not
- Not a live camera network or cloud fleet.
- Not plate read, face ID, or enforcement.
- Not a model running inside this web page.
03 Materials
Download experiment files from this Space first. Do not depend on a private disk path on one lab PC.
| Item | Source |
|---|---|
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 | git-scm.com/download/win |
huggingface_hub / hf CLI | HF Hub CLI |
| PyTorch CUDA 12.8 | pytorch.org · cu128 wheels |
| YOLOv8 small | ultralytics · yolov8s.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. |
04 Procedure
Clone or download this Space into any working directory, then run there.
- Confirm Python and GPU
Need Python 3.11+ and an NVIDIA GPU. Reference machine: 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: 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
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 printed local URL (usually.venv\Scripts\python.exe -u app.pyhttp://127.0.0.1:7860). First click downloadsyolov8s.ptand SmolVLM into the machine cache. - Field capture. Set lot and window (morning 07:30–09:00, class change, lunch, evening, weekend). Photograph so stalls and empty bays are both visible. Record the capacity you will type.
- UI path: lot → window → capacity → photo / webcam → Estimate occupancy.
- Notebook: datetime, lot, window, capacity, counts, occupancy_pct, flag, caption, occlusion note. Repeat the same lot at a second window.
05 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("yolov8s.pt")
im = Image.open("lot.jpg")
r = model.predict(im, verbose=False, conf=0.12, imgsz=1280, classes=[2, 3, 5, 7])[0]
print("vehicles", len(r.boxes), "occupancy%", 100 * len(r.boxes) / 40)
Default nano at 640 px often misses a far or pale car and will box a shade as a table. The app uses small weights, a larger image, and vehicle classes only.
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])
06 Files on this Space
07 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.