Docfile commited on
Commit
dab8075
·
verified ·
1 Parent(s): 62e9f7a

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +660 -0
templates/index.html ADDED
@@ -0,0 +1,660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ --- START OF FILE templates_index (10).html ---
2
+
3
+ <!DOCTYPE html>
4
+ <html lang="fr">
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>FaceAI Pro - Démonstration de Reconnaissance Faciale</title>
9
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
12
+ <style>
13
+ :root {
14
+ --primary-color: #2563eb; /* Bleu plus formel */
15
+ --secondary-color: #1d4ed8; /* Bleu secondaire */
16
+ --gray-dark: #1f2937;
17
+ --gray-light: #f3f4f6;
18
+ --font-sans: 'Inter', sans-serif; /* Police plus professionnelle */
19
+ }
20
+
21
+ body {
22
+ font-family: var(--font-sans);
23
+ background-color: var(--gray-light);
24
+ color: var(--gray-dark);
25
+ line-height: 1.6;
26
+ }
27
+
28
+ /* Animations */
29
+ @keyframes pulse-border {
30
+ 0% { box-shadow: 0 0 0 0 rgba(var(--primary-color), 0.7); }
31
+ 70% { box-shadow: 0 0 0 10px rgba(var(--primary-color), 0); }
32
+ 100% { box-shadow: 0 0 0 0 rgba(var(--primary-color), 0); }
33
+ }
34
+
35
+ @keyframes spin {
36
+ 100% { transform: rotate(360deg); }
37
+ }
38
+
39
+ /* Composants */
40
+ .container {
41
+ max-width: 1200px;
42
+ }
43
+
44
+ .preview-container {
45
+ position: relative;
46
+ aspect-ratio: 1;
47
+ background: white;
48
+ border-radius: 0.5rem;
49
+ overflow: hidden;
50
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
51
+ }
52
+
53
+ .preview-image {
54
+ position: absolute;
55
+ top: 0;
56
+ left: 0;
57
+ width: 100%;
58
+ height: 100%;
59
+ background-size: cover;
60
+ background-position: center;
61
+ transition: transform 0.2s ease;
62
+ }
63
+
64
+ .preview-container:hover .preview-image {
65
+ transform: scale(1.03);
66
+ }
67
+
68
+ .delete-btn {
69
+ position: absolute;
70
+ bottom: 0.5rem;
71
+ right: 0.5rem;
72
+ background-color: rgba(220, 53, 69, 0.9); /* Red-500 with opacity */
73
+ color: white;
74
+ padding: 0.4rem 0.5rem;
75
+ border-radius: 50%;
76
+ opacity: 0;
77
+ transition: opacity 0.2s ease;
78
+ cursor: pointer;
79
+ z-index: 10;
80
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
81
+ }
82
+
83
+ .preview-container:hover .delete-btn {
84
+ opacity: 0.9;
85
+ }
86
+
87
+ .preview-container:hover .delete-btn:hover {
88
+ background-color: rgba(220, 53, 69, 1);
89
+ opacity: 1;
90
+ }
91
+
92
+ .loading-overlay {
93
+ position: fixed;
94
+ top: 0;
95
+ left: 0;
96
+ width: 100%;
97
+ height: 100%;
98
+ background: rgba(0, 0, 0, 0.8);
99
+ display: none;
100
+ justify-content: center;
101
+ align-items: center;
102
+ z-index: 1000;
103
+ }
104
+
105
+ .progress-ring {
106
+ animation: spin 1.5s linear infinite;
107
+ width: 60px;
108
+ height: 60px;
109
+ }
110
+
111
+ .btn-primary {
112
+ background-color: var(--primary-color);
113
+ color: white;
114
+ transition: all 0.2s ease;
115
+ font-weight: 600;
116
+ padding: 0.75rem 1.5rem;
117
+ border-radius: 0.375rem;
118
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
119
+ }
120
+
121
+ .btn-primary:hover {
122
+ background-color: var(--secondary-color);
123
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
124
+ }
125
+
126
+ .btn-primary:active {
127
+ transform: translateY(1px);
128
+ }
129
+
130
+ .btn-secondary {
131
+ background-color: #4b5563;
132
+ color: white;
133
+ transition: all 0.2s ease;
134
+ font-weight: 600;
135
+ padding: 0.75rem 1.5rem;
136
+ border-radius: 0.375rem;
137
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
138
+ }
139
+
140
+ .btn-secondary:hover {
141
+ background-color: #374151;
142
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
143
+ }
144
+
145
+ .result-card {
146
+ border: 1px solid #e5e7eb;
147
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
148
+ padding: 1.5rem;
149
+ border-radius: 0.5rem;
150
+ background-color: white;
151
+ }
152
+
153
+ .result-card.show {
154
+ animation: fadeInUp 0.4s; /* Animation plus subtile */
155
+ }
156
+
157
+ @keyframes fadeInUp {
158
+ from {
159
+ opacity: 0;
160
+ transform: translate3d(0, 20px, 0);
161
+ }
162
+ to {
163
+ opacity: 1;
164
+ transform: none;
165
+ }
166
+ }
167
+
168
+ .camera-feed {
169
+ border-radius: 0.5rem;
170
+ transform: scaleX(-1);
171
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
172
+ }
173
+
174
+ .github-corner {
175
+ position: fixed;
176
+ top: 0;
177
+ right: 0;
178
+ width: 70px;
179
+ height: 70px;
180
+ z-index: 1001;
181
+ }
182
+
183
+ .github-corner:hover {
184
+ transform: scale(1.05);
185
+ transition: transform 0.2s ease;
186
+ }
187
+
188
+ .section-title {
189
+ font-size: 1.75rem;
190
+ font-weight: 700;
191
+ color: var(--gray-dark);
192
+ margin-bottom: 1.5rem;
193
+ display: flex;
194
+ align-items: center;
195
+ border-bottom: 2px solid var(--primary-color);
196
+ padding-bottom: 0.75rem;
197
+ }
198
+
199
+ .section-title i {
200
+ color: var(--primary-color);
201
+ margin-right: 1rem;
202
+ font-size: 1.5rem;
203
+ }
204
+
205
+ .input-hidden {
206
+ display: none;
207
+ }
208
+
209
+ .comparison-result {
210
+ font-size: 1.2rem;
211
+ margin-bottom: 0.75rem;
212
+ }
213
+
214
+ .comparison-result.positive {
215
+ color: #22c55e; /* Vert plus formel */
216
+ font-weight: 600;
217
+ }
218
+
219
+ .comparison-result.negative {
220
+ color: #ef4444; /* Rouge plus formel */
221
+ font-weight: 600;
222
+ }
223
+
224
+ .progress-bar {
225
+ height: 1rem;
226
+ background-color: #e5e7eb;
227
+ border-radius: 0.5rem;
228
+ margin-bottom: 1rem;
229
+ overflow: hidden;
230
+ }
231
+
232
+ .progress-bar-fill {
233
+ height: 100%;
234
+ background-color: var(--primary-color);
235
+ width: 0%;
236
+ transition: width 0.5s ease;
237
+ }
238
+
239
+ .project-description {
240
+ background-color: white;
241
+ padding: 2.5rem;
242
+ border-radius: 0.5rem;
243
+ margin-bottom: 2.5rem;
244
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
245
+ }
246
+
247
+ .project-description h2 {
248
+ font-size: 2.25rem;
249
+ font-weight: 800;
250
+ color: var(--gray-dark);
251
+ margin-bottom: 1.5rem;
252
+ line-height: 1.2;
253
+ }
254
+
255
+ .project-description p {
256
+ font-size: 1.1rem;
257
+ color: #4b5563;
258
+ line-height: 1.7;
259
+ }
260
+
261
+ .project-description ul {
262
+ list-style-type: disc;
263
+ margin-left: 1.5rem;
264
+ color: #4b5563;
265
+ margin-top: 1rem;
266
+ }
267
+
268
+ .project-description ul li {
269
+ margin-bottom: 0.75rem;
270
+ font-size: 1.1rem;
271
+ line-height: 1.7;
272
+ }
273
+ .project-description ul li ul{
274
+ list-style-type: circle;
275
+ }
276
+
277
+ .tech-used {
278
+ margin-top: 0.5rem;
279
+ }
280
+
281
+ /* Navigation */
282
+ .navbar {
283
+ background-color: white;
284
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
285
+ padding: 1rem 1.5rem;
286
+ }
287
+
288
+ .navbar .container {
289
+ display: flex;
290
+ align-items: center;
291
+ justify-content: space-between;
292
+ }
293
+
294
+ .navbar-brand {
295
+ display: flex;
296
+ align-items: center;
297
+ font-weight: 800;
298
+ font-size: 1.75rem;
299
+ color: var(--gray-dark);
300
+ }
301
+
302
+ .navbar-brand i {
303
+ color: var(--primary-color);
304
+ margin-right: 1rem;
305
+ font-size: 2rem;
306
+ }
307
+ .navbar-brand:hover{
308
+ color: var(--secondary-color);
309
+ transition: all 0.2s ease;
310
+ }
311
+
312
+ .navbar-links {
313
+ display: flex;
314
+ align-items: center;
315
+ }
316
+
317
+ .navbar-links a {
318
+ color: #4b5563;
319
+ font-weight: 500;
320
+ margin-left: 2rem;
321
+ transition: color 0.2s ease;
322
+ }
323
+
324
+ .navbar-links a:hover {
325
+ color: var(--primary-color);
326
+ }
327
+ .font-bold{
328
+ font-weight: 700;
329
+ }
330
+
331
+ /* Responsiveness */
332
+ @media (max-width: 768px) {
333
+ .preview-container {
334
+ margin-bottom: 1rem;
335
+ }
336
+
337
+ .project-description {
338
+ margin-bottom: 1.5rem;
339
+ padding: 1.5rem;
340
+ }
341
+
342
+ .project-description h2 {
343
+ font-size: 2rem;
344
+ }
345
+
346
+ .section-title {
347
+ font-size: 1.5rem;
348
+ }
349
+
350
+ .section-title i {
351
+ font-size: 1.25rem;
352
+ }
353
+
354
+ .navbar-brand {
355
+ font-size: 1.5rem;
356
+ }
357
+
358
+ .navbar-brand i {
359
+ font-size: 1.75rem;
360
+ }
361
+
362
+ .navbar-links a {
363
+ margin-left: 1.5rem;
364
+ }
365
+ }
366
+ </style>
367
+ </head>
368
+ <body class="bg-gray-100">
369
+ <div class="loading-overlay">
370
+ <svg class="progress-ring text-blue-500" viewBox="0 0 50 50">
371
+ <circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-dasharray="80, 200"/>
372
+ </svg>
373
+ </div>
374
+
375
+ <!-- Navigation -->
376
+ <nav class="navbar">
377
+ <div class="container mx-auto">
378
+ <a href="/" class="navbar-brand">
379
+ <i class="fas fa-brain text-blue-600"></i>
380
+ FaceAI Pro
381
+ </a>
382
+ <div class="navbar-links">
383
+ <a href="#" class="hover:text-blue-600">
384
+ <i class="fas fa-question-circle text-lg"></i>
385
+ <span class="hidden md:inline-block ml-1">Aide</span>
386
+ </a>
387
+ <a href="#" class="hover:text-blue-600">
388
+ <i class="fas fa-cog text-lg"></i>
389
+ <span class="hidden md:inline-block ml-1">Paramètres</span>
390
+ </a>
391
+ </div>
392
+ </div>
393
+ </nav>
394
+
395
+ <!-- Contenu principal -->
396
+ <main class="container mx-auto p-6">
397
+ <!-- Description du projet -->
398
+ <div class="project-description">
399
+ <h2 class="text-3xl font-extrabold text-gray-900 mb-4 tracking-tight">FaceAI Pro : Démonstration de Reconnaissance Faciale en Temps Réel</h2>
400
+ <p class="text-gray-700">
401
+ <strong class="font-bold">FaceAI Pro</strong> est une application web pour démontrer la reconnaissance faciale. Comparez deux visages et évaluez leur similarité en temps réel.
402
+ </p>
403
+ <p class="mt-4 text-gray-700">
404
+ Explorez cette technologie et ses applications dans la sécurité et l'authentification.
405
+ </p>
406
+ <h3 class="text-xl font-bold text-gray-800 mt-8 mb-3">Fonctionnalités Clés :</h3>
407
+ <ul class="text-gray-700">
408
+ <li>Comparaison en temps réel de deux images de visages.</li>
409
+ <li>Détection automatique des visages.</li>
410
+ <li>Évaluation quantitative de la similarité (en pourcentage).</li>
411
+ <li>Capture d'image via la caméra.</li>
412
+ <li>Interface utilisateur simple et intuitive.</li>
413
+ </ul>
414
+ <h3 class="text-xl font-bold text-gray-800 mt-8 mb-3">Technologies Employées :</h3>
415
+ <ul class="text-gray-700">
416
+ <li><strong class="font-bold">Backend :</strong> <a href="https://flask.palletsprojects.com/" target="_blank" class="text-blue-600 hover:underline">Flask</a> (Python).</li>
417
+ <li><strong class="font-bold">Reconnaissance Faciale :</strong> Algorithme de deep learning.</li>
418
+ <li><strong class="font-bold">Frontend :</strong> HTML5, CSS3 (<a href="https://tailwindcss.com/" target="_blank" class="text-blue-600 hover:underline">Tailwind CSS</a>), JavaScript.</li>
419
+ <li><strong class="font-bold">Icônes :</strong> <a href="https://fontawesome.com/" target="_blank" class="text-blue-600 hover:underline">Font Awesome</a>.</li>
420
+ <li><strong class="font-bold">Animations :</strong> <a href="https://animate.style/" target="_blank" class="text-blue-600 hover:underline">Animate.css</a>.</li>
421
+ </ul>
422
+ <p class="mt-4 text-gray-700">
423
+ Le code source est disponible sur GitHub : <a href="https://github.com/Yusufibin" target="_blank" class="text-blue-600 hover:underline">https://github.com/Yusufibin</a>.
424
+ </p>
425
+ </div>
426
+ <div class="grid md:grid-cols-2 gap-8">
427
+ <!-- Section de capture -->
428
+ <div class="bg-white rounded-lg shadow-md p-6">
429
+ <h2 class="section-title">
430
+ <i class="fas fa-camera"></i>
431
+ Capture d'Images
432
+ </h2>
433
+
434
+ <div class="space-y-4">
435
+ <div class="flex space-x-4">
436
+ <label for="fileInput" class="btn-primary text-white px-6 py-3 rounded-lg flex items-center cursor-pointer">
437
+ <i class="fas fa-upload mr-2"></i>
438
+ Importer des Images
439
+ </label>
440
+ <button id="cameraBtn" class="btn-secondary text-white px-6 py-3 rounded-lg flex items-center transition-all">
441
+ <i class="fas fa-video mr-2"></i>
442
+ Utiliser la Caméra
443
+ </button>
444
+ </div>
445
+
446
+ <input type="file" id="fileInput" class="input-hidden" accept="image/*" multiple>
447
+ <video id="video" class="camera-feed w-full hidden" autoplay muted playsinline></video>
448
+
449
+ <div class="grid grid-cols-2 gap-4">
450
+ <div class="preview-container">
451
+ <div id="preview1" class="preview-image"></div>
452
+ <button class="delete-btn" data-target="preview1" title="Supprimer">
453
+ <i class="fas fa-times"></i>
454
+ </button>
455
+ </div>
456
+ <div class="preview-container">
457
+ <div id="preview2" class="preview-image"></div>
458
+ <button class="delete-btn" data-target="preview2" title="Supprimer">
459
+ <i class="fas fa-times"></i>
460
+ </button>
461
+ </div>
462
+ </div>
463
+ </div>
464
+ </div>
465
+
466
+ <!-- Section des résultats -->
467
+ <div class="bg-white rounded-lg shadow-md p-6">
468
+ <h2 class="section-title">
469
+ <i class="fas fa-chart-bar"></i>
470
+ Résultats de l'Analyse
471
+ </h2>
472
+
473
+ <div id="results" class="result-card">
474
+ <div class="text-center text-gray-500">
475
+ <i class="fas fa-upload text-4xl mb-3"></i>
476
+ <p class="text-gray-600">Importez ou capturez deux images pour démarrer l'analyse.</p>
477
+ </div>
478
+ </div>
479
+
480
+ <button id="compareBtn" class="btn-primary w-full mt-6 py-3 rounded-lg text-white font-semibold hidden">
481
+ <i class="fas fa-sync-alt mr-2"></i>
482
+ Lancer la Comparaison
483
+ </button>
484
+ </div>
485
+ </div>
486
+ </main>
487
+
488
+ <!-- GitHub Corner -->
489
+ <a href="https://github.com/Yusufibin" class="github-corner" target="_blank" aria-label="Voir le code source sur GitHub">
490
+ <svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--gray-dark); color:#fff;">
491
+ <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
492
+ <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
493
+ <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path>
494
+ </svg>
495
+ </a>
496
+
497
+ <!-- Script JavaScript -->
498
+ <script>
499
+ let uploadedImages = [];
500
+
501
+ const fileInput = document.getElementById('fileInput');
502
+ fileInput.addEventListener('change', handleImageUpload);
503
+
504
+ function handleImageUpload(event) {
505
+ const files = event.target.files;
506
+ for (let i = 0; i < files.length; i++) {
507
+ const file = files[i];
508
+ if (uploadedImages.length < 2 && file.type.startsWith('image/')) {
509
+ const reader = new FileReader();
510
+ reader.onload = (e) => {
511
+ const previewId = uploadedImages.length === 0 ? 'preview1' : 'preview2';
512
+ const previewImg = document.getElementById(previewId);
513
+ previewImg.style.backgroundImage = `url(${e.target.result})`;
514
+ uploadedImages.push({ file: file, preview: previewId });
515
+ updateCompareButtonState();
516
+ };
517
+ reader.readAsDataURL(file);
518
+ }
519
+ }
520
+ }
521
+
522
+ document.querySelectorAll('.delete-btn').forEach(button => {
523
+ button.addEventListener('click', function() {
524
+ const targetPreviewId = this.dataset.target;
525
+ const targetPreview = document.getElementById(targetPreviewId);
526
+ targetPreview.style.backgroundImage = '';
527
+
528
+ uploadedImages = uploadedImages.filter(image => image.preview !== targetPreviewId);
529
+
530
+ updateCompareButtonState();
531
+ });
532
+ });
533
+
534
+ const cameraBtn = document.getElementById('cameraBtn');
535
+ const video = document.getElementById('video');
536
+ let stream = null;
537
+
538
+ cameraBtn.addEventListener('click', async () => {
539
+ if (stream) {
540
+ // Arrêter la caméra
541
+ stream.getTracks().forEach(track => track.stop());
542
+ video.classList.add('hidden');
543
+ cameraBtn.innerHTML = '<i class="fas fa-video mr-2"></i>Utiliser la Caméra';
544
+ stream = null;
545
+ } else {
546
+ // Démarrer la caméra
547
+ try {
548
+ stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' } });
549
+ video.srcObject = stream;
550
+ video.classList.remove('hidden');
551
+ cameraBtn.innerHTML = '<i class="fas fa-times mr-2"></i>Arrêter la caméra';
552
+ } catch (err) {
553
+ console.error('Erreur lors de l\'accès à la caméra:', err);
554
+ }
555
+ }
556
+ });
557
+
558
+ video.addEventListener('click', () => {
559
+ if (stream && uploadedImages.length < 2) {
560
+ const canvas = document.createElement('canvas');
561
+ canvas.width = video.videoWidth;
562
+ canvas.height = video.videoHeight;
563
+ const ctx = canvas.getContext('2d');
564
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
565
+ canvas.toBlob(blob => {
566
+ const file = new File([blob], `capture-${Date.now()}.jpg`, { type: 'image/jpeg' });
567
+ const previewId = uploadedImages.length === 0 ? 'preview1' : 'preview2';
568
+ const previewImg = document.getElementById(previewId);
569
+ previewImg.style.backgroundImage = `url(${URL.createObjectURL(file)})`;
570
+ uploadedImages.push({ file: file, preview: previewId });
571
+ updateCompareButtonState();
572
+ }, 'image/jpeg');
573
+ }
574
+ });
575
+
576
+ const compareBtn = document.getElementById('compareBtn');
577
+ compareBtn.addEventListener('click', () => {
578
+ if (uploadedImages.length === 2) {
579
+ showLoading();
580
+ const formData = new FormData();
581
+ formData.append('file1', uploadedImages[0].file);
582
+ formData.append('file2', uploadedImages[1].file);
583
+
584
+ fetch('/compare', {
585
+ method: 'POST',
586
+ body: formData
587
+ })
588
+ .then(response => response.json())
589
+ .then(data => {
590
+ showResults(data);
591
+ })
592
+ .catch(error => {
593
+ console.error('Erreur lors de la comparaison:', error);
594
+ showError(error);
595
+ })
596
+ .finally(() => {
597
+ hideLoading();
598
+ });
599
+ }
600
+ });
601
+
602
+ function updateCompareButtonState() {
603
+ compareBtn.classList.toggle('hidden', uploadedImages.length < 2);
604
+ }
605
+
606
+ function showResults(data) {
607
+ const resultsDiv = document.getElementById('results');
608
+ resultsDiv.classList.remove('show');
609
+
610
+ setTimeout(() => {
611
+ resultsDiv.innerHTML = `
612
+ <div class="text-center">
613
+ <div class="text-4xl mb-4 ${data.verified ? 'text-green-500' : 'text-red-500'}">
614
+ <i class="fas ${data.verified ? 'fa-check-circle' : 'fa-times-circle'}"></i>
615
+ </div>
616
+ <h3 class="text-xl font-bold mb-2">
617
+ ${data.verified ? 'Visages identiques' : 'Visages différents'}
618
+ </h3>
619
+ <div class="space-y-2">
620
+ <p class="comparison-result ${data.verified ? 'positive' : 'negative'}">
621
+ Similarité: <span class="font-semibold">${data.similarity}%</span>
622
+ </p>
623
+ <div class="progress-bar">
624
+ <div class="progress-bar-fill" style="width: ${data.similarity}%; --progress-value: ${data.similarity};" data-progress="${data.similarity}"></div>
625
+ </div>
626
+ </div>
627
+ </div>
628
+ `;
629
+ resultsDiv.classList.add('show');
630
+ setTimeout(() => {
631
+ const progressBarFill = resultsDiv.querySelector('.progress-bar-fill');
632
+ progressBarFill.style.width = `${data.similarity}%`;
633
+ }, 50);
634
+ }, 300);
635
+ }
636
+
637
+ function showError(error) {
638
+ const resultsDiv = document.getElementById('results');
639
+ resultsDiv.classList.remove('show');
640
+ setTimeout(() => {
641
+ resultsDiv.innerHTML = `
642
+ <div class="text-center text-red-500">
643
+ <i class="fas fa-exclamation-triangle text-4xl mb-3"></i>
644
+ <p>Erreur : ${error.message || 'Une erreur est survenue.'}</p>
645
+ </div>
646
+ `;
647
+ resultsDiv.classList.add('show');
648
+ }, 300);
649
+ }
650
+
651
+ function showLoading() {
652
+ document.querySelector('.loading-overlay').style.display = 'flex';
653
+ }
654
+
655
+ function hideLoading() {
656
+ document.querySelector('.loading-overlay').style.display = 'none';
657
+ }
658
+ </script>
659
+ </body>
660
+ </html>