Campus-Vision-Lab / index.html
BuildingTHEITGUY's picture
Add plate-masked UD campus field figure; ethics note for public stills
6b288f6 verified
Raw History Blame Contribute Delete
23.7 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CVL-PK-001 · Peak-time campus parking occupancy · UD AI Lab</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" />
<style>
:root {
--bg: #07090c;
--panel: #0e1319;
--panel-2: #121820;
--line: #243041;
--line-strong: #3a4d66;
--ink: #e7edf5;
--muted: #8b9bb0;
--accent: #3dd6c6;
--accent-dim: rgba(61, 214, 198, 0.12);
--warn: #f0b429;
--danger: #ff6b6b;
--ok: #3dd68c;
--mono: "IBM Plex Mono", ui-monospace, Consolas, monospace;
--sans: "IBM Plex Sans", system-ui, -apple-system, Segoe UI, sans-serif;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--bg); }
body {
margin: 0;
color: var(--ink);
background:
radial-gradient(1200px 500px at 10% -10%, rgba(61, 214, 198, 0.07), transparent 55%),
radial-gradient(900px 400px at 90% 0%, rgba(240, 180, 41, 0.04), transparent 50%),
var(--bg);
font: 15px/1.55 var(--sans);
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.frame { max-width: 1080px; margin: 0 auto; padding: 1rem 1rem 3.5rem; }
/* Top classification bar */
.ribbon {
display: flex; flex-wrap: wrap; gap: .45rem .7rem; align-items: center;
padding: .55rem .75rem;
border: 1px solid var(--line);
background: var(--panel);
font-family: var(--mono);
font-size: .68rem;
letter-spacing: .04em;
color: var(--muted);
text-transform: uppercase;
}
.ribbon .sep { opacity: .35; }
.pill {
display: inline-flex; align-items: center; gap: .35rem;
padding: .18rem .45rem;
border: 1px solid var(--line-strong);
background: var(--panel-2);
color: var(--ink);
border-radius: 2px;
}
.pill.live { border-color: rgba(61, 214, 198, .45); color: var(--accent); background: var(--accent-dim); }
.pill.warn { border-color: rgba(240, 180, 41, .45); color: var(--warn); }
/* Masthead */
header.mast {
margin-top: .75rem;
padding: 1.15rem 1rem 1.25rem;
border: 1px solid var(--line);
border-top: none;
background: linear-gradient(180deg, #10161e 0%, var(--panel) 100%);
}
.kicker {
margin: 0 0 .55rem;
font-family: var(--mono);
font-size: .72rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--accent);
}
.doc-id {
float: right;
margin: 0 0 .6rem .8rem;
padding: .35rem .55rem;
border: 1px solid var(--line);
background: var(--bg);
font-family: var(--mono);
font-size: .72rem;
color: var(--muted);
text-align: right;
line-height: 1.35;
}
h1 {
margin: 0 0 .45rem;
font-size: clamp(1.45rem, 2.4vw, 1.85rem);
font-weight: 600;
letter-spacing: -0.02em;
line-height: 1.2;
color: #f4f7fb;
}
.lede {
margin: 0;
max-width: 48rem;
color: var(--muted);
font-size: .98rem;
}
.lede strong { color: var(--ink); font-weight: 500; }
/* KPI strip */
.kpis {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin-top: .9rem;
border: 1px solid var(--line);
background: var(--line);
}
.kpi {
background: var(--panel-2);
padding: .65rem .75rem;
}
.kpi .l {
font-family: var(--mono);
font-size: .65rem;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: .25rem;
}
.kpi .v { font-size: .95rem; font-weight: 600; color: var(--ink); }
.kpi .v em { font-style: normal; color: var(--accent); }
/* Layout: toc + content */
.layout {
display: grid;
grid-template-columns: 200px minmax(0, 1fr);
gap: 1rem;
margin-top: 1rem;
align-items: start;
}
nav.toc {
position: sticky;
top: .75rem;
padding: .75rem;
border: 1px solid var(--line);
background: var(--panel);
}
nav.toc .t {
font-family: var(--mono);
font-size: .65rem;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--muted);
margin: 0 0 .55rem;
}
nav.toc a {
display: block;
padding: .28rem 0;
color: var(--muted);
font-size: .84rem;
border-bottom: 1px solid transparent;
}
nav.toc a:hover { color: var(--accent); text-decoration: none; }
nav.toc a span { color: var(--accent); font-family: var(--mono); font-size: .72rem; margin-right: .35rem; }
article {
border: 1px solid var(--line);
background: var(--panel);
padding: 0;
min-width: 0;
}
section {
padding: 1.15rem 1.1rem 1.25rem;
border-bottom: 1px solid var(--line);
}
section:last-child { border-bottom: none; }
h2 {
margin: 0 0 .7rem;
font-size: 1.02rem;
font-weight: 600;
letter-spacing: -0.01em;
display: flex;
align-items: baseline;
gap: .5rem;
color: #f4f7fb;
}
h2 .n {
font-family: var(--mono);
font-size: .78rem;
color: var(--accent);
letter-spacing: .04em;
}
p { margin: 0 0 .7rem; }
ul, ol { margin: 0 0 .8rem; padding-left: 1.15rem; }
li { margin: .28rem 0; }
ol.proc > li { margin: .65rem 0; }
figure { margin: .85rem 0 1rem; }
figure img {
width: 100%; height: auto; display: block;
border: 1px solid var(--line);
background: #000;
}
figcaption {
margin-top: .4rem;
font-size: .82rem;
color: var(--muted);
line-height: 1.4;
}
figcaption strong {
font-family: var(--mono);
font-size: .72rem;
letter-spacing: .04em;
text-transform: uppercase;
color: var(--accent);
font-weight: 500;
margin-right: .35rem;
}
table {
width: 100%;
border-collapse: collapse;
font-size: .88rem;
margin: .45rem 0 .9rem;
}
caption {
caption-side: top;
text-align: left;
font-family: var(--mono);
font-size: .68rem;
letter-spacing: .06em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: .4rem;
}
th, td {
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
padding: .42rem .5rem;
text-align: left;
vertical-align: top;
}
th {
background: var(--panel-2);
color: var(--ink);
font-weight: 600;
font-size: .78rem;
letter-spacing: .03em;
text-transform: uppercase;
}
td.mono, .mono { font-family: var(--mono); font-size: .8rem; }
.bands {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: .45rem;
margin: .55rem 0 1rem;
}
.band {
border: 1px solid var(--line);
background: var(--panel-2);
padding: .55rem .6rem;
}
.band .r {
font-family: var(--mono);
font-size: .68rem;
color: var(--muted);
margin-bottom: .15rem;
}
.band .n { font-weight: 600; font-size: .92rem; }
.band.quiet .n { color: var(--ok); }
.band.mixed .n { color: #8ec8ff; }
.band.peak .n { color: var(--warn); }
.band.stress .n { color: var(--danger); }
.callout {
margin: .85rem 0 0;
padding: .7rem .8rem;
border-left: 3px solid var(--warn);
background: rgba(240, 180, 41, 0.07);
border-top: 1px solid var(--line);
border-right: 1px solid var(--line);
border-bottom: 1px solid var(--line);
font-size: .9rem;
}
.callout strong { color: var(--warn); }
code, pre, .hljs { font-family: var(--mono); font-size: .78rem; }
:not(pre) > code {
background: #18212c;
color: #9fe8de;
padding: .08rem .3rem;
border-radius: 2px;
border: 1px solid var(--line);
}
pre { margin: .45rem 0 .75rem; }
pre code {
border-radius: 2px;
border: 1px solid var(--line);
background: #06080b !important;
}
.file-row {
display: flex; flex-wrap: wrap; gap: .45rem;
margin: .4rem 0 .8rem;
}
.file-row a {
display: inline-block;
padding: .35rem .55rem;
border: 1px solid var(--line-strong);
background: var(--panel-2);
color: var(--ink);
font-family: var(--mono);
font-size: .74rem;
text-decoration: none;
}
.file-row a:hover { border-color: var(--accent); color: var(--accent); }
footer.colophon {
margin-top: 1rem;
padding: .75rem .85rem;
border: 1px solid var(--line);
background: var(--panel);
color: var(--muted);
font-size: .78rem;
font-family: var(--mono);
}
@media (max-width: 860px) {
.layout { grid-template-columns: 1fr; }
nav.toc { position: static; }
.kpis, .bands { grid-template-columns: 1fr 1fr; }
.doc-id { float: none; display: block; margin: 0 0 .7rem; text-align: left; }
}
</style>
</head>
<body>
<div class="frame">
<div class="ribbon">
<span class="pill live">UD AI Lab</span>
<span class="sep">/</span>
<span>Campus Vision</span>
<span class="sep">/</span>
<span>Parking occupancy</span>
<span class="sep">·</span>
<span class="pill">Internal teaching stack</span>
<span class="pill warn">No enforcement</span>
<span class="pill">Local inference only</span>
</div>
<header class="mast">
<div class="doc-id">Doc ID CVL-PK-001<br>Rev B · 2026-09-15<br>Static protocol</div>
<p class="kicker">University of Dubai · Artificial Intelligence Lab · Computer Vision</p>
<h1>Peak-time campus parking occupancy</h1>
<p class="lede">
Measurement brief for a <strong>still-photo occupancy estimate</strong> on an 8&nbsp;GB teaching GPU.
Counts vehicles with YOLO; optional lot caption with SmolVLM.
This Space is the <strong>canonical protocol and source tree</strong> — models do not run in the browser.
</p>
<div class="kpis">
<div class="kpi"><div class="l">Sensing</div><div class="v">Still photo / webcam</div></div>
<div class="kpi"><div class="l">Detector</div><div class="v"><em>YOLOv8s</em> · 1280 px</div></div>
<div class="kpi"><div class="l">Caption</div><div class="v">SmolVLM-500M</div></div>
<div class="kpi"><div class="l">Hardware</div><div class="v">≥ 8 GB NVIDIA · local</div></div>
</div>
</header>
<div class="layout">
<nav class="toc" aria-label="Contents">
<p class="t">Contents</p>
<a href="#s0"><span>00</span> Run look</a>
<a href="#s1"><span>01</span> Concept</a>
<a href="#s2"><span>02</span> Scope</a>
<a href="#s3"><span>03</span> Materials</a>
<a href="#s4"><span>04</span> Procedure</a>
<a href="#s5"><span>05</span> Code</a>
<a href="#s6"><span>06</span> Files</a>
<a href="#s7"><span>07</span> Report</a>
</nav>
<article>
<section id="s0">
<h2><span class="n">00</span> How a run looks</h2>
<p>Illustrative figures (not a live capture of a UD lot). Prefer the captions below if burned-in text in the images is imperfect.</p>
<figure>
<img src="figures/fig1-experiment-window.png" alt="Figure 1. Local experiment window." />
<figcaption><strong>Fig 1</strong> Local Gradio window after <code>app.py</code>. Left: lot photo. Right: vehicle boxes. Bottom: count, occupancy, Quiet / Mixed / Peak / Stress.</figcaption>
</figure>
<figure>
<img src="figures/fig2-sample-occupancy.png" alt="Figure 2. Sample occupancy overlay." />
<figcaption><strong>Fig 2</strong> Example walkway view with boxes and a one-line readout. Your capture will match your campus geometry, not this drawing.</figcaption>
</figure>
<figure>
<img src="figures/fig3-campus-masked-detections.png" alt="Figure 3. Real UD lot capture with vehicle boxes and plate zones masked." />
<figcaption><strong>Fig 3</strong> 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.</figcaption>
</figure>
</section>
<section id="s1">
<h2><span class="n">01</span> Concept</h2>
<p>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?</p>
<pre><code class="language-python">occupancy_pct = min(100, 100 * vehicle_count / stall_capacity)</code></pre>
<p><code>vehicle_count</code> is YOLO boxes whose COCO class is <code>car</code>, <code>truck</code>, <code>bus</code>, or <code>motorcycle</code>. <code>stall_capacity</code> is typed by the experimenter (whole lot or a counted subsection).</p>
<div class="bands">
<div class="band quiet"><div class="r">&lt; 40%</div><div class="n">Quiet</div></div>
<div class="band mixed"><div class="r">40–74%</div><div class="n">Mixed</div></div>
<div class="band peak"><div class="r">75–89%</div><div class="n">Peak</div></div>
<div class="band stress"><div class="r">≥ 90%</div><div class="n">Stress</div></div>
</div>
<table>
<caption>Table 1 · Occupancy flags (lab heuristics, not facilities policy)</caption>
<tr><th>occupancy_pct</th><th>flag</th><th>ops reading</th></tr>
<tr><td class="mono">&lt; 40</td><td>Quiet</td><td>Spare capacity</td></tr>
<tr><td class="mono">40–74</td><td>Mixed</td><td>Normal load</td></tr>
<tr><td class="mono">75–89</td><td>Peak</td><td>Few stalls left</td></tr>
<tr><td class="mono">≥ 90</td><td>Stress</td><td>Treat as full / send overflow</td></tr>
</table>
<p>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.</p>
</section>
<section id="s2">
<h2><span class="n">02</span> What this experiment is not</h2>
<ul>
<li>Not a live camera network or cloud fleet.</li>
<li>Not plate read, face ID, or enforcement.</li>
<li>Not a model running inside this web page.</li>
</ul>
</section>
<section id="s3">
<h2><span class="n">03</span> Materials</h2>
<p>Download experiment files from this Space first. Do not depend on a private disk path on one lab PC.</p>
<table>
<caption>Table 2 · Bill of materials</caption>
<tr><th>Item</th><th>Source</th></tr>
<tr>
<td>Experiment files (<code>app.py</code>, <code>requirements.txt</code>, this page)</td>
<td><a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/tree/main">Space tree/main</a> ·
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/app.py">app.py</a> ·
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/requirements.txt">requirements.txt</a></td>
</tr>
<tr><td>Python 3.11+</td><td><a href="https://www.python.org/downloads/windows/">python.org</a> or Microsoft Store Python 3.13</td></tr>
<tr><td>Git</td><td><a href="https://git-scm.com/download/win">git-scm.com/download/win</a></td></tr>
<tr><td>huggingface_hub / <code>hf</code> CLI</td><td><a href="https://huggingface.co/docs/huggingface_hub/en/guides/cli">HF Hub CLI</a></td></tr>
<tr><td>PyTorch CUDA 12.8</td><td><a href="https://pytorch.org/get-started/locally/">pytorch.org</a> · <a href="https://download.pytorch.org/whl/cu128">cu128 wheels</a></td></tr>
<tr><td>YOLOv8 small</td><td><a href="https://github.com/ultralytics/ultralytics">ultralytics</a> · <code>yolov8s.pt</code> auto-downloads</td></tr>
<tr><td>SmolVLM-500M-Instruct</td><td><a href="https://huggingface.co/HuggingFaceTB/SmolVLM-500M-Instruct">HuggingFaceTB/SmolVLM-500M-Instruct</a></td></tr>
<tr><td>Gradio / Transformers</td><td><a href="https://www.gradio.app/">gradio.app</a> · <a href="https://huggingface.co/docs/transformers">transformers</a></td></tr>
<tr><td>COCO classes</td><td><a href="https://docs.ultralytics.com/datasets/detect/coco/">Ultralytics COCO detect</a></td></tr>
<tr><td>Photos</td><td>Your own UD lot stills or webcam. No gated third-party driving sets.</td></tr>
</table>
</section>
<section id="s4">
<h2><span class="n">04</span> Procedure</h2>
<p>Clone or download this Space into any working directory, then run there.</p>
<ol class="proc">
<li><strong>Confirm Python and GPU</strong>
<pre><code class="language-powershell">python --version
nvidia-smi</code></pre>
Need Python 3.11+ and an NVIDIA GPU. Reference machine: RTX 5070 8&nbsp;GB with CUDA 12.8 wheels.</li>
<li><strong>Get the experiment from Hugging Face (pick one)</strong>
<pre><code class="language-powershell">git clone https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab
cd Campus-Vision-Lab</code></pre>
or without git:
<pre><code class="language-powershell">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</code></pre>
Direct: <a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/app.py">resolve/main/app.py</a>,
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/requirements.txt">requirements.txt</a>.</li>
<li><strong>Virtual environment</strong>
<pre><code class="language-powershell">python -m venv .venv
.venv\Scripts\python.exe -m pip install --upgrade pip</code></pre></li>
<li><strong>Install GPU PyTorch, then requirements</strong>
<pre><code class="language-powershell">.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</code></pre>
Plain <code>pip install torch</code> from PyPI may give a CPU wheel.</li>
<li><strong>Check CUDA</strong>
<pre><code class="language-powershell">.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')"</code></pre>
On the lab 5070 you should see <code>True NVIDIA GeForce RTX 5070 Laptop GPU</code>.</li>
<li><strong>Start the app</strong>
<pre><code class="language-powershell">.venv\Scripts\python.exe -u app.py</code></pre>
Open the printed local URL (usually <code>http://127.0.0.1:7860</code>). First click downloads <code>yolov8s.pt</code> and SmolVLM into the machine cache.</li>
<li><strong>Field capture.</strong> 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.</li>
<li><strong>UI path:</strong> lot → window → capacity → photo / webcam → Estimate occupancy.</li>
<li><strong>Notebook:</strong> datetime, lot, window, capacity, counts, occupancy_pct, flag, caption, occlusion note. Repeat the same lot at a second window.</li>
</ol>
</section>
<section id="s5">
<h2><span class="n">05</span> Code you should understand</h2>
<p>Measurement in <code>app.py</code>:</p>
<pre><code class="language-python">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))</code></pre>
<p>Detector without Gradio:</p>
<pre><code class="language-python">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)</code></pre>
<p>Default nano at 640&nbsp;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.</p>
<p>Optional caption:</p>
<pre><code class="language-python">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])</code></pre>
</section>
<section id="s6">
<h2><span class="n">06</span> Files on this Space</h2>
<div class="file-row">
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/app.py">app.py</a>
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/resolve/main/requirements.txt">requirements.txt</a>
<a href="https://huggingface.co/spaces/BuildingTHEITGUY/Campus-Vision-Lab/tree/main">tree/main</a>
</div>
<div class="callout"><strong>Ethics / ops bound.</strong> 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.</div>
</section>
<section id="s7">
<h2><span class="n">07</span> Report checklist</h2>
<ol>
<li>Two photos, same lot, two windows.</li>
<li>Capacity, and how you got it.</li>
<li>Counts, occupancy_pct, flag, caption.</li>
<li>One limitation (occlusion, night, cropped aisle, …).</li>
<li>One follow-up change.</li>
</ol>
</section>
</article>
</div>
<footer class="colophon">
University of Dubai AI Lab · CVL-PK-001 · MIT for our app code · Ultralytics, SmolVLM, and PyTorch keep their own licenses · Not Waymo / Tesla / RTA affiliated
</footer>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
</body>
</html>