From 3a995f7c3278afd477835b0430e5447eaa3309e9 Mon Sep 17 00:00:00 2001 From: SirBlob Date: Sun, 12 Apr 2026 09:28:14 -0400 Subject: [PATCH] Final Update --- backend/vision.py | 19 +- scripts/generate_fake_data.py | 6 + .../src/lib/components/InteractiveMap.svelte | 19 ++ .../src/lib/components/LocationPopup.svelte | 42 ++++- website/src/routes/+layout.svelte | 6 + website/src/routes/vision/+page.svelte | 169 ++++++++++++++++++ 6 files changed, 246 insertions(+), 15 deletions(-) create mode 100644 website/src/routes/vision/+page.svelte diff --git a/backend/vision.py b/backend/vision.py index 6eabd0d..0a1d718 100644 --- a/backend/vision.py +++ b/backend/vision.py @@ -76,23 +76,28 @@ def analyze_room_image(image_bytes: bytes): row_ind, col_ind = linear_sum_assignment(dist_matrix) + DISTANCE_THRESHOLD = 800.0 # Increased threshold to ensure grouping even if they are far in the camera view + for person_idx, chair_idx in zip(row_ind, col_ind): distance = float(dist_matrix[person_idx, chair_idx]) - pairs.append({ - "person_index": int(person_idx), - "chair_index": int(chair_idx), - "distance": distance - }) + if distance <= DISTANCE_THRESHOLD: + pairs.append({ + "person_index": int(person_idx), + "chair_index": int(chair_idx), + "distance": distance + }) - is_full = num_people >= num_chairs if num_chairs > 0 else False + available_chairs = num_chairs - len(pairs) + is_full = available_chairs <= 0 if num_chairs > 0 else False return { "room_status": "full" if is_full else "available", "counts": { "people": num_people, - "chairs": num_chairs + "chairs": num_chairs, + "available_chairs": available_chairs }, "pairs": pairs, "details": { diff --git a/scripts/generate_fake_data.py b/scripts/generate_fake_data.py index 42f435e..eddc8c3 100644 --- a/scripts/generate_fake_data.py +++ b/scripts/generate_fake_data.py @@ -83,6 +83,11 @@ def generate_fake_data(): for loc in UMD_LOCATIONS: db_level = get_db_for_time_and_location(hour, loc["id"]) + + # Estimate people based on noise level. + # 35dB = ~0 people. Every 1.5 dB above 35 adds ~1 person. + base_people = max(0, (db_level - 35) * 1.5) + people_count = int(max(0, base_people + random.uniform(-5, 10))) doc = { "room_id": loc["id"], @@ -91,6 +96,7 @@ def generate_fake_data(): "coordinates": [loc["lng"], loc["lat"]] }, "db": round(db_level, 2), + "people": people_count, "date": current_time } docs_to_insert.append(doc) diff --git a/website/src/lib/components/InteractiveMap.svelte b/website/src/lib/components/InteractiveMap.svelte index 03775f7..0290446 100644 --- a/website/src/lib/components/InteractiveMap.svelte +++ b/website/src/lib/components/InteractiveMap.svelte @@ -92,6 +92,7 @@ properties: { room_id: room.room_id, db: room.db, + people: room.people || 0, name: locName, date: room.date, }, @@ -114,6 +115,7 @@ properties: { room_id: loc.id, db: 0, + people: 0, name: loc.name, date: new Date().toISOString(), }, @@ -263,6 +265,19 @@ } const rawName = props.name.split("\\n")[0].split("\n")[0]; + + let occStatus = "Quiet"; + let occColor = "#94e2d5"; + if (props.people > 60) { + occStatus = "Packed"; + occColor = "#f38ba8"; + } else if (props.people > 25) { + occStatus = "Moderate"; + occColor = "#f9e2af"; + } else if (props.people === 0 && props.db === 0) { + occStatus = "No Data"; + occColor = "#888888"; + } return `
@@ -272,6 +287,10 @@ ${status}

Noise: ${props.db > 0 ? props.db.toFixed(1) + " dB" : "N/A"}

