UD AI Lab / Campus Vision / Parking occupancy · Internal teaching stack No enforcement Local inference only
Doc ID CVL-PK-001
Rev B · 2026-09-15
Static protocol

University of Dubai · Artificial Intelligence Lab · Computer Vision

Peak-time campus parking occupancy

Measurement brief for a still-photo occupancy estimate on an 8 GB teaching GPU. Counts vehicles with YOLO; optional lot caption with SmolVLM. This Space is the canonical protocol and source tree — models do not run in the browser.

Sensing
Still photo / webcam
Detector
YOLOv8s · 1280 px
Caption
SmolVLM-500M
Hardware
≥ 8 GB NVIDIA · local

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.

Figure 1. Local experiment window.
Fig 1 Local Gradio window after app.py. Left: lot photo. Right: vehicle boxes. Bottom: count, occupancy, Quiet / Mixed / Peak / Stress.
Figure 2. Sample occupancy overlay.
Fig 2 Example walkway view with boxes and a one-line readout. Your capture will match your campus geometry, not this drawing.
Figure 3. Real UD lot capture with vehicle boxes and plate zones masked.
Fig 3 Real University of Dubai lot still (field capture). Vehicle boxes only. Plate zones and people are blacked out in the publishable frame — no plate OCR. Safe to host on this Space.

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).

< 40%
Quiet
40–74%
Mixed
75–89%
Peak
≥ 90%
Stress
Table 1 · Occupancy flags (lab heuristics, not facilities policy)
occupancy_pctflagops reading
< 40QuietSpare capacity
40–74MixedNormal load
75–89PeakFew stalls left
≥ 90StressTreat 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.

Table 2 · Bill of materials
ItemSource
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
Gitgit-scm.com/download/win
huggingface_hub / hf CLIHF Hub CLI
PyTorch CUDA 12.8pytorch.org · cu128 wheels
YOLOv8 smallultralytics · yolov8s.pt auto-downloads
SmolVLM-500M-InstructHuggingFaceTB/SmolVLM-500M-Instruct
Gradio / Transformersgradio.app · transformers
COCO classesUltralytics COCO detect
PhotosYour 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.

  1. Confirm Python and GPU
    python --version
    nvidia-smi
    Need Python 3.11+ and an NVIDIA GPU. Reference machine: RTX 5070 8 GB with CUDA 12.8 wheels.
  2. Get the experiment from Hugging Face (pick one)
    git clone https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab
    cd Campus-Vision-Lab
    or without git:
    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
    Direct: resolve/main/app.py, requirements.txt.
  3. Virtual environment
    python -m venv .venv
    .venv\Scripts\python.exe -m pip install --upgrade pip
  4. Install GPU PyTorch, then requirements
    .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.txt
    Plain pip install torch from PyPI may give a CPU wheel.
  5. Check CUDA
    .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')"
    On the lab 5070 you should see True NVIDIA GeForce RTX 5070 Laptop GPU.
  6. Start the app
    .venv\Scripts\python.exe -u app.py
    Open the printed local URL (usually http://127.0.0.1:7860). First click downloads yolov8s.pt and SmolVLM into the machine cache.
  7. 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.
  8. UI path: lot → window → capacity → photo / webcam → Estimate occupancy.
  9. 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

Ethics / ops bound. Use photos you took of UD lots. Before any public figure: run the app (or the redaction script) so plate zones and people are masked. No plate OCR. No person ID. Not an enforcement decision. Treat Stress as a teaching flag only.

07 Report checklist

  1. Two photos, same lot, two windows.
  2. Capacity, and how you got it.
  3. Counts, occupancy_pct, flag, caption.
  4. One limitation (occlusion, night, cropped aisle, …).
  5. One follow-up change.