Spaces:
Running
Running
restored to the safe state
Browse files- backend/engine.py +2 -2
- backend/geometry.py +8 -13
- backend/server.py +1 -1
- frontend/js/initial.js +1 -1
- frontend/js/vehicles.js +9 -57
- frontend/vehicles.html +1 -1
backend/engine.py
CHANGED
|
@@ -180,11 +180,11 @@ def run(model, video_path, line, config, on_frame, save_annotated=False, annotat
|
|
| 180 |
cur_boxes = xyxy
|
| 181 |
cur_ids = ids
|
| 182 |
|
| 183 |
-
for obj_id, c, box
|
| 184 |
cx = int((box[0] + box[2]) / 2)
|
| 185 |
cy = int((box[1] + box[3]) / 2)
|
| 186 |
|
| 187 |
-
heatmap_points.append([cx, cy, float(
|
| 188 |
track_positions[obj_id].append((frame_idx, cx, cy))
|
| 189 |
|
| 190 |
if not valid_line:
|
|
|
|
| 180 |
cur_boxes = xyxy
|
| 181 |
cur_ids = ids
|
| 182 |
|
| 183 |
+
for obj_id, c, box in zip(ids, cls, xyxy):
|
| 184 |
cx = int((box[0] + box[2]) / 2)
|
| 185 |
cy = int((box[1] + box[3]) / 2)
|
| 186 |
|
| 187 |
+
heatmap_points.append([cx, cy, float(r.boxes.conf.cpu().numpy()[list(ids).index(obj_id)])])
|
| 188 |
track_positions[obj_id].append((frame_idx, cx, cy))
|
| 189 |
|
| 190 |
if not valid_line:
|
backend/geometry.py
CHANGED
|
@@ -1,17 +1,12 @@
|
|
| 1 |
-
import
|
| 2 |
-
|
| 3 |
|
| 4 |
def _side(p, a, b):
|
| 5 |
-
|
| 6 |
-
return (cross > 0) - (cross < 0)
|
| 7 |
-
|
| 8 |
|
| 9 |
def _point_to_segment_dist(px, py, ax, ay, bx, by):
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
t =
|
| 15 |
-
|
| 16 |
-
proj_y = ay + t * aby
|
| 17 |
-
return math.hypot(px - proj_x, py - proj_y)
|
|
|
|
| 1 |
+
import numpy as np
|
|
|
|
| 2 |
|
| 3 |
def _side(p, a, b):
|
| 4 |
+
return np.sign((b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]))
|
|
|
|
|
|
|
| 5 |
|
| 6 |
def _point_to_segment_dist(px, py, ax, ay, bx, by):
|
| 7 |
+
A = np.array([ax, ay], dtype=float)
|
| 8 |
+
B = np.array([bx, by], dtype=float)
|
| 9 |
+
P = np.array([px, py], dtype=float)
|
| 10 |
+
AB = B - A
|
| 11 |
+
t = np.clip(np.dot(P - A, AB) / np.dot(AB, AB), 0, 1)
|
| 12 |
+
return np.linalg.norm(P - (A + t * AB))
|
|
|
|
|
|
backend/server.py
CHANGED
|
@@ -469,7 +469,7 @@ async def ws_run(ws: WebSocket):
|
|
| 469 |
if done:
|
| 470 |
break
|
| 471 |
|
| 472 |
-
await asyncio.sleep(0.
|
| 473 |
|
| 474 |
result = task.result() # re-raises any exception from the engine
|
| 475 |
result["report_format"] = report_format
|
|
|
|
| 469 |
if done:
|
| 470 |
break
|
| 471 |
|
| 472 |
+
await asyncio.sleep(0.05)
|
| 473 |
|
| 474 |
result = task.result() # re-raises any exception from the engine
|
| 475 |
result["report_format"] = report_format
|
frontend/js/initial.js
CHANGED
|
@@ -71,7 +71,7 @@ function uploadFile(file) {
|
|
| 71 |
// Estimate upload duration: ~1 MB/s conservative, capped between 3s and 60s
|
| 72 |
const fileMB = file.size / (1024 * 1024);
|
| 73 |
const estDurationMs = Math.min(Math.max(fileMB * 1000, 3000), 60000);
|
| 74 |
-
const targetPct =
|
| 75 |
const tickMs = 200; // update every 200ms
|
| 76 |
const totalTicks = estDurationMs / tickMs;
|
| 77 |
const stepPerTick = targetPct / totalTicks;
|
|
|
|
| 71 |
// Estimate upload duration: ~1 MB/s conservative, capped between 3s and 60s
|
| 72 |
const fileMB = file.size / (1024 * 1024);
|
| 73 |
const estDurationMs = Math.min(Math.max(fileMB * 1000, 3000), 60000);
|
| 74 |
+
const targetPct = 98; // stop simulation at 98%, snap to 100% on load
|
| 75 |
const tickMs = 200; // update every 200ms
|
| 76 |
const totalTicks = estDurationMs / tickMs;
|
| 77 |
const stepPerTick = targetPct / totalTicks;
|
frontend/js/vehicles.js
CHANGED
|
@@ -1011,46 +1011,12 @@ window.switchTab = switchTab;
|
|
| 1011 |
let liveCongestion = [];
|
| 1012 |
let liveFlowTimes = [];
|
| 1013 |
|
| 1014 |
-
// ---- Smooth progress interpolation ----
|
| 1015 |
-
// Predicts progress between real frame updates so the bar never "freezes"
|
| 1016 |
-
let lastRealPct = 0;
|
| 1017 |
-
let lastRealTime = 0;
|
| 1018 |
-
let pctPerMs = 0; // observed speed: % gained per ms
|
| 1019 |
-
let interpPct = 0; // current interpolated display %
|
| 1020 |
-
let interpRAF = null;
|
| 1021 |
-
|
| 1022 |
-
function startProgressInterpolation() {
|
| 1023 |
-
const bar = document.getElementById('proc-bar');
|
| 1024 |
-
const pctEl = document.getElementById('proc-pct');
|
| 1025 |
-
|
| 1026 |
-
function tick() {
|
| 1027 |
-
if (processingDone) return;
|
| 1028 |
-
const now = performance.now();
|
| 1029 |
-
const elapsed = now - lastRealTime;
|
| 1030 |
-
|
| 1031 |
-
if (pctPerMs > 0 && lastRealPct < 99.5) {
|
| 1032 |
-
// Predict forward, but cap at ~halfway to the next expected frame
|
| 1033 |
-
// to avoid overshoot before the next real update arrives
|
| 1034 |
-
const predicted = lastRealPct + (elapsed * pctPerMs * 0.85);
|
| 1035 |
-
const cap = Math.min(predicted, lastRealPct + (pctPerMs * 3000), 99.5);
|
| 1036 |
-
if (cap > interpPct) {
|
| 1037 |
-
interpPct = cap;
|
| 1038 |
-
bar.style.width = interpPct.toFixed(1) + '%';
|
| 1039 |
-
pctEl.innerText = Math.floor(interpPct) + '%';
|
| 1040 |
-
}
|
| 1041 |
-
}
|
| 1042 |
-
interpRAF = requestAnimationFrame(tick);
|
| 1043 |
-
}
|
| 1044 |
-
interpRAF = requestAnimationFrame(tick);
|
| 1045 |
-
}
|
| 1046 |
-
|
| 1047 |
ws.onmessage = e => {
|
| 1048 |
const d = JSON.parse(e.data);
|
| 1049 |
|
| 1050 |
if (!firstMessageReceived) {
|
| 1051 |
firstMessageReceived = true;
|
| 1052 |
document.getElementById('proc-label').innerText = 'Processing';
|
| 1053 |
-
startProgressInterpolation();
|
| 1054 |
}
|
| 1055 |
|
| 1056 |
// Hide empty state on first data
|
|
@@ -1058,7 +1024,6 @@ window.switchTab = switchTab;
|
|
| 1058 |
if (emptyState) emptyState.style.display = 'none';
|
| 1059 |
if (d.error) {
|
| 1060 |
processingDone = true;
|
| 1061 |
-
if (interpRAF) cancelAnimationFrame(interpRAF);
|
| 1062 |
document.getElementById('proc-label').innerText = 'Engine Error';
|
| 1063 |
console.error('[UrbanFlow] Engine error:', d.detail || d.error);
|
| 1064 |
document.getElementById('run-results-content').innerHTML = `
|
|
@@ -1075,7 +1040,6 @@ window.switchTab = switchTab;
|
|
| 1075 |
|
| 1076 |
if (d.done) {
|
| 1077 |
processingDone = true;
|
| 1078 |
-
if (interpRAF) cancelAnimationFrame(interpRAF);
|
| 1079 |
|
| 1080 |
// Stats Tracking (Scoped by email)
|
| 1081 |
const session = (typeof getAuthSession === 'function') ? getAuthSession() : null;
|
|
@@ -1167,27 +1131,14 @@ window.switchTab = switchTab;
|
|
| 1167 |
return;
|
| 1168 |
}
|
| 1169 |
|
| 1170 |
-
|
| 1171 |
-
|
| 1172 |
-
|
| 1173 |
-
|
| 1174 |
-
const now = performance.now();
|
| 1175 |
-
if (lastRealTime > 0 && pct > lastRealPct) {
|
| 1176 |
-
const dt = now - lastRealTime;
|
| 1177 |
-
const dp = pct - lastRealPct;
|
| 1178 |
-
// Exponential moving average of speed for stability
|
| 1179 |
-
const newRate = dp / dt;
|
| 1180 |
-
pctPerMs = pctPerMs > 0 ? (pctPerMs * 0.6 + newRate * 0.4) : newRate;
|
| 1181 |
-
}
|
| 1182 |
-
lastRealPct = pct;
|
| 1183 |
-
lastRealTime = now;
|
| 1184 |
-
|
| 1185 |
-
// Snap bar + percentage to real value (interpolation was just a prediction)
|
| 1186 |
-
interpPct = pct;
|
| 1187 |
-
const pctStr = pct.toFixed(1);
|
| 1188 |
-
document.getElementById('proc-bar').style.width = pctStr + '%';
|
| 1189 |
document.getElementById('proc-frames').innerText = `${d.frame_index} / ${d.total_iters} Frames`;
|
| 1190 |
-
|
|
|
|
|
|
|
|
|
|
| 1191 |
|
| 1192 |
const totalIn = sumValues(d.class_in);
|
| 1193 |
const totalOut = sumValues(d.class_out);
|
|
@@ -1217,7 +1168,8 @@ window.switchTab = switchTab;
|
|
| 1217 |
}
|
| 1218 |
}
|
| 1219 |
|
| 1220 |
-
|
|
|
|
| 1221 |
lastUIUpdate = now;
|
| 1222 |
|
| 1223 |
updateCongestion(liveCongestion, stride);
|
|
|
|
| 1011 |
let liveCongestion = [];
|
| 1012 |
let liveFlowTimes = [];
|
| 1013 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1014 |
ws.onmessage = e => {
|
| 1015 |
const d = JSON.parse(e.data);
|
| 1016 |
|
| 1017 |
if (!firstMessageReceived) {
|
| 1018 |
firstMessageReceived = true;
|
| 1019 |
document.getElementById('proc-label').innerText = 'Processing';
|
|
|
|
| 1020 |
}
|
| 1021 |
|
| 1022 |
// Hide empty state on first data
|
|
|
|
| 1024 |
if (emptyState) emptyState.style.display = 'none';
|
| 1025 |
if (d.error) {
|
| 1026 |
processingDone = true;
|
|
|
|
| 1027 |
document.getElementById('proc-label').innerText = 'Engine Error';
|
| 1028 |
console.error('[UrbanFlow] Engine error:', d.detail || d.error);
|
| 1029 |
document.getElementById('run-results-content').innerHTML = `
|
|
|
|
| 1040 |
|
| 1041 |
if (d.done) {
|
| 1042 |
processingDone = true;
|
|
|
|
| 1043 |
|
| 1044 |
// Stats Tracking (Scoped by email)
|
| 1045 |
const session = (typeof getAuthSession === 'function') ? getAuthSession() : null;
|
|
|
|
| 1131 |
return;
|
| 1132 |
}
|
| 1133 |
|
| 1134 |
+
let pct = ((d.frame_index / d.total_iters) * 100).toFixed(1);
|
| 1135 |
+
if (d.frame_index >= d.total_iters - 1) pct = '100.0';
|
| 1136 |
+
document.getElementById('proc-bar').style.width = pct + '%';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1137 |
document.getElementById('proc-frames').innerText = `${d.frame_index} / ${d.total_iters} Frames`;
|
| 1138 |
+
|
| 1139 |
+
const procPctEl = document.getElementById('proc-pct');
|
| 1140 |
+
const currPct = parseFloat(procPctEl.innerText) || 0;
|
| 1141 |
+
animateValue(procPctEl, currPct, pct + '%', 300);
|
| 1142 |
|
| 1143 |
const totalIn = sumValues(d.class_in);
|
| 1144 |
const totalOut = sumValues(d.class_out);
|
|
|
|
| 1168 |
}
|
| 1169 |
}
|
| 1170 |
|
| 1171 |
+
const now = performance.now();
|
| 1172 |
+
if (now - lastUIUpdate < 300) return;
|
| 1173 |
lastUIUpdate = now;
|
| 1174 |
|
| 1175 |
updateCongestion(liveCongestion, stride);
|
frontend/vehicles.html
CHANGED
|
@@ -149,7 +149,7 @@
|
|
| 149 |
<span class="text-[11px] font-black text-white uppercase tracking-wider whitespace-nowrap"
|
| 150 |
id="proc-label">Waiting</span>
|
| 151 |
<div class="flex-1 h-2 bg-[#111111] rounded-full overflow-hidden relative border border-[#1a1a1a]">
|
| 152 |
-
<div id="proc-bar" class="h-full bg-[#444444] rounded-full"
|
| 153 |
style="width: 0%"></div>
|
| 154 |
</div>
|
| 155 |
</div>
|
|
|
|
| 149 |
<span class="text-[11px] font-black text-white uppercase tracking-wider whitespace-nowrap"
|
| 150 |
id="proc-label">Waiting</span>
|
| 151 |
<div class="flex-1 h-2 bg-[#111111] rounded-full overflow-hidden relative border border-[#1a1a1a]">
|
| 152 |
+
<div id="proc-bar" class="h-full bg-[#444444] rounded-full transition-all duration-500 ease-out"
|
| 153 |
style="width: 0%"></div>
|
| 154 |
</div>
|
| 155 |
</div>
|