SakibAhmed commited on
Commit
75d2ec6
·
verified ·
1 Parent(s): decb0e0

Upload index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +198 -292
templates/index.html CHANGED
@@ -19,281 +19,151 @@
19
  position: relative;
20
  }
21
 
22
- /* Animated background particles */
23
- .particles {
24
- position: absolute;
25
- width: 100%;
26
- height: 100%;
27
- overflow: hidden;
28
- z-index: 0;
29
  }
30
 
31
- .particle {
32
- position: absolute;
33
- width: 2px;
34
- height: 2px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  background: #00d4ff;
36
- border-radius: 50%;
37
- animation: float 6s ease-in-out infinite;
38
- opacity: 0.6;
39
  }
 
40
 
 
 
 
 
 
 
41
  @keyframes float {
42
  0%, 100% { transform: translateY(0px) rotate(0deg); }
43
  50% { transform: translateY(-20px) rotate(180deg); }
44
  }
45
-
46
  .container {
47
- position: relative;
48
- z-index: 1;
49
- max-width: 800px;
50
- margin: 0 auto;
51
- padding: 2rem;
52
- min-height: 100vh;
53
- display: flex;
54
- flex-direction: column;
55
- justify-content: center;
56
- align-items: center;
57
  }
58
-
59
  .header {
60
- text-align: center;
61
- margin-bottom: 3rem;
62
- animation: slideDown 1s ease-out;
63
  }
