Spaces:
Sleeping
Sleeping
cyberai-1 commited on
Commit Β·
524ee4b
1
Parent(s): 6506943
update data video box
Browse files- index.html +86 -18
index.html
CHANGED
|
@@ -292,16 +292,48 @@ header {
|
|
| 292 |
/* ββ VIDEO PANEL ββ */
|
| 293 |
#videoPanel { display:none;padding:32px; }
|
| 294 |
|
| 295 |
-
.video-layout {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
-
.video-wrap { position:relative;background:#000;border-radius:6px;overflow:hidden;border:1px solid var(--border);width:100%;height:100%; }
|
| 298 |
#videoEl { display:block;width:100%;height:100%;object-fit:contain; }
|
| 299 |
#overlayCanvas {
|
| 300 |
position:absolute;top:0;left:0;width:100%;height:100%;
|
| 301 |
pointer-events:none;
|
| 302 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 303 |
|
| 304 |
-
.video-sidebar { display:flex;flex-direction:column;gap:14px; }
|
| 305 |
|
| 306 |
.vid-ctrl {
|
| 307 |
background:var(--s1);border:1px solid var(--border);border-radius:6px;padding:18px;
|
|
@@ -355,11 +387,26 @@ header {
|
|
| 355 |
|
| 356 |
.no-video {
|
| 357 |
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
| 358 |
-
min-height:360px;gap:12px;opacity:.35;
|
| 359 |
}
|
| 360 |
.no-video-icon { font-size:48px; }
|
| 361 |
.no-video-txt { font-family:var(--mono);font-size:12px;letter-spacing:2px; }
|
| 362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
/* ββ CANVAS CHARTS (timeline) ββ */
|
| 364 |
#timelineCanvas { width:100%;height:130px;display:block; }
|
| 365 |
|
|
@@ -535,8 +582,8 @@ header {
|
|
| 535 |
<div class="video-layout">
|
| 536 |
|
| 537 |
<div>
|
| 538 |
-
<div class="video-wrap" id="
|
| 539 |
-
<div class="no-video" id="
|
| 540 |
<div class="no-video-icon">π¬</div>
|
| 541 |
<div class="no-video-txt">NO VIDEO LOADED</div>
|
| 542 |
<div style="font-family:var(--mono);font-size:10px;color:var(--dim);margin-top:6px">
|
|
@@ -545,6 +592,7 @@ header {
|
|
| 545 |
</div>
|
| 546 |
<video id="videoEl" style="display:none" controls></video>
|
| 547 |
<canvas id="overlayCanvas"></canvas>
|
|
|
|
| 548 |
</div>
|
| 549 |
</div>
|
| 550 |
|
|
@@ -784,6 +832,11 @@ function classUniqueCounts(rows) {
|
|
| 784 |
return Object.fromEntries(Object.entries(byClass).map(([cls, ids]) => [cls, ids.size]));
|
| 785 |
}
|
| 786 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
function annotateRows(rows, meta={}) {
|
| 788 |
const key = sourceKeyFromMeta(meta);
|
| 789 |
return rows.map(row => ({
|
|
@@ -1074,9 +1127,9 @@ function renderSceneTable() {
|
|
| 1074 |
const frames = new Set(rows.map(r => r.frame)).size;
|
| 1075 |
const uniqueObjs = data.trackIds.size;
|
| 1076 |
const duration = Math.max(...rows.map(r => parseFloat(r.timestamp_sec||0))).toFixed(1);
|
| 1077 |
-
const cars =
|
| 1078 |
-
const persons =
|
| 1079 |
-
const heavy =
|
| 1080 |
const avgConf = (rows.reduce((s,r)=>s+parseFloat(r.confidence||0),0)/rows.length*100).toFixed(1);
|
| 1081 |
const down = rows.filter(r => r.direction==='down').length;
|
| 1082 |
const up = rows.filter(r => r.direction==='up').length;
|
|
@@ -1134,7 +1187,7 @@ function selectScene(sc, el) {
|
|
| 1134 |
const vid = document.getElementById('videoEl');
|
| 1135 |
vid.pause();
|
| 1136 |
vid.style.display = 'none';
|
| 1137 |
-
document.getElementById('
|
| 1138 |
document.getElementById('vidName').textContent = '';
|
| 1139 |
document.getElementById('liveCounts').innerHTML =
|
| 1140 |
'<div style="font-family:var(--mono);font-size:10px;color:var(--dim);text-align:center;padding:8px">Choose a video for this scene</div>';
|
|
@@ -1205,18 +1258,19 @@ function loadRemoteVideo(file) {
|
|
| 1205 |
vid.pause();
|
| 1206 |
vid.src = file.url;
|
| 1207 |
vid.style.display = 'block';
|
| 1208 |
-
document.getElementById('
|
| 1209 |
document.getElementById('vidName').textContent = `${file.name} (${file.repo}${file.folder ? ' / ' + file.folder : ''})`;
|
| 1210 |
document.getElementById('liveCounts').innerHTML =
|
| 1211 |
'<div style="font-family:var(--mono);font-size:10px;color:var(--dim);text-align:center;padding:8px">Play the selected video to see live counts</div>';
|
| 1212 |
|
| 1213 |
vid.onloadedmetadata = () => {
|
| 1214 |
-
const wrap = document.getElementById('
|
| 1215 |
const canvas = document.getElementById('overlayCanvas');
|
| 1216 |
canvas.style.width = wrap.offsetWidth + 'px';
|
| 1217 |
canvas.style.height = wrap.offsetHeight + 'px';
|
| 1218 |
canvas.width = wrap.offsetWidth * window.devicePixelRatio;
|
| 1219 |
canvas.height = wrap.offsetHeight * window.devicePixelRatio;
|
|
|
|
| 1220 |
};
|
| 1221 |
vid.ontimeupdate = onVideoTimeUpdate;
|
| 1222 |
}
|
|
@@ -1234,11 +1288,11 @@ function loadVideo(input) {
|
|
| 1234 |
const vid = document.getElementById('videoEl');
|
| 1235 |
vid.src = url;
|
| 1236 |
vid.style.display = 'block';
|
| 1237 |
-
document.getElementById('
|
| 1238 |
document.getElementById('vidName').textContent = file.name;
|
| 1239 |
|
| 1240 |
vid.onloadedmetadata = () => {
|
| 1241 |
-
const wrap = document.getElementById('
|
| 1242 |
const canvas = document.getElementById('overlayCanvas');
|
| 1243 |
|
| 1244 |
// Match canvas display size to wrapper
|
|
@@ -1249,6 +1303,7 @@ function loadVideo(input) {
|
|
| 1249 |
canvas.width = wrap.offsetWidth * window.devicePixelRatio;
|
| 1250 |
canvas.height = wrap.offsetHeight * window.devicePixelRatio;
|
| 1251 |
|
|
|
|
| 1252 |
toast(`Video loaded: ${vid.videoWidth}Γ${vid.videoHeight}`, 'ok');
|
| 1253 |
};
|
| 1254 |
|
|
@@ -1257,9 +1312,22 @@ function loadVideo(input) {
|
|
| 1257 |
|
| 1258 |
let lastDrawnFrame = -1;
|
| 1259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
function onVideoTimeUpdate() {
|
| 1261 |
if (!activeScene) return;
|
| 1262 |
const vid = document.getElementById('videoEl');
|
|
|
|
| 1263 |
const fps = detectFPS(activeScene);
|
| 1264 |
const frameNum = Math.round(vid.currentTime * fps);
|
| 1265 |
if (frameNum === lastDrawnFrame) return;
|
|
@@ -1281,7 +1349,7 @@ function drawOverlay(frameNum) {
|
|
| 1281 |
const canvas = document.getElementById('overlayCanvas');
|
| 1282 |
const ctx = canvas.getContext('2d');
|
| 1283 |
const vid = document.getElementById('videoEl');
|
| 1284 |
-
const wrap = document.getElementById('
|
| 1285 |
|
| 1286 |
// Get actual display dimensions
|
| 1287 |
const displayWidth = wrap.offsetWidth;
|
|
@@ -1719,9 +1787,9 @@ function renderFilteredSceneTable() {
|
|
| 1719 |
const frames = new Set(sceneRows.map(r => r.frame)).size;
|
| 1720 |
const uniqueObjs = new Set(sceneRows.map(objectKey)).size;
|
| 1721 |
const duration = Math.max(...sceneRows.map(r => parseFloat(r.timestamp_sec||0))).toFixed(1);
|
| 1722 |
-
const cars =
|
| 1723 |
-
const persons =
|
| 1724 |
-
const heavy =
|
| 1725 |
const avgConf = (sceneRows.reduce((s,r)=>s+parseFloat(r.confidence||0),0)/sceneRows.length*100).toFixed(1);
|
| 1726 |
const down = sceneRows.filter(r => r.direction==='down').length;
|
| 1727 |
const up = sceneRows.filter(r => r.direction==='up').length;
|
|
|
|
| 292 |
/* ββ VIDEO PANEL ββ */
|
| 293 |
#videoPanel { display:none;padding:32px; }
|
| 294 |
|
| 295 |
+
.video-layout {
|
| 296 |
+
display:flex;
|
| 297 |
+
align-items:stretch;
|
| 298 |
+
gap:20px;
|
| 299 |
+
min-height:calc(100vh - 125px);
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
#view-video {
|
| 303 |
+
min-width:0;
|
| 304 |
+
flex:1;
|
| 305 |
+
display:flex;
|
| 306 |
+
align-items:center;
|
| 307 |
+
justify-content:center;
|
| 308 |
+
background:#030608;
|
| 309 |
+
overflow:hidden;
|
| 310 |
+
position:relative;
|
| 311 |
+
border:1px solid var(--border);
|
| 312 |
+
border-radius:6px;
|
| 313 |
+
min-height:420px;
|
| 314 |
+
}
|
| 315 |
|
|
|
|
| 316 |
#videoEl { display:block;width:100%;height:100%;object-fit:contain; }
|
| 317 |
#overlayCanvas {
|
| 318 |
position:absolute;top:0;left:0;width:100%;height:100%;
|
| 319 |
pointer-events:none;
|
| 320 |
}
|
| 321 |
+
#bufferInfo {
|
| 322 |
+
position:absolute;
|
| 323 |
+
right:12px;
|
| 324 |
+
bottom:12px;
|
| 325 |
+
z-index:2;
|
| 326 |
+
padding:4px 8px;
|
| 327 |
+
background:rgba(3,6,8,.72);
|
| 328 |
+
border:1px solid rgba(24,32,48,.9);
|
| 329 |
+
border-radius:3px;
|
| 330 |
+
color:var(--dim);
|
| 331 |
+
font-family:var(--mono);
|
| 332 |
+
font-size:10px;
|
| 333 |
+
letter-spacing:1px;
|
| 334 |
+
}
|
| 335 |
|
| 336 |
+
.video-sidebar { display:flex;flex:0 0 320px;flex-direction:column;gap:14px; }
|
| 337 |
|
| 338 |
.vid-ctrl {
|
| 339 |
background:var(--s1);border:1px solid var(--border);border-radius:6px;padding:18px;
|
|
|
|
| 387 |
|
| 388 |
.no-video {
|
| 389 |
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
| 390 |
+
width:100%;min-height:360px;gap:12px;opacity:.35;
|
| 391 |
}
|
| 392 |
.no-video-icon { font-size:48px; }
|
| 393 |
.no-video-txt { font-family:var(--mono);font-size:12px;letter-spacing:2px; }
|
| 394 |
|
| 395 |
+
@media (max-width: 900px) {
|
| 396 |
+
.video-layout {
|
| 397 |
+
flex-direction:column;
|
| 398 |
+
min-height:0;
|
| 399 |
+
}
|
| 400 |
+
#view-video {
|
| 401 |
+
flex:none;
|
| 402 |
+
min-height:320px;
|
| 403 |
+
aspect-ratio:16 / 9;
|
| 404 |
+
}
|
| 405 |
+
.video-sidebar {
|
| 406 |
+
flex:0 0 auto;
|
| 407 |
+
}
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
/* ββ CANVAS CHARTS (timeline) ββ */
|
| 411 |
#timelineCanvas { width:100%;height:130px;display:block; }
|
| 412 |
|
|
|
|
| 582 |
<div class="video-layout">
|
| 583 |
|
| 584 |
<div>
|
| 585 |
+
<div class="video-wrap" id="view-video">
|
| 586 |
+
<div class="no-video no-signal" id="noSignal">
|
| 587 |
<div class="no-video-icon">π¬</div>
|
| 588 |
<div class="no-video-txt">NO VIDEO LOADED</div>
|
| 589 |
<div style="font-family:var(--mono);font-size:10px;color:var(--dim);margin-top:6px">
|
|
|
|
| 592 |
</div>
|
| 593 |
<video id="videoEl" style="display:none" controls></video>
|
| 594 |
<canvas id="overlayCanvas"></canvas>
|
| 595 |
+
<div id="bufferInfo">BUF: 0</div>
|
| 596 |
</div>
|
| 597 |
</div>
|
| 598 |
|
|
|
|
| 832 |
return Object.fromEntries(Object.entries(byClass).map(([cls, ids]) => [cls, ids.size]));
|
| 833 |
}
|
| 834 |
|
| 835 |
+
function uniqueClassCount(rows, keys) {
|
| 836 |
+
const wanted = new Set(keys);
|
| 837 |
+
return new Set(rows.filter(r => wanted.has(r.class_name)).map(objectKey)).size;
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
function annotateRows(rows, meta={}) {
|
| 841 |
const key = sourceKeyFromMeta(meta);
|
| 842 |
return rows.map(row => ({
|
|
|
|
| 1127 |
const frames = new Set(rows.map(r => r.frame)).size;
|
| 1128 |
const uniqueObjs = data.trackIds.size;
|
| 1129 |
const duration = Math.max(...rows.map(r => parseFloat(r.timestamp_sec||0))).toFixed(1);
|
| 1130 |
+
const cars = uniqueClassCount(rows, ['car']);
|
| 1131 |
+
const persons = uniqueClassCount(rows, ['person']);
|
| 1132 |
+
const heavy = uniqueClassCount(rows, ['bus', 'truck']);
|
| 1133 |
const avgConf = (rows.reduce((s,r)=>s+parseFloat(r.confidence||0),0)/rows.length*100).toFixed(1);
|
| 1134 |
const down = rows.filter(r => r.direction==='down').length;
|
| 1135 |
const up = rows.filter(r => r.direction==='up').length;
|
|
|
|
| 1187 |
const vid = document.getElementById('videoEl');
|
| 1188 |
vid.pause();
|
| 1189 |
vid.style.display = 'none';
|
| 1190 |
+
document.getElementById('noSignal').style.display = 'flex';
|
| 1191 |
document.getElementById('vidName').textContent = '';
|
| 1192 |
document.getElementById('liveCounts').innerHTML =
|
| 1193 |
'<div style="font-family:var(--mono);font-size:10px;color:var(--dim);text-align:center;padding:8px">Choose a video for this scene</div>';
|
|
|
|
| 1258 |
vid.pause();
|
| 1259 |
vid.src = file.url;
|
| 1260 |
vid.style.display = 'block';
|
| 1261 |
+
document.getElementById('noSignal').style.display = 'none';
|
| 1262 |
document.getElementById('vidName').textContent = `${file.name} (${file.repo}${file.folder ? ' / ' + file.folder : ''})`;
|
| 1263 |
document.getElementById('liveCounts').innerHTML =
|
| 1264 |
'<div style="font-family:var(--mono);font-size:10px;color:var(--dim);text-align:center;padding:8px">Play the selected video to see live counts</div>';
|
| 1265 |
|
| 1266 |
vid.onloadedmetadata = () => {
|
| 1267 |
+
const wrap = document.getElementById('view-video');
|
| 1268 |
const canvas = document.getElementById('overlayCanvas');
|
| 1269 |
canvas.style.width = wrap.offsetWidth + 'px';
|
| 1270 |
canvas.style.height = wrap.offsetHeight + 'px';
|
| 1271 |
canvas.width = wrap.offsetWidth * window.devicePixelRatio;
|
| 1272 |
canvas.height = wrap.offsetHeight * window.devicePixelRatio;
|
| 1273 |
+
updateBufferInfo();
|
| 1274 |
};
|
| 1275 |
vid.ontimeupdate = onVideoTimeUpdate;
|
| 1276 |
}
|
|
|
|
| 1288 |
const vid = document.getElementById('videoEl');
|
| 1289 |
vid.src = url;
|
| 1290 |
vid.style.display = 'block';
|
| 1291 |
+
document.getElementById('noSignal').style.display = 'none';
|
| 1292 |
document.getElementById('vidName').textContent = file.name;
|
| 1293 |
|
| 1294 |
vid.onloadedmetadata = () => {
|
| 1295 |
+
const wrap = document.getElementById('view-video');
|
| 1296 |
const canvas = document.getElementById('overlayCanvas');
|
| 1297 |
|
| 1298 |
// Match canvas display size to wrapper
|
|
|
|
| 1303 |
canvas.width = wrap.offsetWidth * window.devicePixelRatio;
|
| 1304 |
canvas.height = wrap.offsetHeight * window.devicePixelRatio;
|
| 1305 |
|
| 1306 |
+
updateBufferInfo();
|
| 1307 |
toast(`Video loaded: ${vid.videoWidth}Γ${vid.videoHeight}`, 'ok');
|
| 1308 |
};
|
| 1309 |
|
|
|
|
| 1312 |
|
| 1313 |
let lastDrawnFrame = -1;
|
| 1314 |
|
| 1315 |
+
function updateBufferInfo() {
|
| 1316 |
+
const vid = document.getElementById('videoEl');
|
| 1317 |
+
const el = document.getElementById('bufferInfo');
|
| 1318 |
+
if (!vid || !el || !vid.duration || !vid.buffered.length) {
|
| 1319 |
+
if (el) el.textContent = 'BUF: 0';
|
| 1320 |
+
return;
|
| 1321 |
+
}
|
| 1322 |
+
const end = vid.buffered.end(vid.buffered.length - 1);
|
| 1323 |
+
const pct = Math.min(100, Math.round((end / vid.duration) * 100));
|
| 1324 |
+
el.textContent = `BUF: ${pct}`;
|
| 1325 |
+
}
|
| 1326 |
+
|
| 1327 |
function onVideoTimeUpdate() {
|
| 1328 |
if (!activeScene) return;
|
| 1329 |
const vid = document.getElementById('videoEl');
|
| 1330 |
+
updateBufferInfo();
|
| 1331 |
const fps = detectFPS(activeScene);
|
| 1332 |
const frameNum = Math.round(vid.currentTime * fps);
|
| 1333 |
if (frameNum === lastDrawnFrame) return;
|
|
|
|
| 1349 |
const canvas = document.getElementById('overlayCanvas');
|
| 1350 |
const ctx = canvas.getContext('2d');
|
| 1351 |
const vid = document.getElementById('videoEl');
|
| 1352 |
+
const wrap = document.getElementById('view-video');
|
| 1353 |
|
| 1354 |
// Get actual display dimensions
|
| 1355 |
const displayWidth = wrap.offsetWidth;
|
|
|
|
| 1787 |
const frames = new Set(sceneRows.map(r => r.frame)).size;
|
| 1788 |
const uniqueObjs = new Set(sceneRows.map(objectKey)).size;
|
| 1789 |
const duration = Math.max(...sceneRows.map(r => parseFloat(r.timestamp_sec||0))).toFixed(1);
|
| 1790 |
+
const cars = uniqueClassCount(sceneRows, ['car']);
|
| 1791 |
+
const persons = uniqueClassCount(sceneRows, ['person']);
|
| 1792 |
+
const heavy = uniqueClassCount(sceneRows, ['bus', 'truck']);
|
| 1793 |
const avgConf = (sceneRows.reduce((s,r)=>s+parseFloat(r.confidence||0),0)/sceneRows.length*100).toFixed(1);
|
| 1794 |
const down = sceneRows.filter(r => r.direction==='down').length;
|
| 1795 |
const up = sceneRows.filter(r => r.direction==='up').length;
|