BuildingTHEITGUY's picture
Add runnable experiment app.py for download
4b0e223 verified
Raw History Blame Contribute Delete
5.63 kB
"""UD Campus Parking Lab — peak-time lot occupancy. Campus operations only."""
from __future__ import annotations
from functools import lru_cache
import gradio as gr
import numpy as np
from PIL import Image
BANNER = (
"**UD Campus Parking Lab.** Peak-time lot occupancy from a photo you took. "
"Counts vehicles. Estimates how full the lot is. "
"Does **not** read plates, identify people, or store a watchlist."
)
FOOTER_IDLE = "waiting for a lot photo"
YOLO_ID = "yolov8n.pt"
VLM_ID = "HuggingFaceTB/SmolVLM-500M-Instruct"
VEHICLE = {"car", "truck", "bus", "motorcycle"}
LOTS = [
"Academic City — student lot",
"Academic City — staff lot",
"Visitor / short stay",
"Overflow / event parking",
"Other campus lot",
]
WINDOWS = [
"Morning arrival (07:30–09:00)",
"Class change",
"Lunch",
"Evening leave",
"Weekend / off-peak",
]
def device() -> str:
try:
import torch
return "cuda" if torch.cuda.is_available() else "cpu"
except Exception:
return "cpu"
@lru_cache(maxsize=1)
def yolo():
from ultralytics import YOLO
return YOLO(YOLO_ID)
@lru_cache(maxsize=1)
def vlm():
import torch
from transformers import AutoModelForVision2Seq, AutoProcessor
proc = AutoProcessor.from_pretrained(VLM_ID)
model = AutoModelForVision2Seq.from_pretrained(
VLM_ID,
torch_dtype=torch.float16 if device() == "cuda" else torch.float32,
).to(device())
model.eval()
return proc, model
def count_vehicles(image: Image.Image):
result = yolo().predict(image, verbose=False)[0]
plotted = result.plot()[:, :, ::-1]
counts: dict[str, int] = {}
if result.boxes is not None:
names = result.names
for b in result.boxes:
name = names.get(int(b.cls.item()), "")
if name in VEHICLE:
counts[name] = counts.get(name, 0) + 1
total = sum(counts.values())
return Image.fromarray(plotted), counts, total
def lot_note(image: Image.Image, lot: str, window: str) -> str:
import torch
proc, model = vlm()
prompt = (
f"This is a photo of {lot} at {window}. "
"In two sentences, say whether the lot looks empty, mixed, or packed. "
"Mention parked cars and open stalls if you see them. "
"Do not read license plates. Do not name or describe faces."
)
messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}]
text = proc.apply_chat_template(messages, add_generation_prompt=True)
inputs = proc(text=text, images=[image], return_tensors="pt")
inputs = {k: v.to(model.device) if hasattr(v, "to") else v for k, v in inputs.items()}
with torch.inference_mode():
out = model.generate(**inputs, max_new_tokens=80)
return proc.batch_decode(out, skip_special_tokens=True)[0]
def peak_label(pct: float, window: str) -> str:
if pct >= 90:
return "STRESS — treat as full; send overflow"
if pct >= 75:
return "PEAK — few stalls left"
if pct >= 40:
return "MIXED"
if "off-peak" in window.lower() or "Weekend" in window:
return "QUIET (matches off-peak window)"
return "QUIET — not peak yet"
def run(image, lot, window, capacity):
if image is None:
return BANNER, None, "Take or upload a photo of a campus lot.", FOOTER_IDLE
if not isinstance(image, Image.Image):
image = Image.fromarray(np.asarray(image)).convert("RGB")
else:
image = image.convert("RGB")
cap = max(1, int(capacity or 40))
vis, counts, total = count_vehicles(image)
pct = min(100.0, 100.0 * total / cap)
try:
note = lot_note(image, lot, window)
except Exception as e:
note = f"Caption skipped ({type(e).__name__}). Counts still ran."
breakdown = ", ".join(f"{k} {v}" for k, v in sorted(counts.items())) or "none"
status = peak_label(pct, window)
notes = (
f"Lot: {lot}\n"
f"Window: {window}\n"
f"Vehicles counted: {total} ({breakdown})\n"
f"Capacity you set: {cap} stalls\n"
f"Estimated occupancy: {pct:.0f}%\n"
f"Peak flag: {status}\n\n"
f"Scene note:\n{note}\n\n"
"This is a still-photo estimate, not a live sensor feed. "
"Double-count and occlusion will skew the number."
)
footer = (
f"yolo: {YOLO_ID} | vlm: {VLM_ID} | device: {device()} | "
f"vehicles {total}/{cap} | {status}"
)
return BANNER, vis, notes, footer
with gr.Blocks(title="UD Campus Parking Lab") as demo:
gr.Markdown("# UD Campus Parking Lab")
banner = gr.Markdown(BANNER)
gr.Markdown(
"University of Dubai campus operations. "
"Photograph a lot at peak arrival or class change. "
"Set how many stalls that lot has. The app counts vehicles and flags peak stress."
)
with gr.Row():
lot = gr.Dropdown(LOTS, value=LOTS[0], label="Campus lot")
window = gr.Dropdown(WINDOWS, value=WINDOWS[0], label="Time window")
capacity = gr.Number(value=40, precision=0, label="Stall capacity")
with gr.Row():
inp = gr.Image(type="pil", label="Lot photo or webcam", sources=["upload", "webcam", "clipboard"])
out = gr.Image(type="pil", label="Detections (vehicles)")
notes = gr.Textbox(label="Occupancy report", lines=16)
footer = gr.Textbox(label="Footer", value=FOOTER_IDLE)
gr.Button("Estimate occupancy").click(
fn=run, inputs=[inp, lot, window, capacity], outputs=[banner, out, notes, footer]
)
if __name__ == "__main__":
demo.launch()