arshtech commited on
Commit
93fbf04
·
verified ·
1 Parent(s): 00dc7c2

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +506 -0
templates/index.html ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Face Detection App</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ font-family: Arial, sans-serif;
13
+ }
14
+
15
+ body {
16
+ background: linear-gradient(135deg, #667eea, #764ba2);
17
+ min-height: 100vh;
18
+ padding: 20px;
19
+ }
20
+
21
+ .container {
22
+ max-width: 1000px;
23
+ margin: 0 auto;
24
+ background: white;
25
+ border-radius: 15px;
26
+ padding: 30px;
27
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
28
+ }
29
+
30
+ header {
31
+ text-align: center;
32
+ margin-bottom: 30px;
33
+ }
34
+
35
+ h1 {
36
+ color: #333;
37
+ margin-bottom: 10px;
38
+ }
39
+
40
+ .subtitle {
41
+ color: #666;
42
+ font-size: 1.1rem;
43
+ }
44
+
45
+ .main-content {
46
+ display: grid;
47
+ grid-template-columns: 1fr 1fr;
48
+ gap: 30px;
49
+ margin-bottom: 30px;
50
+ }
51
+
52
+ @media (max-width: 768px) {
53
+ .main-content {
54
+ grid-template-columns: 1fr;
55
+ }
56
+ }
57
+
58
+ .input-section, .output-section {
59
+ background: #f8f9fa;
60
+ padding: 25px;
61
+ border-radius: 10px;
62
+ border: 2px dashed #dee2e6;
63
+ }
64
+
65
+ .section-title {
66
+ color: #495057;
67
+ margin-bottom: 20px;
68
+ font-size: 1.3rem;
69
+ }
70
+
71
+ .input-options {
72
+ display: flex;
73
+ gap: 10px;
74
+ margin-bottom: 20px;
75
+ }
76
+
77
+ .btn {
78
+ padding: 12px 20px;
79
+ border: none;
80
+ border-radius: 8px;
81
+ background: #6c757d;
82
+ color: white;
83
+ cursor: pointer;
84
+ flex: 1;
85
+ text-align: center;
86
+ transition: background 0.3s;
87
+ }
88
+
89
+ .btn:hover {
90
+ background: #5a6268;
91
+ }
92
+
93
+ .btn.active {
94
+ background: #007bff;
95
+ }
96
+
97
+ .upload-area {
98
+ border: 2px dashed #adb5bd;
99
+ border-radius: 10px;
100
+ padding: 40px 20px;
101
+ text-align: center;
102
+ margin-bottom: 20px;
103
+ cursor: pointer;
104
+ transition: border-color 0.3s;
105
+ }
106
+
107
+ .upload-area:hover {
108
+ border-color: #007bff;
109
+ }
110
+
111
+ .upload-icon {
112
+ font-size: 3rem;
113
+ margin-bottom: 15px;
114
+ color: #6c757d;
115
+ }
116
+
117
+ .camera-container {
118
+ position: relative;
119
+ width: 100%;
120
+ height: 300px;
121
+ background: #000;
122
+ border-radius: 10px;
123
+ overflow: hidden;
124
+ margin-bottom: 20px;
125
+ display: none;
126
+ }
127
+
128
+ #videoElement {
129
+ width: 100%;
130
+ height: 100%;
131
+ object-fit: cover;
132
+ }
133
+
134
+ .capture-btn {
135
+ position: absolute;
136
+ bottom: 20px;
137
+ left: 50%;
138
+ transform: translateX(-50%);
139
+ background: #dc3545;
140
+ width: 60px;
141
+ height: 60px;
142
+ border-radius: 50%;
143
+ border: 4px solid white;
144
+ cursor: pointer;
145
+ }
146
+
147
+ .slider-container {
148
+ margin: 20px 0;
149
+ }
150
+
151
+ .slider-label {
152
+ display: flex;
153
+ justify-content: space-between;
154
+ margin-bottom: 10px;
155
+ color: #495057;
156
+ }
157
+
158
+ .slider {
159
+ width: 100%;
160
+ height: 8px;
161
+ -webkit-appearance: none;
162
+ background: #dee2e6;
163
+ border-radius: 5px;
164
+ outline: none;
165
+ }
166
+
167
+ .slider::-webkit-slider-thumb {
168
+ -webkit-appearance: none;
169
+ width: 20px;
170
+ height: 20px;
171
+ border-radius: 50%;
172
+ background: #007bff;
173
+ cursor: pointer;
174
+ }
175
+
176
+ .detect-btn {
177
+ width: 100%;
178
+ padding: 15px;
179
+ background: #28a745;
180
+ color: white;
181
+ border: none;
182
+ border-radius: 8px;
183
+ font-size: 1.1rem;
184
+ cursor: pointer;
185
+ transition: background 0.3s;
186
+ }
187
+
188
+ .detect-btn:hover {
189
+ background: #218838;
190
+ }
191
+
192
+ .detect-btn:disabled {
193
+ background: #6c757d;
194
+ cursor: not-allowed;
195
+ }
196
+
197
+ .image-container {
198
+ width: 100%;
199
+ height: 300px;
200
+ background: #e9ecef;
201
+ border-radius: 10px;
202
+ overflow: hidden;
203
+ margin-bottom: 20px;
204
+ display: flex;
205
+ align-items: center;
206
+ justify-content: center;
207
+ }
208
+
209
+ #outputImage {
210
+ max-width: 100%;
211
+ max-height: 100%;
212
+ display: none;
213
+ }
214
+
215
+ .placeholder-text {
216
+ color: #6c757d;
217
+ text-align: center;
218
+ }
219
+
220
+ .results-container {
221
+ background: white;
222
+ border-radius: 10px;
223
+ padding: 20px;
224
+ border: 2px solid #e9ecef;
225
+ }
226
+
227
+ .result-item {
228
+ display: flex;
229
+ justify-content: space-between;
230
+ padding: 12px 0;
231
+ border-bottom: 1px solid #dee2e6;
232
+ }
233
+
234
+ .result-item:last-child {
235
+ border-bottom: none;
236
+ }
237
+
238
+ .result-label {
239
+ font-weight: bold;
240
+ color: #495057;
241
+ }
242
+
243
+ .result-value {
244
+ color: #007bff;
245
+ font-weight: bold;
246
+ }
247
+
248
+ .loading {
249
+ display: none;
250
+ text-align: center;
251
+ margin: 20px 0;
252
+ }
253
+
254
+ .spinner {
255
+ border: 4px solid #f3f3f3;
256
+ border-radius: 50%;
257
+ border-top: 4px solid #007bff;
258
+ width: 40px;
259
+ height: 40px;
260
+ animation: spin 1s linear infinite;
261
+ margin: 0 auto 15px;
262
+ }
263
+
264
+ @keyframes spin {
265
+ 0% { transform: rotate(0deg); }
266
+ 100% { transform: rotate(360deg); }
267
+ }
268
+
269
+ #fileInput {
270
+ display: none;
271
+ }
272
+ </style>
273
+ </head>
274
+ <body>
275
+ <div class="container">
276
+ <header>
277
+ <h1>Face Detection App</h1>
278
+ <p class="subtitle">Upload an image or use camera to detect faces</p>
279
+ </header>
280
+
281
+ <div class="main-content">
282
+ <div class="input-section">
283
+ <h2 class="section-title">Input</h2>
284
+
285
+ <div class="input-options">
286
+ <button id="uploadBtn" class="btn active">Upload Image</button>
287
+ <button id="cameraBtn" class="btn">Use Camera</button>
288
+ </div>
289
+
290
+ <div id="uploadArea" class="upload-area">
291
+ <div class="upload-icon">📁</div>
292
+ <p>Click to upload an image</p>
293
+ <p style="font-size: 0.9rem; color: #6c757d; margin-top: 5px;">Supported: JPG, PNG</p>
294
+ <input type="file" id="fileInput" accept="image/*">
295
+ </div>
296
+
297
+ <div id="cameraContainer" class="camera-container">
298
+ <video id="videoElement" autoplay playsinline></video>
299
+ <button id="captureBtn" class="capture-btn"></button>
300
+ </div>
301
+
302
+ <div class="slider-container">
303
+ <div class="slider-label">
304
+ <span>Detection Scale:</span>
305
+ <span id="scaleValue">1.1</span>
306
+ </div>
307
+ <input type="range" min="1.1" max="2.0" step="0.1" value="1.1" class="slider" id="scaleSlider">
308
+ </div>
309
+
310
+ <button id="detectBtn" class="detect-btn" disabled>Detect Faces</button>
311
+
312
+ <div id="loading" class="loading">
313
+ <div class="spinner"></div>
314
+ <p>Processing image...</p>
315
+ </div>
316
+ </div>
317
+
318
+ <div class="output-section">
319
+ <h2 class="section-title">Output</h2>
320
+
321
+ <div class="image-container">
322
+ <img id="outputImage" alt="Processed Image">
323
+ <p id="placeholderText" class="placeholder-text">Processed image will appear here</p>
324
+ </div>
325
+
326
+ <div id="resultsContainer" class="results-container" style="display: none;">
327
+ <h3 style="margin-bottom: 15px; color: #495057;">Detection Results</h3>
328
+ <div class="result-item">
329
+ <span class="result-label">Faces Detected:</span>
330
+ <span id="facesCount" class="result-value">0</span>
331
+ </div>
332
+ <div id="facesDetails">
333
+ <!-- Face details will be added here -->
334
+ </div>
335
+ </div>
336
+ </div>
337
+ </div>
338
+ </div>
339
+
340
+ <script>
341
+ // DOM Elements
342
+ const uploadBtn = document.getElementById('uploadBtn');
343
+ const cameraBtn = document.getElementById('cameraBtn');
344
+ const uploadArea = document.getElementById('uploadArea');
345
+ const fileInput = document.getElementById('fileInput');
346
+ const cameraContainer = document.getElementById('cameraContainer');
347
+ const videoElement = document.getElementById('videoElement');
348
+ const captureBtn = document.getElementById('captureBtn');
349
+ const scaleSlider = document.getElementById('scaleSlider');
350
+ const scaleValue = document.getElementById('scaleValue');
351
+ const detectBtn = document.getElementById('detectBtn');
352
+ const outputImage = document.getElementById('outputImage');
353
+ const placeholderText = document.getElementById('placeholderText');
354
+ const resultsContainer = document.getElementById('resultsContainer');
355
+ const facesCount = document.getElementById('facesCount');
356
+ const facesDetails = document.getElementById('facesDetails');
357
+ const loading = document.getElementById('loading');
358
+
359
+ // State variables
360
+ let currentImage = null;
361
+ let stream = null;
362
+
363
+ // Event Listeners
364
+ uploadBtn.addEventListener('click', () => {
365
+ uploadBtn.classList.add('active');
366
+ cameraBtn.classList.remove('active');
367
+ uploadArea.style.display = 'block';
368
+ cameraContainer.style.display = 'none';
369
+ stopCamera();
370
+ });
371
+
372
+ cameraBtn.addEventListener('click', () => {
373
+ cameraBtn.classList.add('active');
374
+ uploadBtn.classList.remove('active');
375
+ uploadArea.style.display = 'none';
376
+ cameraContainer.style.display = 'block';
377
+ startCamera();
378
+ });
379
+
380
+ uploadArea.addEventListener('click', () => {
381
+ fileInput.click();
382
+ });
383
+
384
+ fileInput.addEventListener('change', handleImageUpload);
385
+ captureBtn.addEventListener('click', captureImage);
386
+ detectBtn.addEventListener('click', detectFaces);
387
+
388
+ scaleSlider.addEventListener('input', () => {
389
+ scaleValue.textContent = scaleSlider.value;
390
+ });
391
+
392
+ // Functions
393
+ function handleImageUpload(event) {
394
+ const file = event.target.files[0];
395
+ if (file) {
396
+ const reader = new FileReader();
397
+ reader.onload = function(e) {
398
+ currentImage = e.target.result;
399
+ outputImage.src = currentImage;
400
+ outputImage.style.display = 'block';
401
+ placeholderText.style.display = 'none';
402
+ detectBtn.disabled = false;
403
+ resultsContainer.style.display = 'none';
404
+ };
405
+ reader.readAsDataURL(file);
406
+ }
407
+ }
408
+
409
+ function startCamera() {
410
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
411
+ navigator.mediaDevices.getUserMedia({ video: true })
412
+ .then(function(mediaStream) {
413
+ stream = mediaStream;
414
+ videoElement.srcObject = stream;
415
+ detectBtn.disabled = false;
416
+ })
417
+ .catch(function(error) {
418
+ console.error("Camera error: ", error);
419
+ alert("Unable to access camera. Please check permissions.");
420
+ });
421
+ } else {
422
+ alert("Your browser doesn't support camera access.");
423
+ }
424
+ }
425
+
426
+ function stopCamera() {
427
+ if (stream) {
428
+ stream.getTracks().forEach(track => track.stop());
429
+ stream = null;
430
+ }
431
+ }
432
+
433
+ function captureImage() {
434
+ const canvas = document.createElement('canvas');
435
+ canvas.width = videoElement.videoWidth;
436
+ canvas.height = videoElement.videoHeight;
437
+ const ctx = canvas.getContext('2d');
438
+ ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
439
+
440
+ currentImage = canvas.toDataURL('image/png');
441
+ outputImage.src = currentImage;
442
+ outputImage.style.display = 'block';
443
+ placeholderText.style.display = 'none';
444
+ resultsContainer.style.display = 'none';
445
+ }
446
+
447
+ async function detectFaces() {
448
+ if (!currentImage) {
449
+ alert("Please upload an image or capture from camera first.");
450
+ return;
451
+ }
452
+
453
+ loading.style.display = 'block';
454
+ detectBtn.disabled = true;
455
+
456
+ try {
457
+ const response = await fetch('/detect', {
458
+ method: 'POST',
459
+ headers: {
460
+ 'Content-Type': 'application/json',
461
+ },
462
+ body: JSON.stringify({
463
+ image: currentImage,
464
+ scale: scaleSlider.value
465
+ })
466
+ });
467
+
468
+ const data = await response.json();
469
+
470
+ if (data.success) {
471
+ outputImage.src = data.result_image;
472
+ displayResults(data.faces_detected, data.face_data);
473
+ } else {
474
+ alert('Error detecting faces: ' + data.error);
475
+ }
476
+ } catch (error) {
477
+ console.error('Detection error:', error);
478
+ alert('Error detecting faces. Please try again.');
479
+ } finally {
480
+ loading.style.display = 'none';
481
+ detectBtn.disabled = false;
482
+ }
483
+ }
484
+
485
+ function displayResults(faceCount, faceData) {
486
+ facesCount.textContent = faceCount;
487
+
488
+ facesDetails.innerHTML = '';
489
+ faceData.forEach(face => {
490
+ const faceElement = document.createElement('div');
491
+ faceElement.className = 'result-item';
492
+ faceElement.innerHTML = `
493
+ <span class="result-label">Face ${face.id}:</span>
494
+ <span class="result-value">${face.gender}, ${face.age}</span>
495
+ `;
496
+ facesDetails.appendChild(faceElement);
497
+ });
498
+
499
+ resultsContainer.style.display = 'block';
500
+ }
501
+
502
+ // Initialize
503
+ uploadBtn.click(); // Start with upload mode
504
+ </script>
505
+ </body>
506
+ </html>