+

+ Occupancy: ${props.people} + (${occStatus}) +

Last updated: ${timeStr}

`; diff --git a/website/src/lib/components/LocationPopup.svelte b/website/src/lib/components/LocationPopup.svelte index 616b8ea..1bac6a6 100644 --- a/website/src/lib/components/LocationPopup.svelte +++ b/website/src/lib/components/LocationPopup.svelte @@ -6,7 +6,7 @@ let Chart: any; - let chartCanvas: HTMLCanvasElement; + let chartCanvas: HTMLCanvasElement | undefined = $state(); let chartInstance: any; function closePopup() { @@ -46,8 +46,24 @@ return Math.round(sum / points.length); }); + let currentPeople = $derived.by(() => { + if (!mapState.selectedLocation || !mapState.historyData) return 0; + const points = mapState.historyData + .filter(d => d.room_id === mapState.selectedLocation?.id) + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + if (points.length === 0) return 0; + return points[0].people || 0; + }); + let statusColor = $derived(getChartColor(current2hAvg, themeState.isLight, themeState.isColorBlindFriendly)); let statusLabel = $derived(getStatusLabel(current2hAvg)); + + let occStatus = $derived.by(() => { + if (currentPeople > 60) return { label: "Packed", color: "#f38ba8" }; + if (currentPeople > 25) return { label: "Moderate", color: "#f9e2af" }; + if (currentPeople === 0 && current2hAvg === 0) return { label: "No Data", color: "#888888" }; + return { label: "Quiet", color: "#94e2d5" }; + }); function drawChart() { if (!Chart || !chartCanvas || !mapState.selectedLocation) return; @@ -65,8 +81,8 @@ const isCB = themeState.isColorBlindFriendly; const ctx = chartCanvas.getContext('2d'); - let gradientLine = statusColor; - let gradientFill = statusColor + '33'; + let gradientLine: any = statusColor; + let gradientFill: any = statusColor + '33'; if (ctx) { gradientLine = ctx.createLinearGradient(0, 0, 0, 200); @@ -95,7 +111,7 @@ borderWidth: 3, fill: true, segment: { - borderColor: ctx => getChartColor(ctx.p1.parsed.y, isLight, isCB) + borderColor: (ctx: any) => getChartColor(ctx.p1.parsed.y, isLight, isCB) }, tension: 0.5, pointRadius: 0, @@ -145,6 +161,7 @@ onMount(async () => { const chartModule = await import('chart.js/auto'); + // @ts-ignore const chartjsAdapter = await import('chartjs-adapter-date-fns'); Chart = chartModule.default; if (mapState.selectedLocation) drawChart(); @@ -163,10 +180,19 @@

{mapState.selectedLocation.name}

-
-
- {statusLabel} - · {current2hAvg} dB Avg (Last 2h) + +
+
+
+ {statusLabel} + · {current2hAvg} dB Avg (Last 2h) +
+ +
+ + {occStatus.label} + · {currentPeople} people currently +
diff --git a/website/src/routes/+layout.svelte b/website/src/routes/+layout.svelte index 17fa10e..595964d 100644 --- a/website/src/routes/+layout.svelte +++ b/website/src/routes/+layout.svelte @@ -34,6 +34,12 @@ + + + + + + diff --git a/website/src/routes/vision/+page.svelte b/website/src/routes/vision/+page.svelte new file mode 100644 index 0000000..553d9f3 --- /dev/null +++ b/website/src/routes/vision/+page.svelte @@ -0,0 +1,169 @@ + + + { + // Re-process layout or clear overlay on resize if needed + if (canvasElement) { + const ctx = canvasElement.getContext('2d'); + if (ctx) ctx.clearRect(0, 0, canvasElement.width, canvasElement.height); + } +}} /> + +
+
+ + +
+ +
+

+ Live Vision +

+ +
+ Status + + {roomStatus} + +
+ +
+ People + {peopleCount} +
+ +
+ Available Chairs + {availableChairsCount} / {chairsCount} +
+
+
+ + \ No newline at end of file