Docfile commited on
Commit
f5f210d
·
verified ·
1 Parent(s): 7d77f9b

Create templates/index.html

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