IThioye commited on
Commit
4874e86
1 Parent(s): cb72b38

added templates

Browse files
Files changed (3) hide show
  1. templates/index.html +554 -0
  2. templates/retrain.html +242 -0
  3. templates/yolo.html +355 -0
templates/index.html ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Image Segmentation Tool</title>
7
+ <style>
8
+ .container {
9
+ max-width: 1200px;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ }
13
+
14
+ .controls {
15
+ margin-bottom: 20px;
16
+ position: sticky;
17
+ top: 0;
18
+ background: white;
19
+ z-index: 100;
20
+ padding: 10px 0;
21
+ }
22
+
23
+ .button {
24
+ padding: 8px 16px;
25
+ margin-right: 10px;
26
+ background-color: #4CAF50;
27
+ color: white;
28
+ border: none;
29
+ border-radius: 4px;
30
+ cursor: pointer;
31
+ }
32
+
33
+ .button.active {
34
+ background-color: #45a049;
35
+ }
36
+
37
+ .button:disabled {
38
+ background-color: #cccccc;
39
+ cursor: not-allowed;
40
+ }
41
+
42
+ .image-container {
43
+ display: grid;
44
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
45
+ gap: 20px;
46
+ }
47
+
48
+ .image-wrapper {
49
+ position: relative;
50
+ border: 1px solid #ddd;
51
+ padding: 10px;
52
+ }
53
+
54
+ .canvas-wrapper {
55
+ position: relative;
56
+ overflow: hidden;
57
+ width: 100%;
58
+ height: 100%;
59
+ }
60
+
61
+ canvas {
62
+ position: absolute;
63
+ top: 0;
64
+ left: 0;
65
+ pointer-events: none;
66
+ width: 100%;
67
+ height: 100%;
68
+ }
69
+
70
+ img {
71
+ max-width: 100%;
72
+ height: auto;
73
+ display: block;
74
+ }
75
+
76
+ .status {
77
+ margin-top: 10px;
78
+ padding: 20px; /* Increased padding for larger size */
79
+ border-radius: 8px; /* More rounded corners */
80
+ display: none;
81
+ position: fixed;
82
+ bottom: 20px;
83
+ right: 20px;
84
+ z-index: 1000;
85
+ width: 300px; /* Define a width for consistency */
86
+ font-size: 1.2em; /* Larger font size */
87
+ background-color: #333; /* Dark background for visibility */
88
+ color: #fff; /* White text color for contrast */
89
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); /* Shadow effect for emphasis */
90
+ text-align: center; /* Center-align the text */
91
+ }
92
+
93
+
94
+ .status.error {
95
+ background-color: #ffebee;
96
+ color: #c62828;
97
+ display: block;
98
+ }
99
+
100
+ .status.success {
101
+ background-color: #e8f5e9;
102
+ color: #2e7d32;
103
+ display: block;
104
+ }
105
+ .status.wait {
106
+ background-color: #fff3e0;
107
+ color: #f57c00;
108
+ display: block;
109
+ }
110
+ .top-right-buttons {
111
+ position: absolute;
112
+ top: 20px;
113
+ right: 20px;
114
+ display: flex;
115
+ gap: 10px;
116
+ }
117
+ </style>
118
+ </head>
119
+ <body>
120
+ <div class="container">
121
+ <h1>Image Annotation Tool</h1>
122
+
123
+ <div class="controls">
124
+ <input type="file" id="fileInput" accept="image/*">
125
+ <button id="voidButton" class="button">Mark Voids</button>
126
+ <button id="componentButton" class="button">Draw Components</button>
127
+ <button id="clearButton" class="button" disabled>Clear All</button>
128
+ <button id="generateMaskButton" class="button" disabled>Generate Mask</button>
129
+ <button id="retrainButton" class="button" disabled>Retrain Model</button>
130
+ </div>
131
+
132
+ <div id="status" class="status"></div>
133
+
134
+ <div class="image-container">
135
+ <div class="image-wrapper">
136
+ <h2>Original Image</h2>
137
+ <div class="canvas-wrapper" id="canvasWrapper">
138
+ <img id="image" src="" alt="Upload an image" draggable="false">
139
+ <canvas id="overlayCanvas"></canvas>
140
+ </div>
141
+ </div>
142
+ <div class="image-wrapper">
143
+ <h2>Segmented Result</h2>
144
+ <img id="segmentedImage" src="" alt="Segmented image will appear here">
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ <div class="top-right-buttons">
150
+ <button class="button" disabled>Go to SAM</button>
151
+ <a href="{{ url_for('yolo') }}" class="button">Go to Yolo</a>
152
+ </div>
153
+
154
+ <script>
155
+ class SegmentationTool {
156
+ constructor() {
157
+ this.initializeElements();
158
+ this.initializeState();
159
+ this.setupEventListeners();
160
+ }
161
+
162
+ initializeElements() {
163
+ this.fileInput = document.getElementById('fileInput');
164
+ this.image = document.getElementById('image');
165
+ this.overlayCanvas = document.getElementById('overlayCanvas');
166
+ this.overlayCtx = this.overlayCanvas.getContext('2d');
167
+ this.segmentedImage = document.getElementById('segmentedImage');
168
+ this.status = document.getElementById('status');
169
+ this.canvasWrapper = document.getElementById('canvasWrapper');
170
+
171
+ this.buttons = {
172
+ void: document.getElementById('voidButton'),
173
+ component: document.getElementById('componentButton'),
174
+ clear: document.getElementById('clearButton'),
175
+ generate: document.getElementById('generateMaskButton'),
176
+ retrain: document.getElementById('retrainButton')
177
+ };
178
+ }
179
+
180
+ initializeState() {
181
+ this.normalizedVoidPoints = [];
182
+ this.normalizedComponentBoxes = [];
183
+ this.currentMode = null;
184
+ this.hoverStart = null;
185
+ this.uploadedFilename = '';
186
+ this.isDrawing = false;
187
+ this.tempBox = null; // Store the current box being drawn
188
+ }
189
+
190
+ setupEventListeners() {
191
+ this.fileInput.addEventListener('change', (e) => this.handleFileUpload(e));
192
+ this.image.addEventListener('load', () => this.handleImageLoad());
193
+ this.buttons.void.addEventListener('click', () => this.setMode('void'));
194
+ this.buttons.component.addEventListener('click', () => this.setMode('component'));
195
+ this.buttons.clear.addEventListener('click', () => this.clearAll());
196
+ this.buttons.generate.addEventListener('click', () => this.generateMask());
197
+ this.buttons.retrain.addEventListener('click', () => this.retrainModel());
198
+
199
+ this.canvasWrapper.addEventListener('click', (e) => this.handleImageClick(e));
200
+ this.canvasWrapper.addEventListener('mousedown', (e) => this.handleMouseDown(e));
201
+ this.canvasWrapper.addEventListener('mousemove', (e) => this.handleMouseMove(e));
202
+ this.canvasWrapper.addEventListener('mouseup', (e) => this.handleMouseUp(e));
203
+
204
+ // Add ResizeObserver
205
+ const resizeObserver = new ResizeObserver(() => {
206
+ this.updateCanvasSize();
207
+ this.redrawAllPoints();
208
+ });
209
+
210
+ resizeObserver.observe(this.canvasWrapper);
211
+ }
212
+
213
+ getImageCoordinates(event) {
214
+ const rect = this.image.getBoundingClientRect();
215
+ const x = event.clientX - rect.left;
216
+ const y = event.clientY - rect.top;
217
+
218
+ return {
219
+ display: [x, y],
220
+ normalized: [x / rect.width, y / rect.height]
221
+ };
222
+ }
223
+
224
+ updateCanvasSize() {
225
+ const rect = this.image.getBoundingClientRect();
226
+ this.overlayCanvas.style.width = `${rect.width}px`;
227
+ this.overlayCanvas.style.height = `${rect.height}px`;
228
+ this.overlayCanvas.width = rect.width;
229
+ this.overlayCanvas.height = rect.height;
230
+ }
231
+
232
+ handleImageLoad() {
233
+ this.updateCanvasSize();
234
+ this.redrawAllPoints();
235
+ }
236
+
237
+ setMode(mode) {
238
+ this.currentMode = this.currentMode === mode ? null : mode;
239
+ Object.values(this.buttons).forEach(button => button.classList.remove('active'));
240
+ if (this.currentMode) {
241
+ this.buttons[this.currentMode].classList.add('active');
242
+ }
243
+ this.canvasWrapper.style.cursor = this.currentMode ? 'crosshair' : 'default';
244
+ }
245
+
246
+ handleImageClick(event) {
247
+ if (this.currentMode !== 'void') return;
248
+
249
+ const coords = this.getImageCoordinates(event);
250
+ this.normalizedVoidPoints.push(coords.normalized);
251
+ this.drawVoidPoint(coords.display);
252
+ }
253
+
254
+ handleMouseDown(event) {
255
+ if (this.currentMode !== 'component') return;
256
+ event.preventDefault();
257
+ this.isDrawing = true;
258
+ const coords = this.getImageCoordinates(event);
259
+ this.hoverStart = coords; // Start point of the box
260
+ this.tempBox = null; // Reset temporary box
261
+ }
262
+
263
+ handleMouseMove(event) {
264
+ if (!this.isDrawing || this.currentMode !== 'component') return;
265
+
266
+ const coords = this.getImageCoordinates(event);
267
+ this.clearOverlay();
268
+ this.redrawAllPoints();
269
+
270
+ // Draw the temporary box
271
+ if (this.hoverStart) {
272
+ this.tempBox = {
273
+ start: this.hoverStart.display,
274
+ end: coords.display
275
+ };
276
+ this.drawComponentBox(this.tempBox.start, this.tempBox.end);
277
+ }
278
+ }
279
+
280
+ handleMouseUp(event) {
281
+ if (this.currentMode !== 'component' || !this.isDrawing) return;
282
+
283
+ const coords = this.getImageCoordinates(event);
284
+
285
+ // Calculate normalized coordinates for the box
286
+ const normalizedBox = [
287
+ Math.min(this.hoverStart.normalized[0], coords.normalized[0]),
288
+ Math.min(this.hoverStart.normalized[1], coords.normalized[1]),
289
+ Math.max(this.hoverStart.normalized[0], coords.normalized[0]),
290
+ Math.max(this.hoverStart.normalized[1], coords.normalized[1])
291
+ ];
292
+
293
+ // Ensure box size is valid
294
+ const minSize = 0.01; // 1% of image size
295
+ if (Math.abs(normalizedBox[2] - normalizedBox[0]) > minSize &&
296
+ Math.abs(normalizedBox[3] - normalizedBox[1]) > minSize) {
297
+ this.normalizedComponentBoxes.push(normalizedBox);
298
+ console.log('Added box:', normalizedBox); // Debug logging
299
+ }
300
+
301
+ // Reset drawing state
302
+ this.isDrawing = false;
303
+ this.hoverStart = null;
304
+ this.tempBox = null; // Clear temp box
305
+ this.redrawAllPoints(); // Redraw all points and boxes
306
+ }
307
+
308
+
309
+ normalizedToDisplay(point) {
310
+ const rect = this.image.getBoundingClientRect();
311
+ return [
312
+ point[0] * rect.width,
313
+ point[1] * rect.height
314
+ ];
315
+ }
316
+
317
+ normalizedToDisplayBox(box) {
318
+ const rect = this.image.getBoundingClientRect();
319
+ return [
320
+ box[0] * rect.width,
321
+ box[1] * rect.height,
322
+ box[2] * rect.width,
323
+ box[3] * rect.height
324
+ ];
325
+ }
326
+
327
+ drawVoidPoint([x, y]) {
328
+ this.overlayCtx.fillStyle = 'rgba(255, 0, 0, 0.5)';
329
+ this.overlayCtx.beginPath();
330
+ this.overlayCtx.arc(x, y, 5, 0, Math.PI * 2);
331
+ this.overlayCtx.fill();
332
+ }
333
+
334
+ drawComponentBox([startX, startY], [endX, endY]) {
335
+ // Draw the box outline
336
+ this.overlayCtx.strokeStyle = 'rgba(0, 255, 0, 0.8)';
337
+ this.overlayCtx.lineWidth = 2;
338
+ this.overlayCtx.strokeRect(
339
+ startX,
340
+ startY,
341
+ endX - startX,
342
+ endY - startY
343
+ );
344
+
345
+ // Add semi-transparent fill
346
+ this.overlayCtx.fillStyle = 'rgba(0, 255, 0, 0.1)';
347
+ this.overlayCtx.fillRect(
348
+ startX,
349
+ startY,
350
+ endX - startX,
351
+ endY - startY
352
+ );
353
+ }
354
+
355
+ clearOverlay() {
356
+ this.overlayCtx.clearRect(0, 0, this.overlayCanvas.width, this.overlayCanvas.height);
357
+ }
358
+
359
+ redrawAllPoints() {
360
+ this.clearOverlay();
361
+
362
+ // Draw all void points
363
+ this.normalizedVoidPoints.forEach(point => {
364
+ const displayPoint = this.normalizedToDisplay(point);
365
+ this.drawVoidPoint(displayPoint);
366
+ });
367
+
368
+ // Draw all component boxes
369
+ this.normalizedComponentBoxes.forEach(box => {
370
+ const displayBox = this.normalizedToDisplayBox(box);
371
+ this.drawComponentBox(
372
+ [displayBox[0], displayBox[1]],
373
+ [displayBox[2], displayBox[3]]
374
+ );
375
+ });
376
+
377
+ // Draw the temporary box if it exists
378
+ if (this.tempBox) {
379
+ this.drawComponentBox(this.tempBox.start, this.tempBox.end);
380
+ }
381
+ }
382
+
383
+ clearAll() {
384
+ // Reset only the data points and visual elements
385
+ this.normalizedVoidPoints = [];
386
+ this.normalizedComponentBoxes = [];
387
+ this.clearOverlay();
388
+ this.currentMode = null;
389
+ this.hoverStart = null;
390
+ this.isDrawing = false;
391
+ this.tempBox = null;
392
+
393
+ // Reset button states
394
+ Object.values(this.buttons).forEach(button => button.classList.remove('active'));
395
+ this.canvasWrapper.style.cursor = 'default';
396
+
397
+ // Clear the segmented image
398
+ this.segmentedImage.src = '';
399
+
400
+ // Make sure the generate button is still enabled if we have an image
401
+ this.buttons.generate.disabled = !this.uploadedFilename;
402
+ this.buttons.clear.disabled = true;
403
+
404
+ // Redraw the canvas to ensure it's clear
405
+ this.redrawAllPoints();
406
+
407
+ this.showStatus('All markings cleared', 'success');
408
+ }
409
+ async handleFileUpload(event) {
410
+ const file = event.target.files[0];
411
+ if (!file) return;
412
+
413
+ this.clearAll();
414
+
415
+ // Show "please wait" message
416
+ this.showStatus('Uploading and embedding image, please wait...', 'wait');
417
+
418
+ try {
419
+ const formData = new FormData();
420
+ formData.append('file', file);
421
+
422
+ const response = await fetch('/upload_sam', {
423
+ method: 'POST',
424
+ body: formData
425
+ });
426
+
427
+ const result = await response.json();
428
+ if (result.error) throw new Error(result.error);
429
+
430
+ this.image.src = result.image_url;
431
+ this.uploadedFilename = result.filename;
432
+ this.originalDimensions = result.dimensions;
433
+ this.buttons.generate.disabled = false;
434
+
435
+
436
+ // Show success message after upload is complete
437
+ this.showStatus('Image uploaded successfully', 'success');
438
+ } catch (error) {
439
+ this.showStatus(`Upload failed: ${error.message}`, 'error');
440
+ }
441
+ }
442
+
443
+ async generateMask() {
444
+ if (!this.uploadedFilename) {
445
+ this.showStatus('Please upload an image first', 'error');
446
+ return;
447
+ }
448
+
449
+ try {
450
+ this.buttons.generate.disabled = true;
451
+
452
+ const requestData = {
453
+ void_points: this.normalizedVoidPoints,
454
+ component_boxes: this.normalizedComponentBoxes,
455
+ filename: this.uploadedFilename,
456
+ original_dimensions: this.originalDimensions
457
+ };
458
+
459
+ console.log('Sending data to backend:', requestData); // Debug logging
460
+
461
+ const response = await fetch('/generate_mask', {
462
+ method: 'POST',
463
+ headers: {
464
+ 'Content-Type': 'application/json',
465
+ },
466
+ body: JSON.stringify(requestData),
467
+ });
468
+
469
+ const result = await response.json();
470
+ if (result.error) throw new Error(result.error);
471
+
472
+ this.segmentedImage.src = result.result_path + '?t=' + new Date().getTime();
473
+ this.showStatus('Mask generated successfully', 'success');
474
+ } catch (error) {
475
+ this.showStatus(`Failed to generate mask: ${error.message}`, 'error');
476
+ console.error('Mask generation error:', error); // Debug logging
477
+ } finally {
478
+ this.buttons.generate.disabled = false;
479
+ this.buttons.retrain.disabled = false;
480
+ this.buttons.clear.style.backgroundColor = '#c62828';
481
+ this.buttons.clear.disabled = false;
482
+ }
483
+ }
484
+ async retrainModel() {
485
+ try {
486
+ this.buttons.retrain.disabled = true;
487
+ this.showStatus('Starting model retraining...', 'wait');
488
+
489
+ // First make the POST request to start the training
490
+ const response = await fetch('/start_retraining', {
491
+ method: 'POST',
492
+ headers: {
493
+ 'Content-Type': 'application/json'
494
+ }
495
+ });
496
+
497
+ if (!response.ok) {
498
+ throw new Error('Failed to start retraining');
499
+ }
500
+
501
+ // If POST was successful, open the monitoring page in a new window
502
+ window.open('/start_retraining', '_blank');
503
+
504
+ // Connect to Socket.IO to receive updates in the main window
505
+ const socket = io();
506
+
507
+ socket.on('training_update', (data) => {
508
+ this.showStatus(`Retraining progress: ${data.status}`, 'wait');
509
+ });
510
+
511
+ socket.on('training_complete', (data) => {
512
+ this.showStatus('Model retraining completed successfully!', 'success');
513
+ this.buttons.retrain.disabled = false;
514
+ socket.disconnect();
515
+ });
516
+
517
+ socket.on('training_error', (data) => {
518
+ this.showStatus(data.status, 'error');
519
+ this.buttons.retrain.disabled = false;
520
+ socket.disconnect();
521
+ });
522
+
523
+ socket.on('connect_error', (error) => {
524
+ this.showStatus('Connection error: ' + error, 'error');
525
+ this.buttons.retrain.disabled = false;
526
+ socket.disconnect();
527
+ });
528
+
529
+ } catch (error) {
530
+ this.showStatus(`Failed to start retraining: ${error.message}`, 'error');
531
+ this.buttons.retrain.disabled = false;
532
+ }
533
+ }
534
+
535
+ showStatus(message, type) {
536
+ this.status.className = `status ${type}`;
537
+ this.status.textContent = message;
538
+ this.status.style.display = 'block';
539
+ if (type === 'success' || type === 'error') {
540
+ setTimeout(() => {
541
+ this.status.style.display = 'none';
542
+ }, 6000);
543
+ }
544
+ }
545
+ }
546
+
547
+ // Initialize the tool when the page loads
548
+ window.addEventListener('load', () => {
549
+ new SegmentationTool();
550
+ });
551
+
552
+ </script>
553
+ </body>
554
+ </html>
templates/retrain.html ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Model Retraining</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ margin: 0;
11
+ padding: 0;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ height: 100vh;
16
+ background-color: #f4f4f9;
17
+ }
18
+ .container {
19
+ text-align: center;
20
+ }
21
+ .status {
22
+ padding: 10px;
23
+ margin-top: 20px;
24
+ border-radius: 5px;
25
+ display: none;
26
+ position: absolute;
27
+ bottom: 20px;
28
+ width: 80%;
29
+ left: 10%;
30
+ }
31
+ .status.success {
32
+ background-color: #4CAF50;
33
+ color: white;
34
+ }
35
+ .status.error {
36
+ background-color: #f44336;
37
+ color: white;
38
+ }
39
+ .status.info {
40
+ background-color: #2196F3;
41
+ color: white;
42
+ }
43
+ .progress {
44
+ width: 100%;
45
+ background-color: #ddd;
46
+ height: 25px;
47
+ margin-top: 20px;
48
+ border-radius: 5px;
49
+ }
50
+ .progress-bar {
51
+ height: 100%;
52
+ width: 0;
53
+ background-color: #4CAF50;
54
+ text-align: center;
55
+ color: white;
56
+ line-height: 25px;
57
+ border-radius: 5px;
58
+ }
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <div class="container">
63
+ <h1>Model Retraining Progress</h1>
64
+ <div class="status" id="statusMessage"></div>
65
+ <div class="progress">
66
+ <div class="progress-bar" id="progressBar">0%</div>
67
+ </div>
68
+ </div>
69
+
70
+ <script src="https://cdn.socket.io/4.6.0/socket.io.min.js"></script>
71
+ <script>
72
+ // Connect to the WebSocket server
73
+ var socket = io.connect('http://' + document.domain + ':' + location.port + '/start_retraining');
74
+
75
+ // Get elements for updates
76
+ var statusMessage = document.getElementById('statusMessage');
77
+ var progressBar = document.getElementById('progressBar');
78
+
79
+ // Handle 'training_update' event
80
+ socket.on('training_update', function(data) {
81
+ var epoch = data.epoch;
82
+ var status = data.status;
83
+ statusMessage.className = "status info"; // Add info class to style
84
+ statusMessage.textContent = status;
85
+ statusMessage.style.display = "block";
86
+
87
+ // Update progress bar
88
+ var progress = (epoch / 5) * 100; // Assuming 5 epochs for this test
89
+ progressBar.style.width = progress + '%';
90
+ progressBar.textContent = Math.round(progress) + '%';
91
+ });
92
+
93
+ // Handle 'training_complete' event
94
+ socket.on('training_complete', function(data) {
95
+ statusMessage.className = "status success"; // Add success class
96
+ statusMessage.textContent = data.status;
97
+ setTimeout(function() {
98
+ statusMessage.style.display = "none"; // Hide status after a few seconds
99
+ }, 3000);
100
+ });
101
+ </script>
102
+ </body>
103
+ </html> -->
104
+
105
+
106
+ <!DOCTYPE html>
107
+ <html lang="en">
108
+ <head>
109
+ <meta charset="UTF-8">
110
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
111
+ <title>Model Retraining</title>
112
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
113
+ <style>
114
+ .container {
115
+ max-width: 800px;
116
+ margin: 40px auto;
117
+ padding: 20px;
118
+ text-align: center;
119
+ }
120
+
121
+ .status-box {
122
+ padding: 20px;
123
+ margin: 20px 0;
124
+ border-radius: 8px;
125
+ background-color: #f5f5f5;
126
+ }
127
+
128
+ .progress-bar {
129
+ width: 100%;
130
+ height: 20px;
131
+ background-color: #f0f0f0;
132
+ border-radius: 10px;
133
+ overflow: hidden;
134
+ margin: 20px 0;
135
+ }
136
+
137
+ .progress-fill {
138
+ height: 100%;
139
+ background-color: #4CAF50;
140
+ width: 0%;
141
+ transition: width 0.3s ease-in-out;
142
+ }
143
+
144
+ .loader {
145
+ border: 4px solid #f3f3f3;
146
+ border-top: 4px solid #3498db;
147
+ border-radius: 50%;
148
+ width: 40px;
149
+ height: 40px;
150
+ animation: spin 1s linear infinite;
151
+ margin: 20px auto;
152
+ }
153
+
154
+ @keyframes spin {
155
+ 0% { transform: rotate(0deg); }
156
+ 100% { transform: rotate(360deg); }
157
+ }
158
+
159
+ .back-button {
160
+ padding: 10px 20px;
161
+ background-color: #4CAF50;
162
+ color: white;
163
+ border: none;
164
+ border-radius: 4px;
165
+ cursor: pointer;
166
+ text-decoration: none;
167
+ display: inline-block;
168
+ margin-top: 20px;
169
+ }
170
+
171
+ .back-button:hover {
172
+ background-color: #45a049;
173
+ }
174
+
175
+ #status-text {
176
+ font-size: 1.1em;
177
+ margin: 10px 0;
178
+ }
179
+ </style>
180
+ </head>
181
+ <body>
182
+ <div class="container">
183
+ <h1>Model Retraining Status</h1>
184
+ <div class="status-box">
185
+ <div class="loader" id="loader"></div>
186
+ <div class="progress-bar">
187
+ <div class="progress-fill" id="progress"></div>
188
+ </div>
189
+ <p id="status-text">Initializing retraining process...</p>
190
+ <p>You can safely navigate away from this page. The retraining will continue in the background.</p>
191
+ </div>
192
+ <a href="/" class="back-button">Back to Image Segmentation Tool</a>
193
+ </div>
194
+
195
+ <script>
196
+ // Connect to Socket.IO server
197
+ const socket = io();
198
+ const progressBar = document.getElementById('progress');
199
+ const statusText = document.getElementById('status-text');
200
+ const loader = document.getElementById('loader');
201
+
202
+ // Handle training updates
203
+ socket.on('training_update', function(data) {
204
+ // Calculate progress percentage (5 epochs total)
205
+ const progress = (data.epoch / 5) * 100;
206
+ progressBar.style.width = `${progress}%`;
207
+ statusText.textContent = data.status;
208
+ });
209
+
210
+ // Handle training completion
211
+ socket.on('training_complete', function(data) {
212
+ progressBar.style.width = '100%';
213
+ statusText.textContent = data.status;
214
+ loader.style.display = 'none';
215
+
216
+ // Optional: Show completion message
217
+ setTimeout(() => {
218
+ alert('Retraining completed successfully!');
219
+ }, 500);
220
+ });
221
+
222
+ // Handle connection errors
223
+ socket.on('connect_error', function(error) {
224
+ statusText.textContent = 'Connection error: ' + error;
225
+ loader.style.display = 'none';
226
+ });
227
+
228
+ // Start the retraining process when the page loads
229
+ window.addEventListener('load', function() {
230
+ fetch('/start_retraining', {
231
+ method: 'POST',
232
+ headers: {
233
+ 'Content-Type': 'application/json'
234
+ }
235
+ }).catch(error => {
236
+ statusText.textContent = 'Failed to start retraining: ' + error;
237
+ loader.style.display = 'none';
238
+ });
239
+ });
240
+ </script>
241
+ </body>
242
+ </html>
templates/yolo.html ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Image Segmentation Tool</title>
7
+ <style>
8
+ .container {
9
+ max-width: 1200px;
10
+ margin: 0 auto;
11
+ padding: 20px;
12
+ }
13
+ /* Additional styles for the area table */
14
+ .area-table {
15
+ width: 100%;
16
+ border-collapse: collapse;
17
+ margin-top: 20px;
18
+ }
19
+ .area-table th, .area-table td {
20
+ border: 1px solid #ddd;
21
+ padding: 8px;
22
+ text-align: center;
23
+ }
24
+ .area-table th {
25
+ background-color: #f2f2f2;
26
+ font-weight: bold;
27
+ }
28
+ .controls {
29
+ margin-bottom: 20px;
30
+ position: sticky;
31
+ top: 0;
32
+ background: white;
33
+ z-index: 100;
34
+ padding: 10px 0;
35
+ }
36
+
37
+ .button {
38
+ padding: 8px 16px;
39
+ margin-right: 10px;
40
+ background-color: #4CAF50;
41
+ color: white;
42
+ border: none;
43
+ border-radius: 4px;
44
+ cursor: pointer;
45
+ }
46
+
47
+ .button.active {
48
+ background-color: #45a049;
49
+ }
50
+
51
+ .button:disabled {
52
+ background-color: #cccccc;
53
+ cursor: not-allowed;
54
+ }
55
+
56
+ .image-container {
57
+ display: grid;
58
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
59
+ gap: 20px;
60
+ }
61
+
62
+ .image-wrapper {
63
+ position: relative;
64
+ border: 1px solid #ddd;
65
+ padding: 10px;
66
+ }
67
+
68
+ .canvas-wrapper {
69
+ position: relative;
70
+ overflow: hidden;
71
+ width: 100%;
72
+ height: 100%;
73
+ }
74
+
75
+ canvas {
76
+ position: absolute;
77
+ top: 0;
78
+ left: 0;
79
+ pointer-events: none;
80
+ width: 100%;
81
+ height: 100%;
82
+ }
83
+
84
+ img {
85
+ max-width: 100%;
86
+ height: auto;
87
+ display: block;
88
+ }
89
+
90
+ .status {
91
+ margin-top: 10px;
92
+ padding: 10px;
93
+ border-radius: 4px;
94
+ display: none;
95
+ position: fixed;
96
+ bottom: 20px;
97
+ right: 20px;
98
+ z-index: 1000;
99
+ }
100
+
101
+ .status.error {
102
+ background-color: #ffebee;
103
+ color: #c62828;
104
+ display: block;
105
+ }
106
+
107
+ .status.success {
108
+ background-color: #e8f5e9;
109
+ color: #2e7d32;
110
+ display: block;
111
+ }
112
+ .status.wait {
113
+ background-color: #fff3e0;
114
+ color: #f57c00;
115
+ display: block;
116
+ }
117
+ .top-right-buttons {
118
+ position: absolute;
119
+ top: 20px;
120
+ right: 20px;
121
+ display: flex;
122
+ gap: 10px;
123
+ }
124
+ </style>
125
+ </head>
126
+ <body>
127
+ <div class="container">
128
+ <h1>Voids and Components Classification</h1>
129
+
130
+ <div class="controls">
131
+ <input type="file" id="fileInput" accept="image/*">
132
+ <button id="classifyButton" class="button" disabled>Classify</button>
133
+ </div>
134
+
135
+ <div id="status" class="status"></div>
136
+
137
+ <div class="image-container">
138
+ <div class="image-wrapper">
139
+ <h2>Original Image</h2>
140
+ <div class="canvas-wrapper" id="canvasWrapper">
141
+ <img id="image" src="" alt="Upload an image" draggable="false">
142
+ <canvas id="overlayCanvas"></canvas>
143
+ </div>
144
+ </div>
145
+ <div class="image-wrapper">
146
+ <h2>Segmentation Prediction</h2>
147
+ <img id="classifiedImage" src="" alt="Classified image will appear here">
148
+ </div>
149
+ </div>
150
+
151
+ <table class="area-table" id="areaTable">
152
+ <caption><strong>Segmentation Area Table</strong></caption>
153
+ <thead>
154
+ <tr>
155
+ <th>Image</th>
156
+ <th>Component</th>
157
+ <th>Area</th>
158
+ <th>Void Area %</th>
159
+ <th>Max Void Area %</th>
160
+ </tr>
161
+ </thead>
162
+ <tbody>
163
+ <!-- Rows will be dynamically added here using JavaScript -->
164
+ </tbody>
165
+ </table>
166
+ <button id="exportButton" class="button" disabled>Export to CSV</button>
167
+ </div>
168
+
169
+ <div class="top-right-buttons">
170
+ <a href="{{ url_for('index') }}" class="button">Go to SAM</a>
171
+ <button class="button" disabled>Go to Yolo</button>
172
+ </div>
173
+
174
+ <script>
175
+ class ClassificationTool {
176
+ constructor() {
177
+ this.initializeElements();
178
+ this.initializeState();
179
+ this.setupEventListeners();
180
+ }
181
+
182
+ initializeElements() {
183
+ this.fileInput = document.getElementById('fileInput');
184
+ this.image = document.getElementById('image');
185
+ this.overlayCanvas = document.getElementById('overlayCanvas');
186
+ this.overlayCtx = this.overlayCanvas.getContext('2d');
187
+ this.classifiedImage = document.getElementById('classifiedImage');
188
+ this.status = document.getElementById('status');
189
+ this.canvasWrapper = document.getElementById('canvasWrapper');
190
+
191
+ this.buttons = {
192
+ classify: document.getElementById('classifyButton'),
193
+ export: document.getElementById('exportButton')
194
+ };
195
+ }
196
+
197
+ initializeState() {
198
+ this.currentMode = null;
199
+ this.uploadedFilename = '';
200
+ }
201
+
202
+ setupEventListeners() {
203
+ this.fileInput.addEventListener('change', (e) => this.handleFileUpload(e));
204
+ this.image.addEventListener('load', () => this.handleImageLoad());
205
+ this.buttons.classify.addEventListener('click', () => this.classify());
206
+ this.buttons.export.addEventListener('click', () => this.exportTableToCSV());
207
+
208
+ }
209
+
210
+ setMode(mode) {
211
+ this.currentMode = this.currentMode === mode ? null : mode;
212
+ Object.values(this.buttons).forEach(button => button.classList.remove('active'));
213
+ if (this.currentMode) {
214
+ this.buttons[this.currentMode].classList.add('active');
215
+ }
216
+ this.canvasWrapper.style.cursor = this.currentMode ? 'crosshair' : 'default';
217
+ }
218
+
219
+
220
+ updateAreaTable(areaData) {
221
+ const areaTableBody = document.getElementById('areaTable').getElementsByTagName('tbody')[0];
222
+ areaTableBody.innerHTML = ''; // Clear existing rows
223
+
224
+ areaData.forEach(item => {
225
+ const row = areaTableBody.insertRow();
226
+
227
+ const cellImage = row.insertCell(0);
228
+ const cellComponent = row.insertCell(1);
229
+ const cellArea = row.insertCell(2);
230
+ const cellVoidArea = row.insertCell(3);
231
+ const cellMaxVoidArea = row.insertCell(4);
232
+
233
+ cellImage.textContent = item['Image'];
234
+ cellComponent.textContent = item.Component;
235
+ cellArea.textContent = item['Area'];
236
+ cellVoidArea.textContent = item['Void Area %'].toFixed(2) + '%'; // Format as percentage
237
+ cellMaxVoidArea.textContent = item['Max Void Area %'].toFixed(2) + '%'; // Format as percentage
238
+ });
239
+ }
240
+ async exportTableToCSV() {
241
+ const table = document.getElementById('areaTable');
242
+ let csvContent = '';
243
+ const rows = table.querySelectorAll('tr');
244
+
245
+ rows.forEach(row => {
246
+ const cols = row.querySelectorAll('th, td');
247
+ const rowData = Array.from(cols).map(col => col.textContent).join(',');
248
+ csvContent += rowData + '\n';
249
+ });
250
+
251
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
252
+ const url = URL.createObjectURL(blob);
253
+ const link = document.createElement('a');
254
+ link.href = url;
255
+ link.setAttribute('download', 'report.csv');
256
+ document.body.appendChild(link);
257
+ link.click();
258
+ document.body.removeChild(link);
259
+ }
260
+ async handleFileUpload(event) {
261
+ const file = event.target.files[0];
262
+ if (!file) return;
263
+
264
+
265
+ // Show "please wait" message
266
+ this.showStatus('Uploading the image, please wait...', 'wait');
267
+
268
+ try {
269
+ const formData = new FormData();
270
+ formData.append('file', file);
271
+
272
+ const response = await fetch('/upload_yolo', {
273
+ method: 'POST',
274
+ body: formData
275
+ });
276
+
277
+ const result = await response.json();
278
+ if (result.error) throw new Error(result.error);
279
+
280
+ this.image.src = result.image_url;
281
+ this.uploadedFilename = result.filename;
282
+ this.originalDimensions = result.dimensions;
283
+ this.buttons.classify.disabled = false;
284
+
285
+ // Show success message after upload is complete
286
+ this.showStatus('Image uploaded successfully', 'success');
287
+ } catch (error) {
288
+ this.showStatus(`Upload failed: ${error.message}`, 'error');
289
+ }
290
+ }
291
+
292
+ async classify() {
293
+ if (!this.uploadedFilename) {
294
+ this.showStatus('Please upload an image first', 'error');
295
+ return;
296
+ }
297
+
298
+ try {
299
+ this.buttons.classify.disabled = true;
300
+
301
+ const requestData = {
302
+ filename: this.uploadedFilename
303
+ };
304
+
305
+ console.log('Sending data to backend:', requestData); // Debug logging
306
+
307
+ const response = await fetch('/classify', {
308
+ method: 'POST',
309
+ headers: {
310
+ 'Content-Type': 'application/json',
311
+ },
312
+ body: JSON.stringify(requestData),
313
+ });
314
+
315
+ const result = await response.json();
316
+ if (result.error) throw new Error(result.error);
317
+
318
+ this.classifiedImage.src = result.result_path + '?t=' + new Date().getTime();
319
+ this.showStatus('Classification completed successfully', 'success');
320
+
321
+ // Check if area_data is defined and is an array before updating the table
322
+ if (Array.isArray(result.area_data)) {
323
+ this.updateAreaTable(result.area_data);
324
+ this.buttons.export.disabled = false;
325
+ } else {
326
+ throw new Error('Area data is not available or is not an array.');
327
+ }
328
+
329
+ } catch (error) {
330
+ this.showStatus(`Failed to classify: ${error.message}`, 'error');
331
+ console.error('Classification error:', error); // Debug logging
332
+ } finally {
333
+ this.buttons.classify.disabled = false;
334
+ }
335
+ }
336
+ showStatus(message, type) {
337
+ this.status.className = `status ${type}`;
338
+ this.status.textContent = message;
339
+ this.status.style.display = 'block';
340
+ if (type === 'success' || type === 'error') {
341
+ setTimeout(() => {
342
+ this.status.style.display = 'none';
343
+ }, 3000);
344
+ }
345
+ }
346
+ }
347
+
348
+ // Initialize the tool when the page loads
349
+ window.addEventListener('load', () => {
350
+ new ClassificationTool();
351
+ });
352
+
353
+ </script>
354
+ </body>
355
+ </html>