Spaces:
Sleeping
Sleeping
cyberai-1 commited on
Commit ·
a8d671e
1
Parent(s): 0551166
update filter error
Browse files- index.html +74 -24
index.html
CHANGED
|
@@ -548,6 +548,17 @@ let filteredRows = []; // current filtered rows based on group/scene select
|
|
| 548 |
// CSV drag-drop
|
| 549 |
setupDrop('csvZone', 'csvInput', handleCSVFiles);
|
| 550 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
function setupDrop(zoneId, inputId, handler) {
|
| 552 |
const zone = document.getElementById(zoneId);
|
| 553 |
const input = document.getElementById(inputId);
|
|
@@ -646,6 +657,11 @@ function buildDashboard() {
|
|
| 646 |
renderSceneTable();
|
| 647 |
renderSceneSelector();
|
| 648 |
populateFilterDropdowns();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
|
| 650 |
showPage('dash');
|
| 651 |
toast(`Loaded ${allRows.length.toLocaleString()} detections across ${Object.keys(scenes).length} scene(s).`, 'ok');
|
|
@@ -957,18 +973,19 @@ function drawOverlay(frameNum) {
|
|
| 957 |
const vid = document.getElementById('videoEl');
|
| 958 |
const wrap = document.getElementById('videoWrap');
|
| 959 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 960 |
// Set canvas to actual display size (not internal resolution)
|
| 961 |
-
canvas.style.width =
|
| 962 |
-
canvas.style.height =
|
| 963 |
|
| 964 |
// Internal resolution for drawing (higher precision)
|
| 965 |
-
canvas.width =
|
| 966 |
-
canvas.height =
|
| 967 |
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
| 968 |
|
| 969 |
-
const displayWidth = wrap.offsetWidth;
|
| 970 |
-
const displayHeight = wrap.offsetHeight;
|
| 971 |
-
|
| 972 |
ctx.clearRect(0, 0, displayWidth, displayHeight);
|
| 973 |
|
| 974 |
if (!activeScene) return;
|
|
@@ -990,12 +1007,38 @@ function drawOverlay(frameNum) {
|
|
| 990 |
}
|
| 991 |
|
| 992 |
// Get original frame dimensions from CSV
|
| 993 |
-
const frameWidth = parseInt(rows[0]?.frame_width)
|
| 994 |
-
const frameHeight = parseInt(rows[0]?.frame_height)
|
| 995 |
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 999 |
|
| 1000 |
const liveCounts = {};
|
| 1001 |
|
|
@@ -1005,11 +1048,11 @@ function drawOverlay(frameNum) {
|
|
| 1005 |
const x2 = parseInt(r.bbox_x2);
|
| 1006 |
const y2 = parseInt(r.bbox_y2);
|
| 1007 |
|
| 1008 |
-
// Scale to display coordinates
|
| 1009 |
-
const dispX1 = x1 * scaleX;
|
| 1010 |
-
const dispY1 = y1 * scaleY;
|
| 1011 |
-
const dispX2 = x2 * scaleX;
|
| 1012 |
-
const dispY2 = y2 * scaleY;
|
| 1013 |
|
| 1014 |
const w = dispX2 - dispX1;
|
| 1015 |
const h = dispY2 - dispY1;
|
|
@@ -1036,8 +1079,8 @@ function drawOverlay(frameNum) {
|
|
| 1036 |
ctx.fillText(lbl, dispX1 + 4, dispY1 - 4);
|
| 1037 |
|
| 1038 |
// Center dot (scaled)
|
| 1039 |
-
const cx = parseInt(r.cx) * scaleX;
|
| 1040 |
-
const cy = parseInt(r.cy) * scaleY;
|
| 1041 |
ctx.beginPath();
|
| 1042 |
ctx.arc(cx, cy, 4, 0, Math.PI*2);
|
| 1043 |
ctx.fillStyle = col;
|
|
@@ -1046,8 +1089,8 @@ function drawOverlay(frameNum) {
|
|
| 1046 |
liveCounts[r.class_name] = (liveCounts[r.class_name]||0) + 1;
|
| 1047 |
});
|
| 1048 |
|
| 1049 |
-
// Counting line (55% height)
|
| 1050 |
-
const lineY = displayHeight * 0.55;
|
| 1051 |
ctx.setLineDash([8, 6]);
|
| 1052 |
ctx.strokeStyle = '#e8ff47';
|
| 1053 |
ctx.lineWidth = 1.5;
|
|
@@ -1122,8 +1165,13 @@ function populateFilterDropdowns() {
|
|
| 1122 |
groups.add(r.group_id || '?');
|
| 1123 |
});
|
| 1124 |
|
| 1125 |
-
// Populate group filter
|
| 1126 |
const groupSelect = document.getElementById('groupFilter');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1127 |
const groupOptions = Array.from(groups).sort();
|
| 1128 |
groupOptions.forEach(group => {
|
| 1129 |
const option = document.createElement('option');
|
|
@@ -1140,8 +1188,10 @@ function updateSceneFilter() {
|
|
| 1140 |
const selectedGroup = document.getElementById('groupFilter').value;
|
| 1141 |
const sceneSelect = document.getElementById('sceneFilter');
|
| 1142 |
|
| 1143 |
-
// Clear existing options except "ALL SCENES"
|
| 1144 |
-
sceneSelect.
|
|
|
|
|
|
|
| 1145 |
|
| 1146 |
// Get scenes for selected group (or all scenes if no group selected)
|
| 1147 |
const sceneNames = new Set();
|
|
|
|
| 548 |
// CSV drag-drop
|
| 549 |
setupDrop('csvZone', 'csvInput', handleCSVFiles);
|
| 550 |
|
| 551 |
+
// Handle window resize for video overlay
|
| 552 |
+
window.addEventListener('resize', () => {
|
| 553 |
+
if (activeScene && document.getElementById('videoEl').style.display !== 'none') {
|
| 554 |
+
const frameNum = lastDrawnFrame;
|
| 555 |
+
if (frameNum >= 0) {
|
| 556 |
+
// Redraw with new dimensions
|
| 557 |
+
drawOverlay(frameNum);
|
| 558 |
+
}
|
| 559 |
+
}
|
| 560 |
+
});
|
| 561 |
+
|
| 562 |
function setupDrop(zoneId, inputId, handler) {
|
| 563 |
const zone = document.getElementById(zoneId);
|
| 564 |
const input = document.getElementById(inputId);
|
|
|
|
| 657 |
renderSceneTable();
|
| 658 |
renderSceneSelector();
|
| 659 |
populateFilterDropdowns();
|
| 660 |
+
|
| 661 |
+
// Reset filter selections when loading new data
|
| 662 |
+
document.getElementById('groupFilter').value = '';
|
| 663 |
+
document.getElementById('sceneFilter').value = '';
|
| 664 |
+
filteredRows = [];
|
| 665 |
|
| 666 |
showPage('dash');
|
| 667 |
toast(`Loaded ${allRows.length.toLocaleString()} detections across ${Object.keys(scenes).length} scene(s).`, 'ok');
|
|
|
|
| 973 |
const vid = document.getElementById('videoEl');
|
| 974 |
const wrap = document.getElementById('videoWrap');
|
| 975 |
|
| 976 |
+
// Get actual display dimensions
|
| 977 |
+
const displayWidth = wrap.offsetWidth;
|
| 978 |
+
const displayHeight = wrap.offsetHeight;
|
| 979 |
+
|
| 980 |
// Set canvas to actual display size (not internal resolution)
|
| 981 |
+
canvas.style.width = displayWidth + 'px';
|
| 982 |
+
canvas.style.height = displayHeight + 'px';
|
| 983 |
|
| 984 |
// Internal resolution for drawing (higher precision)
|
| 985 |
+
canvas.width = displayWidth * window.devicePixelRatio;
|
| 986 |
+
canvas.height = displayHeight * window.devicePixelRatio;
|
| 987 |
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
| 988 |
|
|
|
|
|
|
|
|
|
|
| 989 |
ctx.clearRect(0, 0, displayWidth, displayHeight);
|
| 990 |
|
| 991 |
if (!activeScene) return;
|
|
|
|
| 1007 |
}
|
| 1008 |
|
| 1009 |
// Get original frame dimensions from CSV
|
| 1010 |
+
const frameWidth = parseInt(rows[0]?.frame_width);
|
| 1011 |
+
const frameHeight = parseInt(rows[0]?.frame_height);
|
| 1012 |
|
| 1013 |
+
if (!frameWidth || !frameHeight) {
|
| 1014 |
+
ctx.font = '12px DM Mono';
|
| 1015 |
+
ctx.fillStyle = 'rgba(255,255,255,0.3)';
|
| 1016 |
+
ctx.fillText(`FRAME ${frameNum} - Invalid frame dimensions`, 10, displayHeight - 10);
|
| 1017 |
+
return;
|
| 1018 |
+
}
|
| 1019 |
+
|
| 1020 |
+
// Calculate scale factors accounting for video aspect ratio and display size
|
| 1021 |
+
// Video maintains aspect ratio with object-fit:contain, so we need to calculate the actual video display area
|
| 1022 |
+
const videoAspect = frameWidth / frameHeight;
|
| 1023 |
+
const displayAspect = displayWidth / displayHeight;
|
| 1024 |
+
|
| 1025 |
+
let scaleX, scaleY, offsetX = 0, offsetY = 0;
|
| 1026 |
+
|
| 1027 |
+
if (displayAspect > videoAspect) {
|
| 1028 |
+
// Display is wider than video - letterbox left/right
|
| 1029 |
+
const scaledHeight = displayHeight;
|
| 1030 |
+
const scaledWidth = scaledHeight * videoAspect;
|
| 1031 |
+
offsetX = (displayWidth - scaledWidth) / 2;
|
| 1032 |
+
scaleX = scaledWidth / frameWidth;
|
| 1033 |
+
scaleY = scaledHeight / frameHeight;
|
| 1034 |
+
} else {
|
| 1035 |
+
// Display is taller than video - letterbox top/bottom
|
| 1036 |
+
const scaledWidth = displayWidth;
|
| 1037 |
+
const scaledHeight = scaledWidth / videoAspect;
|
| 1038 |
+
offsetY = (displayHeight - scaledHeight) / 2;
|
| 1039 |
+
scaleX = scaledWidth / frameWidth;
|
| 1040 |
+
scaleY = scaledHeight / frameHeight;
|
| 1041 |
+
}
|
| 1042 |
|
| 1043 |
const liveCounts = {};
|
| 1044 |
|
|
|
|
| 1048 |
const x2 = parseInt(r.bbox_x2);
|
| 1049 |
const y2 = parseInt(r.bbox_y2);
|
| 1050 |
|
| 1051 |
+
// Scale to display coordinates accounting for aspect ratio and centering
|
| 1052 |
+
const dispX1 = offsetX + x1 * scaleX;
|
| 1053 |
+
const dispY1 = offsetY + y1 * scaleY;
|
| 1054 |
+
const dispX2 = offsetX + x2 * scaleX;
|
| 1055 |
+
const dispY2 = offsetY + y2 * scaleY;
|
| 1056 |
|
| 1057 |
const w = dispX2 - dispX1;
|
| 1058 |
const h = dispY2 - dispY1;
|
|
|
|
| 1079 |
ctx.fillText(lbl, dispX1 + 4, dispY1 - 4);
|
| 1080 |
|
| 1081 |
// Center dot (scaled)
|
| 1082 |
+
const cx = parseInt(r.cx) * scaleX + offsetX;
|
| 1083 |
+
const cy = parseInt(r.cy) * scaleY + offsetY;
|
| 1084 |
ctx.beginPath();
|
| 1085 |
ctx.arc(cx, cy, 4, 0, Math.PI*2);
|
| 1086 |
ctx.fillStyle = col;
|
|
|
|
| 1089 |
liveCounts[r.class_name] = (liveCounts[r.class_name]||0) + 1;
|
| 1090 |
});
|
| 1091 |
|
| 1092 |
+
// Counting line (55% height from video area top)
|
| 1093 |
+
const lineY = offsetY + (displayHeight - offsetY * 2) * 0.55;
|
| 1094 |
ctx.setLineDash([8, 6]);
|
| 1095 |
ctx.strokeStyle = '#e8ff47';
|
| 1096 |
ctx.lineWidth = 1.5;
|
|
|
|
| 1165 |
groups.add(r.group_id || '?');
|
| 1166 |
});
|
| 1167 |
|
| 1168 |
+
// Populate group filter - clear first to avoid duplicates
|
| 1169 |
const groupSelect = document.getElementById('groupFilter');
|
| 1170 |
+
// Remove all options except the first "ALL GROUPS"
|
| 1171 |
+
while (groupSelect.options.length > 1) {
|
| 1172 |
+
groupSelect.remove(1);
|
| 1173 |
+
}
|
| 1174 |
+
|
| 1175 |
const groupOptions = Array.from(groups).sort();
|
| 1176 |
groupOptions.forEach(group => {
|
| 1177 |
const option = document.createElement('option');
|
|
|
|
| 1188 |
const selectedGroup = document.getElementById('groupFilter').value;
|
| 1189 |
const sceneSelect = document.getElementById('sceneFilter');
|
| 1190 |
|
| 1191 |
+
// Clear existing options except "ALL SCENES" - remove all except first
|
| 1192 |
+
while (sceneSelect.options.length > 1) {
|
| 1193 |
+
sceneSelect.remove(1);
|
| 1194 |
+
}
|
| 1195 |
|
| 1196 |
// Get scenes for selected group (or all scenes if no group selected)
|
| 1197 |
const sceneNames = new Set();
|