64
-
65
  .title {
66
- font-size: 3.5rem;
67
- font-weight: 700;
68
- background: linear-gradient(45deg, #00d4ff, #ff00ff, #00ff88);
69
- background-size: 200% 200%;
70
- -webkit-background-clip: text;
71
- -webkit-text-fill-color: transparent;
72
- background-clip: text;
73
- animation: gradientShift 3s ease-in-out infinite;
74
- margin-bottom: 1rem;
75
- text-shadow: 0 0 30px rgba(0, 212, 255, 0.5);
76
  }
77
-
78
  .subtitle {
79
- font-size: 1.2rem;
80
- color: #a0a0a0;
81
- font-weight: 300;
82
  }
83
-
84
  .upload-area {
85
- width: 100%;
86
- max-width: 500px;
87
- min-height: 300px;
88
- border: 2px dashed #00d4ff;
89
- border-radius: 20px;
90
- background: rgba(0, 212, 255, 0.05);
91
- backdrop-filter: blur(10px);
92
- display: flex;
93
- flex-direction: column;
94
- justify-content: center;
95
- align-items: center;
96
- cursor: pointer;
97
- transition: all 0.3s ease;
98
- position: relative;
99
- overflow: hidden;
100
- animation: slideUp 1s ease-out 0.3s both;
101
  }
102
-
103
  .upload-area:hover {
104
- border-color: #ff00ff;
105
- background: rgba(255, 0, 255, 0.05);
106
- transform: translateY(-5px);
107
- box-shadow: 0 20px 40px rgba(0, 212, 255, 0.2);
108
  }
109
-
110
  .upload-area.dragover {
111
- border-color: #00ff88;
112
- background: rgba(0, 255, 136, 0.1);
113
- transform: scale(1.02);
114
  }
115
-
116
  .upload-icon {
117
- font-size: 4rem;
118
- color: #00d4ff;
119
- margin-bottom: 1rem;
120
- transition: all 0.3s ease;
121
  }
122
-
123
  .upload-area:hover .upload-icon {
124
- color: #ff00ff;
125
- transform: scale(1.1);
126
  }
127
-
128
  .upload-text {
129
- color: #ffffff;
130
- font-size: 1.1rem;
131
- margin-bottom: 0.5rem;
132
- font-weight: 500;
133
  }
134
-
135
  .upload-subtext {
136
- color: #a0a0a0;
137
- font-size: 0.9rem;
138
  }
139
-
140
  .file-input {
141
  display: none;
142
  }
143
-
144
  .preview-container {
145
- margin-top: 2rem;
146
- text-align: center;
147
- animation: fadeIn 0.5s ease-out;
148
  }
149
-
150
  .preview-image {
151
- max-width: 100%;
152
- max-height: 300px;
153
- border-radius: 15px;
154
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
155
- border: 2px solid #00d4ff;
156
  }
157
-
158
  .predict-button {
159
- background: linear-gradient(45deg, #00d4ff, #0099cc);
160
- border: none;
161
- color: white;
162
- padding: 15px 40px;
163
- font-size: 1.1rem;
164
- font-weight: 600;
165
- border-radius: 50px;
166
- cursor: pointer;
167
- margin-top: 1.5rem;
168
- transition: all 0.3s ease;
169
- text-transform: uppercase;
170
- letter-spacing: 1px;
171
- position: relative;
172
- overflow: hidden;
173
  }
174
-
175
  .predict-button:hover {
176
- transform: translateY(-2px);
177
- box-shadow: 0 10px 25px rgba(0, 212, 255, 0.4);
178
- background: linear-gradient(45deg, #ff00ff, #cc0099);
179
  }
180
-
181
  .predict-button:disabled {
182
- opacity: 0.6;
183
- cursor: not-allowed;
184
- transform: none;
185
  }
186
-
187
  .loading {
188
- display: none;
189
- margin-top: 1rem;
190
  }
191
-
192
  .spinner {
193
- width: 40px;
194
- height: 40px;
195
- border: 4px solid rgba(0, 212, 255, 0.3);
196
- border-top: 4px solid #00d4ff;
197
- border-radius: 50%;
198
- animation: spin 1s linear infinite;
199
- margin: 0 auto;
200
  }
201
-
202
- .result-container {
 
203
  margin-top: 2rem;
204
- padding: 2rem;
205
- background: rgba(255, 255, 255, 0.05);
206
- backdrop-filter: blur(15px);
207
- border-radius: 20px;
208
- border: 1px solid rgba(0, 212, 255, 0.3);
209
- animation: slideUp 0.5s ease-out;
210
- text-align: center;
211
  }
212
-
213
- .result-class {
214
- font-size: 2rem;
215
- font-weight: 700;
216
- color: #00ff88;
217
- margin-bottom: 1rem;
 
218
  text-transform: uppercase;
219
- letter-spacing: 2px;
 
 
 
 
 
 
220
  }
221
-
222
  .result-confidence {
223
- font-size: 1.2rem;
224
- color: #ffffff;
225
- margin-bottom: 0.5rem;
226
  }
227
-
228
  .confidence-bar {
229
- width: 100%;
230
- height: 10px;
231
- background: rgba(255, 255, 255, 0.1);
232
- border-radius: 5px;
233
- overflow: hidden;
234
- margin-top: 1rem;
235
  }
236
-
237
  .confidence-fill {
238
- height: 100%;
239
- background: linear-gradient(90deg, #00d4ff, #00ff88);
240
- border-radius: 5px;
241
- transition: width 1s ease-out;
242
- animation: pulse 2s ease-in-out infinite;
243
  }
 
244
 
245
  .error {
246
- color: #ff4444;
247
- background: rgba(255, 68, 68, 0.1);
248
- padding: 1rem;
249
- border-radius: 10px;
250
- border: 1px solid #ff4444;
251
- margin-top: 1rem;
252
- }
253
-
254
- @keyframes slideDown {
255
- from { opacity: 0; transform: translateY(-50px); }
256
- to { opacity: 1; transform: translateY(0); }
257
- }
258
-
259
- @keyframes slideUp {
260
- from { opacity: 0; transform: translateY(50px); }
261
- to { opacity: 1; transform: translateY(0); }
262
- }
263
-
264
- @keyframes fadeIn {
265
- from { opacity: 0; }
266
- to { opacity: 1; }
267
- }
268
-
269
- @keyframes spin {
270
- 0% { transform: rotate(0deg); }
271
- 100% { transform: rotate(360deg); }
272
- }
273
-
274
- @keyframes gradientShift {
275
- 0%, 100% { background-position: 0% 50%; }
276
- 50% { background-position: 100% 50%; }
277
- }
278
-
279
- @keyframes pulse {
280
- 0%, 100% { opacity: 1; }
281
- 50% { opacity: 0.7; }
282
- }
283
-
284
- /* Responsive design */
285
  @media (max-width: 768px) {
286
- .title {
287
- font-size: 2.5rem;
288
- }
289
-
290
- .container {
291
- padding: 1rem;
292
- }
293
-
294
- .upload-area {
295
- min-height: 250px;
296
- }
297
  }
298
  </style>
299
  </head>
@@ -302,10 +172,17 @@
302
 
303
  <div class="container">
304
  <div class="header">
305
- <h1 class="title">YOLO12 Vision AI</h1>
306
  <p class="subtitle">Advanced Image Classification with Neural Networks</p>
307
  </div>
308
 
 
 
 
 
 
 
 
309
  <div class="upload-area" id="uploadArea">
310
  <div class="upload-icon">🔮</div>
311
  <div class="upload-text">Drop your image here or click to upload</div>
@@ -323,13 +200,30 @@
323
  <p style="color: #00d4ff; margin-top: 1rem;">Processing your image...</p>
324
  </div>
325
 
326
- <div class="result-container" id="resultContainer" style="display: none;">
327
- <div class="result-class" id="resultClass"></div>
328
- <div class="result-confidence">
329
- Confidence: <span id="confidencePercentage"></span>
 
 
 
 
 
 
 
 
330
  </div>
331
- <div class="confidence-bar">
332
- <div class="confidence-fill" id="confidenceFill"></div>
 
 
 
 
 
 
 
 
 
333
  </div>
334
  </div>
335
 
@@ -339,21 +233,17 @@
339
  <script>
340
  // Create animated particles
341
  function createParticles() {
342
- const particlesContainer = document.getElementById('particles');
343
- const particleCount = 50;
344
-
345
- for (let i = 0; i < particleCount; i++) {
346
  const particle = document.createElement('div');
347
  particle.className = 'particle';
348
  particle.style.left = Math.random() * 100 + '%';
349
  particle.style.top = Math.random() * 100 + '%';
350
  particle.style.animationDelay = Math.random() * 6 + 's';
351
  particle.style.animationDuration = (3 + Math.random() * 3) + 's';
352
- particlesContainer.appendChild(particle);
353
  }
354
  }
355
-
356
- // Initialize particles
357
  createParticles();
358
 
359
  // DOM elements
@@ -363,74 +253,75 @@
363
  const previewImage = document.getElementById('previewImage');
364
  const predictButton = document.getElementById('predictButton');
365
  const loading = document.getElementById('loading');
366
- const resultContainer = document.getElementById('resultContainer');
367
  const errorContainer = document.getElementById('errorContainer');
368
- const resultClass = document.getElementById('resultClass');
369
- const confidencePercentage = document.getElementById('confidencePercentage');
370
- const confidenceFill = document.getElementById('confidenceFill');
 
 
 
 
 
 
 
 
 
 
 
371
 
372
  let selectedFile = null;
373
-
374
- // Upload area click handler
375
- uploadArea.addEventListener('click', () => {
376
- fileInput.click();
 
 
 
 
 
 
 
 
 
 
 
377
  });
378
 
379
- // File input change handler
 
380
  fileInput.addEventListener('change', handleFileSelect);
381
-
382
- // Drag and drop handlers
383
- uploadArea.addEventListener('dragover', (e) => {
384
- e.preventDefault();
385
- uploadArea.classList.add('dragover');
386
- });
387
-
388
- uploadArea.addEventListener('dragleave', () => {
389
- uploadArea.classList.remove('dragover');
390
- });
391
-
392
  uploadArea.addEventListener('drop', (e) => {
393
  e.preventDefault();
394
  uploadArea.classList.remove('dragover');
395
  const files = e.dataTransfer.files;
396
- if (files.length > 0) {
397
- handleFile(files[0]);
398
- }
399
  });
400
-
401
- // Predict button handler
402
  predictButton.addEventListener('click', predictImage);
403
 
404
  function handleFileSelect(e) {
405
- const file = e.target.files[0];
406
- if (file) {
407
- handleFile(file);
408
- }
409
  }
410
 
411
  function handleFile(file) {
412
- // Validate file type
413
  const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
414
  if (!allowedTypes.includes(file.type)) {
415
  showError('Please select a valid image file (PNG, JPG, JPEG)');
416
  return;
417
  }
418
-
419
- // Validate file size (max 10MB)
420
  if (file.size > 10 * 1024 * 1024) {
421
  showError('File size too large. Please select a file smaller than 10MB.');
422
  return;
423
  }
424
 
425
  selectedFile = file;
426
-
427
- // Show preview
428
  const reader = new FileReader();
429
  reader.onload = (e) => {
430
  previewImage.src = e.target.result;
431
  previewContainer.style.display = 'block';
432
  hideError();
433
- hideResult();
434
  };
435
  reader.readAsDataURL(file);
436
  }
@@ -441,21 +332,17 @@
441
  return;
442
  }
443
 
444
- // Show loading
445
  loading.style.display = 'block';
446
  predictButton.disabled = true;
447
  hideError();
448
- hideResult();
449
 
450
  try {
451
  const formData = new FormData();
452
  formData.append('file', selectedFile);
 
453
 
454
- const response = await fetch('/predict', {
455
- method: 'POST',
456
- body: formData
457
- });
458
-
459
  const data = await response.json();
460
 
461
  if (response.ok) {
@@ -473,16 +360,37 @@
473
  }
474
 
475
  function showResult(data) {
476
- resultClass.textContent = data.class;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  const confidence = Math.round(data.confidence * 100);
478
- confidencePercentage.textContent = confidence + '%';
479
- confidenceFill.style.width = confidence + '%';
480
 
481
- resultContainer.style.display = 'block';
482
 
483
- // Add some visual flair
 
484
  setTimeout(() => {
485
- confidenceFill.style.width = confidence + '%';
486
  }, 100);
487
  }
488
 
@@ -495,23 +403,21 @@
495
  errorContainer.style.display = 'none';
496
  }
497
 
498
- function hideResult() {
499
- resultContainer.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
500
  }
501
-
502
- // Add some interactive effects
503
- document.addEventListener('mousemove', (e) => {
504
- const particles = document.querySelectorAll('.particle');
505
- const x = e.clientX / window.innerWidth;
506
- const y = e.clientY / window.innerHeight;
507
-
508
- particles.forEach((particle, index) => {
509
- const speed = (index % 3 + 1) * 0.5;
510
- const newX = x * speed;
511
- const newY = y * speed;
512
- particle.style.transform = `translate(${newX}px, ${newY}px)`;
513
- });
514
- });
515
  </script>
516
  </body>
517
  </html>
 
19
  position: relative;
20
  }
21
 
22
+ /* --- NEW: Tab Styles --- */
23
+ .tabs {
24
+ display: flex;
25
+ justify-content: center;
26
+ margin-bottom: 2rem;
27
+ animation: slideDown 1s ease-out;
 
28
  }
29
 
30
+ .tab-button {
31
+ background: transparent;
32
+ border: 2px solid #00d4ff;
33
+ color: #00d4ff;
34
+ padding: 10px 25px;
35
+ margin: 0 10px;
36
+ font-size: 1rem;
37
+ font-weight: 600;
38
+ border-radius: 50px;
39
+ cursor: pointer;
40
+ transition: all 0.3s ease;
41
+ }
42
+
43
+ .tab-button:hover {
44
+ background: rgba(0, 212, 255, 0.2);
45
+ transform: translateY(-2px);
46
+ }
47
+
48
+ .tab-button.active {
49
  background: #00d4ff;
50
+ color: #1a1a2e;
51
+ box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
 
52
  }
53
+ /* --- End Tab Styles --- */
54
 
55
+ .particles {
56
+ position: absolute; width: 100%; height: 100%; overflow: hidden; z-index: 0;
57
+ }
58
+ .particle {
59
+ position: absolute; width: 2px; height: 2px; background: #00d4ff; border-radius: 50%; animation: float 6s ease-in-out infinite; opacity: 0.6;
60
+ }
61
  @keyframes float {
62
  0%, 100% { transform: translateY(0px) rotate(0deg); }
63
  50% { transform: translateY(-20px) rotate(180deg); }
64
  }
 
65
  .container {
66
+ position: relative; z-index: 1; max-width: 800px; margin: 0 auto; padding: 2rem; min-height: 100vh; display: flex; flex-direction: column; justify-content: center; align-items: center;
 
 
 
 
 
 
 
 
 
67
  }
 
68
  .header {
69
+ text-align: center; margin-bottom: 2rem; animation: slideDown 1s ease-out;
 
 
70
  }
 
71
  .title {
72
+ font-size: 3.5rem; font-weight: 700; background: linear-gradient(45deg, #00d4ff, #ff00ff, #00ff88); background-size: 200% 200%; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; animation: gradientShift 3s ease-in-out infinite; margin-bottom: 1rem; text-shadow: 0 0 30px rgba(0, 212, 255, 0.5);
 
 
 
 
 
 
 
 
 
73
  }
 
74
  .subtitle {
75
+ font-size: 1.2rem; color: #a0a0a0; font-weight: 300;
 
 
76
  }
 
77
  .upload-area {
78
+ width: 100%; max-width: 500px; min-height: 300px; border: 2px dashed #00d4ff; border-radius: 20px; background: rgba(0, 212, 255, 0.05); backdrop-filter: blur(10px); display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer; transition: all 0.3s ease; position: relative; overflow: hidden; animation: slideUp 1s ease-out 0.3s both;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
 
80
  .upload-area:hover {
81
+ border-color: #ff00ff; background: rgba(255, 0, 255, 0.05); transform: translateY(-5px); box-shadow: 0 20px 40px rgba(0, 212, 255, 0.2);
 
 
 
82
  }
 
83
  .upload-area.dragover {
84
+ border-color: #00ff88; background: rgba(0, 255, 136, 0.1); transform: scale(1.02);
 
 
85
  }
 
86
  .upload-icon {
87
+ font-size: 4rem; color: #00d4ff; margin-bottom: 1rem; transition: all 0.3s ease;
 
 
 
88
  }
 
89
  .upload-area:hover .upload-icon {
90
+ color: #ff00ff; transform: scale(1.1);
 
91
  }
 
92
  .upload-text {
93
+ color: #ffffff; font-size: 1.1rem; margin-bottom: 0.5rem; font-weight: 500;
 
 
 
94
  }
 
95
  .upload-subtext {
96
+ color: #a0a0a0; font-size: 0.9rem;
 
97
  }
 
98
  .file-input {
99
  display: none;
100
  }
 
101
  .preview-container {
102
+ margin-top: 2rem; text-align: center; animation: fadeIn 0.5s ease-out;
 
 
103
  }
 
104
  .preview-image {
105
+ max-width: 100%; max-height: 300px; border-radius: 15px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); border: 2px solid #00d4ff;
 
 
 
 
106
  }
 
107
  .predict-button {
108
+ background: linear-gradient(45deg, #00d4ff, #0099cc); border: none; color: white; padding: 15px 40px; font-size: 1.1rem; font-weight: 600; border-radius: 50px; cursor: pointer; margin-top: 1.5rem; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; position: relative; overflow: hidden;
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  }
 
110
  .predict-button:hover {
111
+ transform: translateY(-2px); box-shadow: 0 10px 25px rgba(0, 212, 255, 0.4); background: linear-gradient(45deg, #ff00ff, #cc0099);
 
 
112
  }
 
113
  .predict-button:disabled {
114
+ opacity: 0.6; cursor: not-allowed; transform: none;
 
 
115
  }
 
116
  .loading {
117
+ display: none; margin-top: 1rem;
 
118
  }
 
119
  .spinner {
120
+ width: 40px; height: 40px; border: 4px solid rgba(0, 212, 255, 0.3); border-top: 4px solid #00d4ff; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto;
 
 
 
 
 
 
121
  }
122
+
123
+ /* --- NEW: Result Area Styles --- */
124
+ .results-wrapper {
125
  margin-top: 2rem;
126
+ width: 100%;
127
+ max-width: 550px;
 
 
 
 
 
128
  }
129
+ .result-container {
130
+ padding: 2rem; background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(15px); border-radius: 20px; border: 1px solid rgba(0, 212, 255, 0.3); animation: slideUp 0.5s ease-out; text-align: center;
131
+ }
132
+ .result-title {
133
+ font-size: 1.1rem;
134
+ color: #00d4ff;
135
+ font-weight: 400;
136
  text-transform: uppercase;
137
+ letter-spacing: 1px;
138
+ margin-bottom: 1.5rem;
139
+ border-bottom: 1px solid rgba(0, 212, 255, 0.2);
140
+ padding-bottom: 0.8rem;
141
+ }
142
+ .result-class {
143
+ font-size: 2rem; font-weight: 700; color: #00ff88; margin-bottom: 1rem; text-transform: uppercase; letter-spacing: 2px;
144
  }
 
145
  .result-confidence {
146
+ font-size: 1.2rem; color: #ffffff; margin-bottom: 0.5rem;
 
 
147
  }
 
148
  .confidence-bar {
149
+ width: 100%; height: 10px; background: rgba(255, 255, 255, 0.1); border-radius: 5px; overflow: hidden; margin-top: 1rem;
 
 
 
 
 
150
  }
 
151
  .confidence-fill {
152
+ height: 100%; background: linear-gradient(90deg, #00d4ff, #00ff88); border-radius: 5px; transition: width 1s ease-out; animation: pulse 2s ease-in-out infinite;
 
 
 
 
153
  }
154
+ /* --- End Result Area Styles --- */
155
 
156
  .error {
157
+ color: #ff4444; background: rgba(255, 68, 68, 0.1); padding: 1rem; border-radius: 10px; border: 1px solid #ff4444; margin-top: 1rem;
158
+ }
159
+ @keyframes slideDown { from { opacity: 0; transform: translateY(-50px); } to { opacity: 1; transform: translateY(0); } }
160
+ @keyframes slideUp { from { opacity: 0; transform: translateY(50px); } to { opacity: 1; transform: translateY(0); } }
161
+ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
162
+ @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
163
+ @keyframes gradientShift { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
164
+ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  @media (max-width: 768px) {
166
+ .title { font-size: 2.5rem; } .container { padding: 1rem; } .upload-area { min-height: 250px; } .tabs { flex-wrap: wrap; } .tab-button { margin-bottom: 10px;}
 
 
 
 
 
 
 
 
 
 
167
  }
168
  </style>
169
  </head>
 
172
 
173
  <div class="container">
174
  <div class="header">
175
+ <h1 class="title">YOLO Vision AI</h1>
176
  <p class="subtitle">Advanced Image Classification with Neural Networks</p>
177
  </div>
178
 
179
+ <!-- NEW: Tabs for model selection -->
180
+ <div class="tabs">
181
+ <button class="tab-button active" data-mode="model1">Model 1</button>
182
+ <button class="tab-button" data-mode="model2">Model 2 (Tyre/Alloy)</button>
183
+ <button class="tab-button" data-mode="combined">Combined</button>
184
+ </div>
185
+
186
  <div class="upload-area" id="uploadArea">
187
  <div class="upload-icon">🔮</div>
188
  <div class="upload-text">Drop your image here or click to upload</div>
 
200
  <p style="color: #00d4ff; margin-top: 1rem;">Processing your image...</p>
201
  </div>
202
 
203
+ <!-- NEW: Wrapper for all result blocks -->
204
+ <div class="results-wrapper" id="resultsWrapper" style="display: none;">
205
+ <!-- Block for Model 1 / Single Result -->
206
+ <div class="result-container" id="resultBlock1" style="display: none;">
207
+ <h3 class="result-title" id="resultTitle1">Prediction</h3>
208
+ <div class="result-class" id="resultClass1"></div>
209
+ <div class="result-confidence">
210
+ Confidence: <span id="confidencePercentage1"></span>
211
+ </div>
212
+ <div class="confidence-bar">
213
+ <div class="confidence-fill" id="confidenceFill1"></div>
214
+ </div>
215
  </div>
216
+
217
+ <!-- Block for Model 2 (in combined mode) -->
218
+ <div class="result-container" id="resultBlock2" style="display: none; margin-top: 1.5rem;">
219
+ <h3 class="result-title">Model 2 Prediction</h3>
220
+ <div class="result-class" id="resultClass2"></div>
221
+ <div class="result-confidence">
222
+ Confidence: <span id="confidencePercentage2"></span>
223
+ </div>
224
+ <div class="confidence-bar">
225
+ <div class="confidence-fill" id="confidenceFill2"></div>
226
+ </div>
227
  </div>
228
  </div>
229
 
 
233
  <script>
234
  // Create animated particles
235
  function createParticles() {
236
+ const container = document.getElementById('particles');
237
+ for (let i = 0; i < 50; i++) {
 
 
238
  const particle = document.createElement('div');
239
  particle.className = 'particle';
240
  particle.style.left = Math.random() * 100 + '%';
241
  particle.style.top = Math.random() * 100 + '%';
242
  particle.style.animationDelay = Math.random() * 6 + 's';
243
  particle.style.animationDuration = (3 + Math.random() * 3) + 's';
244
+ container.appendChild(particle);
245
  }
246
  }
 
 
247
  createParticles();
248
 
249
  // DOM elements
 
253
  const previewImage = document.getElementById('previewImage');
254
  const predictButton = document.getElementById('predictButton');
255
  const loading = document.getElementById('loading');
 
256
  const errorContainer = document.getElementById('errorContainer');
257
+ const resultsWrapper = document.getElementById('resultsWrapper');
258
+
259
+ // Result Block 1 Elements
260
+ const resultBlock1 = document.getElementById('resultBlock1');
261
+ const resultTitle1 = document.getElementById('resultTitle1');
262
+ const resultClass1 = document.getElementById('resultClass1');
263
+ const confidencePercentage1 = document.getElementById('confidencePercentage1');
264
+ const confidenceFill1 = document.getElementById('confidenceFill1');
265
+
266
+ // Result Block 2 Elements
267
+ const resultBlock2 = document.getElementById('resultBlock2');
268
+ const resultClass2 = document.getElementById('resultClass2');
269
+ const confidencePercentage2 = document.getElementById('confidencePercentage2');
270
+ const confidenceFill2 = document.getElementById('confidenceFill2');
271
 
272
  let selectedFile = null;
273
+ let currentMode = 'model1'; // NEW: state for current model
274
+
275
+ // --- NEW: Tab switching logic ---
276
+ document.querySelectorAll('.tab-button').forEach(button => {
277
+ button.addEventListener('click', (e) => {
278
+ // Update active button
279
+ document.querySelector('.tab-button.active').classList.remove('active');
280
+ e.target.classList.add('active');
281
+
282
+ // Update mode
283
+ currentMode = e.target.dataset.mode;
284
+
285
+ // Reset UI
286
+ resetUI();
287
+ });
288
  });
289
 
290
+ // Event Listeners
291
+ uploadArea.addEventListener('click', () => fileInput.click());
292
  fileInput.addEventListener('change', handleFileSelect);
293
+ uploadArea.addEventListener('dragover', (e) => { e.preventDefault(); uploadArea.classList.add('dragover'); });
294
+ uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
 
 
 
 
 
 
 
 
 
295
  uploadArea.addEventListener('drop', (e) => {
296
  e.preventDefault();
297
  uploadArea.classList.remove('dragover');
298
  const files = e.dataTransfer.files;
299
+ if (files.length > 0) handleFile(files[0]);
 
 
300
  });
 
 
301
  predictButton.addEventListener('click', predictImage);
302
 
303
  function handleFileSelect(e) {
304
+ if (e.target.files.length > 0) handleFile(e.target.files[0]);
 
 
 
305
  }
306
 
307
  function handleFile(file) {
 
308
  const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg'];
309
  if (!allowedTypes.includes(file.type)) {
310
  showError('Please select a valid image file (PNG, JPG, JPEG)');
311
  return;
312
  }
 
 
313
  if (file.size > 10 * 1024 * 1024) {
314
  showError('File size too large. Please select a file smaller than 10MB.');
315
  return;
316
  }
317
 
318
  selectedFile = file;
 
 
319
  const reader = new FileReader();
320
  reader.onload = (e) => {
321
  previewImage.src = e.target.result;
322
  previewContainer.style.display = 'block';
323
  hideError();
324
+ hideResults();
325
  };
326
  reader.readAsDataURL(file);
327
  }
 
332
  return;
333
  }
334
 
 
335
  loading.style.display = 'block';
336
  predictButton.disabled = true;
337
  hideError();
338
+ hideResults();
339
 
340
  try {
341
  const formData = new FormData();
342
  formData.append('file', selectedFile);
343
+ formData.append('model_type', currentMode); // NEW: Send current mode to backend
344
 
345
+ const response = await fetch('/predict', { method: 'POST', body: formData });
 
 
 
 
346
  const data = await response.json();
347
 
348
  if (response.ok) {
 
360
  }
361
 
362
  function showResult(data) {
363
+ resultsWrapper.style.display = 'block';
364
+
365
+ if (currentMode === 'combined') {
366
+ // Display two results
367
+ if (data.model1_result) {
368
+ displaySingleResult(data.model1_result, 'Model 1 Prediction', resultBlock1, resultTitle1, resultClass1, confidencePercentage1, confidenceFill1);
369
+ }
370
+ if (data.model2_result) {
371
+ displaySingleResult(data.model2_result, 'Model 2 Prediction', resultBlock2, null, resultClass2, confidencePercentage2, confidenceFill2);
372
+ }
373
+ } else {
374
+ // Display single result
375
+ const title = currentMode === 'model1' ? 'Model 1 Prediction' : 'Model 2 Prediction';
376
+ displaySingleResult(data, title, resultBlock1, resultTitle1, resultClass1, confidencePercentage1, confidenceFill1);
377
+ }
378
+ }
379
+
380
+ // NEW: Helper function to populate a result block
381
+ function displaySingleResult(data, title, block, titleEl, classEl, percentEl, fillEl) {
382
+ if (!data) return;
383
+ if (titleEl) titleEl.textContent = title;
384
+ classEl.textContent = data.class;
385
  const confidence = Math.round(data.confidence * 100);
386
+ percentEl.textContent = confidence + '%';
 
387
 
388
+ block.style.display = 'block';
389
 
390
+ // Animate confidence bar
391
+ fillEl.style.width = '0%';
392
  setTimeout(() => {
393
+ fillEl.style.width = confidence + '%';
394
  }, 100);
395
  }
396
 
 
403
  errorContainer.style.display = 'none';
404
  }
405
 
406
+ function hideResults() {
407
+ resultsWrapper.style.display = 'none';
408
+ resultBlock1.style.display = 'none';
409
+ resultBlock2.style.display = 'none';
410
+ }
411
+
412
+ // NEW: Function to reset the entire UI state
413
+ function resetUI() {
414
+ hideResults();
415
+ hideError();
416
+ loading.style.display = 'none';
417
+ previewContainer.style.display = 'none';
418
+ fileInput.value = ''; // Clear file input
419
+ selectedFile = null;
420
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
  </script>
422
  </body>
423
  </html>