Spaces:
Running
Running
Commit ·
313a596
1
Parent(s): 4b4e4f7
Add highly visible camera flip button and detailed session report
Browse files- templates/recognition.html +48 -3
templates/recognition.html
CHANGED
|
@@ -214,6 +214,9 @@
|
|
| 214 |
</div>
|
| 215 |
</div>
|
| 216 |
|
|
|
|
|
|
|
|
|
|
| 217 |
<div class="report-panel" id="reportPanel">
|
| 218 |
<h3 style="margin-bottom:15px; font-size:1rem;">Session Report</h3>
|
| 219 |
<div id="reportList"></div>
|
|
@@ -227,12 +230,13 @@
|
|
| 227 |
let remainingTime = 60; // 1-minute detection session
|
| 228 |
let mainInterval = null;
|
| 229 |
let timerInterval = null;
|
|
|
|
| 230 |
|
| 231 |
const SESSION_WINDOW = 30000; // 30 seconds
|
| 232 |
const PRESENCE_THRESHOLD = 20000; // 20 seconds
|
| 233 |
const INTERVAL = 500; // ms
|
| 234 |
|
| 235 |
-
async function init() {
|
| 236 |
// 1. Mandatory Password Check at Start
|
| 237 |
const password = prompt("AttendNet Administrator Security Check\nEnter password to unlock AI Recognition:");
|
| 238 |
if (password !== "student1") {
|
|
@@ -261,7 +265,7 @@
|
|
| 261 |
});
|
| 262 |
|
| 263 |
const cameraPromise = navigator.mediaDevices.getUserMedia({
|
| 264 |
-
video: { facingMode:
|
| 265 |
}).catch(err => {
|
| 266 |
throw new Error("Camera Access Denied or in Use by another Tab. Please close other AttendNet tabs.");
|
| 267 |
});
|
|
@@ -283,6 +287,8 @@
|
|
| 283 |
const displaySize = { width: video.videoWidth, height: video.videoHeight };
|
| 284 |
faceapi.matchDimensions(canvas, displaySize);
|
| 285 |
|
|
|
|
|
|
|
| 286 |
mainInterval = setInterval(async () => {
|
| 287 |
if (isProcessing || !sessionActive) return;
|
| 288 |
isProcessing = true;
|
|
@@ -405,6 +411,9 @@
|
|
| 405 |
<div style="background:rgba(255,255,255,0.03); border-radius:20px; padding:20px; max-height:200px; overflow-y:auto; text-align:left; margin-bottom:30px;">
|
| 406 |
<h4 style="font-size:0.8rem; opacity:0.5; margin-bottom:10px;">Newly Marked USNs:</h4>
|
| 407 |
<p style="font-size:0.9rem; line-height:1.5;">${markedInSession.length > 0 ? markedInSession.join(', ') : 'No new marks.'}</p>
|
|
|
|
|
|
|
|
|
|
| 408 |
</div>
|
| 409 |
|
| 410 |
<div style="display:flex; gap:12px;">
|
|
@@ -501,7 +510,43 @@
|
|
| 501 |
}
|
| 502 |
}
|
| 503 |
|
| 504 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
</script>
|
| 506 |
</body>
|
| 507 |
</html>
|
|
|
|
| 214 |
</div>
|
| 215 |
</div>
|
| 216 |
|
| 217 |
+
<button id="flipBtn" onclick="flipCamera()" style="position:fixed; bottom:40px; right:40px; width:72px; height:72px; border-radius:50%; border:3px solid white; background:#6366f1; color:white; font-size:36px; cursor:pointer; z-index:2147483647; box-shadow:0 15px 35px rgba(0,0,0,0.8); display:flex; align-items:center; justify-content:center; transition:transform 0.2s;">↻</button>
|
| 218 |
+
|
| 219 |
+
|
| 220 |
<div class="report-panel" id="reportPanel">
|
| 221 |
<h3 style="margin-bottom:15px; font-size:1rem;">Session Report</h3>
|
| 222 |
<div id="reportList"></div>
|
|
|
|
| 230 |
let remainingTime = 60; // 1-minute detection session
|
| 231 |
let mainInterval = null;
|
| 232 |
let timerInterval = null;
|
| 233 |
+
let currentFacingMode = 'user';
|
| 234 |
|
| 235 |
const SESSION_WINDOW = 30000; // 30 seconds
|
| 236 |
const PRESENCE_THRESHOLD = 20000; // 20 seconds
|
| 237 |
const INTERVAL = 500; // ms
|
| 238 |
|
| 239 |
+
async function init(mode = 'user') {
|
| 240 |
// 1. Mandatory Password Check at Start
|
| 241 |
const password = prompt("AttendNet Administrator Security Check\nEnter password to unlock AI Recognition:");
|
| 242 |
if (password !== "student1") {
|
|
|
|
| 265 |
});
|
| 266 |
|
| 267 |
const cameraPromise = navigator.mediaDevices.getUserMedia({
|
| 268 |
+
video: { facingMode: mode, width: { ideal: 1280 }, height: { ideal: 720 } }
|
| 269 |
}).catch(err => {
|
| 270 |
throw new Error("Camera Access Denied or in Use by another Tab. Please close other AttendNet tabs.");
|
| 271 |
});
|
|
|
|
| 287 |
const displaySize = { width: video.videoWidth, height: video.videoHeight };
|
| 288 |
faceapi.matchDimensions(canvas, displaySize);
|
| 289 |
|
| 290 |
+
if (mainInterval) clearInterval(mainInterval);
|
| 291 |
+
|
| 292 |
mainInterval = setInterval(async () => {
|
| 293 |
if (isProcessing || !sessionActive) return;
|
| 294 |
isProcessing = true;
|
|
|
|
| 411 |
<div style="background:rgba(255,255,255,0.03); border-radius:20px; padding:20px; max-height:200px; overflow-y:auto; text-align:left; margin-bottom:30px;">
|
| 412 |
<h4 style="font-size:0.8rem; opacity:0.5; margin-bottom:10px;">Newly Marked USNs:</h4>
|
| 413 |
<p style="font-size:0.9rem; line-height:1.5;">${markedInSession.length > 0 ? markedInSession.join(', ') : 'No new marks.'}</p>
|
| 414 |
+
|
| 415 |
+
<h4 style="font-size:0.8rem; opacity:0.5; margin-top:20px; margin-bottom:10px;">Already Logged USNs:</h4>
|
| 416 |
+
<p style="font-size:0.9rem; line-height:1.5; color:#f59e0b;">${alreadyMarked.length > 0 ? alreadyMarked.join(', ') : 'None.'}</p>
|
| 417 |
</div>
|
| 418 |
|
| 419 |
<div style="display:flex; gap:12px;">
|
|
|
|
| 510 |
}
|
| 511 |
}
|
| 512 |
|
| 513 |
+
async function flipCamera() {
|
| 514 |
+
if (!sessionActive) return;
|
| 515 |
+
currentFacingMode = currentFacingMode === 'user' ? 'environment' : 'user';
|
| 516 |
+
|
| 517 |
+
const flipBtn = document.getElementById('flipBtn');
|
| 518 |
+
flipBtn.style.opacity = '0.5';
|
| 519 |
+
flipBtn.style.pointerEvents = 'none';
|
| 520 |
+
|
| 521 |
+
// stop current stream to release hardware
|
| 522 |
+
if (video.srcObject) {
|
| 523 |
+
video.srcObject.getTracks().forEach(track => track.stop());
|
| 524 |
+
if (mainInterval) clearInterval(mainInterval);
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
try {
|
| 528 |
+
const newStream = await navigator.mediaDevices.getUserMedia({
|
| 529 |
+
video: { facingMode: currentFacingMode, width: { ideal: 1280 }, height: { ideal: 720 } }
|
| 530 |
+
});
|
| 531 |
+
video.srcObject = newStream;
|
| 532 |
+
if (currentFacingMode === 'user') {
|
| 533 |
+
video.style.transform = 'scaleX(-1)';
|
| 534 |
+
canvas.style.transform = 'scaleX(-1)';
|
| 535 |
+
} else {
|
| 536 |
+
video.style.transform = 'scaleX(1)';
|
| 537 |
+
canvas.style.transform = 'scaleX(1)';
|
| 538 |
+
}
|
| 539 |
+
} catch (err) {
|
| 540 |
+
alert("Camera flip failed: " + err.message);
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
setTimeout(() => {
|
| 544 |
+
flipBtn.style.opacity = '1';
|
| 545 |
+
flipBtn.style.pointerEvents = 'auto';
|
| 546 |
+
}, 500);
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
window.onload = () => init();
|
| 550 |
</script>
|
| 551 |
</body>
|
| 552 |
</html>
|