OussamaElfila21 commited on
Commit
bf373ba
·
verified ·
1 Parent(s): c561c85

Update try_page.html

Browse files
Files changed (1) hide show
  1. try_page.html +149 -146
try_page.html CHANGED
@@ -300,10 +300,10 @@
300
  const detectionCanvas = document.getElementById('detectionCanvas');
301
  const ctx = detectionCanvas.getContext('2d');
302
  const debugOutput = document.getElementById('debugOutput');
303
-
304
  // Enable debug mode (set to false in production)
305
  const DEBUG = true;
306
-
307
  // API endpoint URL
308
  const API_URL = '/api/predict';
309
 
@@ -313,73 +313,73 @@
313
  let processingWidth = 0;
314
  let processingHeight = 0;
315
  let responseData = null;
316
-
317
  // Tab switching functionality
318
  tabButtons.forEach(button => {
319
  button.addEventListener('click', () => {
320
  const tabName = button.getAttribute('data-tab');
321
-
322
  // Update button states
323
  tabButtons.forEach(btn => btn.classList.remove('active'));
324
  button.classList.add('active');
325
-
326
  // Update tab content visibility
327
  tabContents.forEach(content => content.classList.remove('active'));
328
  document.getElementById(tabName + 'Tab').classList.add('active');
329
-
330
  // If switching to visual tab and we have data, ensure visualization is rendered
331
  if (tabName === 'visual' && responseData && originalImage) {
332
  visualizeResults(originalImage, responseData);
333
  }
334
  });
335
  });
336
-
337
  // Handle file input change
338
  fileInput.addEventListener('change', (event) => {
339
  const file = event.target.files[0];
340
-
341
  // Clear previous selections
342
  imageFile = null;
343
  imagePreview.style.display = 'none';
344
  sendButton.disabled = true;
345
  originalImage = null;
346
  responseData = null;
347
-
348
  // Validate file
349
  if (!file) return;
350
-
351
  if (file.size > 2 * 1024 * 1024) {
352
  showMessage('File size exceeds 2MB limit.', 'error');
353
  return;
354
  }
355
-
356
  if (!['image/png', 'image/jpeg'].includes(file.type)) {
357
  showMessage('Only PNG and JPEG formats are supported.', 'error');
358
  return;
359
  }
360
-
361
  // Store file for upload
362
  imageFile = file;
363
-
364
  // Show image preview
365
  const reader = new FileReader();
366
  reader.onload = (e) => {
367
  const image = new Image();
368
  image.src = e.target.result;
369
-
370
  image.onload = () => {
371
  // Store original image for visualization
372
  originalImage = image;
373
-
374
  // Set preview
375
  imagePreview.src = e.target.result;
376
  imagePreview.style.display = 'block';
377
-
378
  // Update image info
379
  imageSizeInfo.textContent = `Original size: ${image.width}x${image.height} pixels`;
380
-
381
- // Calculate processing dimensions (for visualization)
382
- calculateProcessingDimensions(image.width, image.height);
383
 
384
  // Enable send button
385
  sendButton.disabled = false;
@@ -388,12 +388,16 @@
388
  };
389
  reader.readAsDataURL(file);
390
  });
391
-
392
- // Calculate dimensions for processing visualization
393
- function calculateProcessingDimensions(width, height) {
 
394
  const maxWidth = 640;
395
  const maxHeight = 320;
396
-
 
 
 
397
  // Calculate dimensions
398
  if (width > height) {
399
  if (width > maxWidth) {
@@ -406,56 +410,65 @@
406
  height = maxHeight;
407
  }
408
  }
409
-
410
  // Store processing dimensions for visualization
411
  processingWidth = width;
412
  processingHeight = height;
413
- }
414
-
 
 
 
 
 
 
 
 
 
415
  // Handle send button click
416
  sendButton.addEventListener('click', async () => {
417
  if (!imageFile) {
418
  showMessage('No image selected.', 'error');
419
  return;
420
  }
421
-
422
  // Clear previous response
423
  responseOutput.textContent = "// Processing...";
424
  clearCanvas();
425
  responseData = null;
426
  debugOutput.style.display = 'none';
427
-
428
  // Show loading state
429
  loading.style.display = 'block';
430
  message.style.display = 'none';
431
-
432
  // Reset processing time
433
  processingTimeInfo.textContent = '';
434
-
435
  // Record start time
436
  startTime = performance.now();
437
-
438
  // Create form data for HTTP request
439
  const formData = new FormData();
440
  formData.append('file', imageFile);
441
-
442
  try {
443
  // Send HTTP request
444
  const response = await fetch(API_URL, {
445
  method: 'POST',
446
  body: formData
447
  });
448
-
449
  // Handle response
450
  if (!response.ok) {
451
  const errorText = await response.text();
452
  throw new Error(`HTTP error ${response.status}: ${errorText}`);
453
  }
454
-
455
  // Parse JSON response
456
  const data = await response.json();
457
  responseData = data;
458
-
459
  // Calculate processing time
460
  const endTime = performance.now();
461
  const timeTaken = endTime - startTime;
@@ -463,12 +476,12 @@
463
  // Format and display raw response
464
  responseOutput.textContent = JSON.stringify(data, null, 2);
465
  processingTimeInfo.textContent = `Processing time: ${timeTaken.toFixed(2)} ms`;
466
-
467
  // Visualize the results
468
  if (originalImage) {
469
  visualizeResults(originalImage, data);
470
  }
471
-
472
  // Show success message
473
  showMessage('Image processed successfully!', 'success');
474
  } catch (error) {
@@ -484,52 +497,42 @@
484
  loading.style.display = 'none';
485
  }
486
  });
487
-
488
  // Visualize detection results
489
- // Visualize detection results
490
  function visualizeResults(image, data) {
491
  try {
492
  // Set canvas dimensions
493
  detectionCanvas.width = processingWidth;
494
  detectionCanvas.height = processingHeight;
495
 
496
- // Draw the original image
497
  ctx.drawImage(image, 0, 0, processingWidth, processingHeight);
498
 
499
  // Set styles for bounding boxes
500
  ctx.lineWidth = 3;
501
  ctx.font = 'bold 14px Arial';
502
 
503
- // Find detections (checking all common formats)
504
  let detections = [];
505
- let detectionSource = '';
506
-
507
  if (data.detections && Array.isArray(data.detections)) {
508
  detections = data.detections;
509
- detectionSource = 'detections';
510
  } else if (data.predictions && Array.isArray(data.predictions)) {
511
  detections = data.predictions;
512
- detectionSource = 'predictions';
513
  } else if (data.objects && Array.isArray(data.objects)) {
514
  detections = data.objects;
515
- detectionSource = 'objects';
516
  } else if (data.results && Array.isArray(data.results)) {
517
  detections = data.results;
518
- detectionSource = 'results';
519
  } else {
520
  // Try to look one level deeper if no detections found
521
  for (const key in data) {
522
  if (typeof data[key] === 'object' && data[key] !== null) {
523
  if (Array.isArray(data[key])) {
524
  detections = data[key];
525
- detectionSource = key;
526
  break;
527
  } else {
528
- // Look one more level down
529
  for (const subKey in data[key]) {
530
  if (Array.isArray(data[key][subKey])) {
531
  detections = data[key][subKey];
532
- detectionSource = `${key}.${subKey}`;
533
  break;
534
  }
535
  }
@@ -538,19 +541,21 @@
538
  }
539
  }
540
 
 
 
 
 
541
  // Process each detection
542
  detections.forEach((detection, index) => {
543
- // Try to extract bounding box information
544
  let bbox = null;
545
  let label = null;
546
  let confidence = null;
547
  let distance = null;
548
 
549
- // Extract label/class
550
  if (detection.class !== undefined) {
551
  label = detection.class;
552
  } else {
553
- // Fallback to other common property names
554
  for (const key of ['label', 'name', 'category', 'className']) {
555
  if (detection[key] !== undefined) {
556
  label = detection[key];
@@ -558,11 +563,9 @@
558
  }
559
  }
560
  }
561
-
562
- // Default label if none found
563
  if (!label) label = `Object ${index + 1}`;
564
 
565
- // Extract confidence score if available
566
  for (const key of ['confidence', 'score', 'probability', 'conf']) {
567
  if (detection[key] !== undefined) {
568
  confidence = detection[key];
@@ -570,11 +573,10 @@
570
  }
571
  }
572
 
573
- // Extract distance - specifically look for distance_estimated first
574
  if (detection.distance_estimated !== undefined) {
575
  distance = detection.distance_estimated;
576
  } else {
577
- // Fallback to other common distance properties
578
  for (const key of ['distance', 'depth', 'z', 'dist', 'range']) {
579
  if (detection[key] !== undefined) {
580
  distance = detection[key];
@@ -583,7 +585,7 @@
583
  }
584
  }
585
 
586
- // Look for bounding box in features
587
  if (detection.features &&
588
  detection.features.xmin !== undefined &&
589
  detection.features.ymin !== undefined &&
@@ -597,11 +599,9 @@
597
  ymax: detection.features.ymax
598
  };
599
  } else {
600
- // Recursively search for bbox-like properties
601
- function findBBox(obj, path = '') {
602
  if (!obj || typeof obj !== 'object') return null;
603
-
604
- // Check if this object looks like a bbox
605
  if ((obj.x !== undefined && obj.y !== undefined &&
606
  (obj.width !== undefined || obj.w !== undefined ||
607
  obj.height !== undefined || obj.h !== undefined)) ||
@@ -609,142 +609,145 @@
609
  obj.xmax !== undefined && obj.ymax !== undefined)) {
610
  return obj;
611
  }
612
-
613
- // Check if it's an array of 4 numbers (potential bbox)
614
  if (Array.isArray(obj) && obj.length === 4 &&
615
  obj.every(item => typeof item === 'number')) {
616
  return obj;
617
  }
618
-
619
- // Check common bbox property names
620
  for (const key of ['bbox', 'box', 'bounding_box', 'boundingBox']) {
621
  if (obj[key] !== undefined) {
622
  return obj[key];
623
  }
624
  }
625
-
626
- // Search nested properties
627
  for (const key in obj) {
628
- const result = findBBox(obj[key], path ? `${path}.${key}` : key);
629
  if (result) return result;
630
  }
631
-
632
  return null;
633
  }
634
-
635
- // Find bbox using recursive search as fallback
636
  bbox = findBBox(detection);
637
  }
638
 
639
- // If we found a bounding box, draw it
640
  if (bbox) {
641
- // Parse different bbox formats
642
  let x, y, width, height;
643
-
644
  if (Array.isArray(bbox)) {
645
- // Try to determine array format
646
  if (bbox.length === 4) {
647
- if (bbox[0] >= 0 && bbox[1] >= 0 && bbox[2] <= 1 && bbox[3] <= 1) {
648
- // Likely normalized [x1, y1, x2, y2]
 
649
  x = bbox[0] * processingWidth;
650
  y = bbox[1] * processingHeight;
651
  width = (bbox[2] - bbox[0]) * processingWidth;
652
  height = (bbox[3] - bbox[1]) * processingHeight;
653
  } else if (bbox[2] > bbox[0] && bbox[3] > bbox[1]) {
654
- // Likely [x1, y1, x2, y2]
655
- x = bbox[0];
656
- y = bbox[1];
657
- width = bbox[2] - bbox[0];
658
- height = bbox[3] - bbox[1];
659
  } else {
660
- // Assume [x, y, width, height]
661
- x = bbox[0];
662
- y = bbox[1];
663
- width = bbox[2];
664
- height = bbox[3];
665
  }
666
  }
667
  } else {
668
- // Object format with named properties
669
  if (bbox.x !== undefined && bbox.y !== undefined) {
 
670
  x = bbox.x;
671
  y = bbox.y;
672
  width = bbox.width || bbox.w || 0;
673
  height = bbox.height || bbox.h || 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  } else if (bbox.xmin !== undefined && bbox.ymin !== undefined) {
675
  x = bbox.xmin;
676
  y = bbox.ymin;
677
  width = (bbox.xmax || 0) - bbox.xmin;
678
  height = (bbox.ymax || 0) - bbox.ymin;
 
 
 
 
 
 
 
 
 
 
 
679
  }
680
  }
681
 
682
- // Validate coordinates
683
- if (x === undefined || y === undefined || width === undefined || height === undefined) {
684
- return;
685
- }
686
-
687
- // Check if we need to scale normalized coordinates (0-1)
688
- if (x >= 0 && x <= 1 && y >= 0 && y <= 1 && width >= 0 && width <= 1 && height >= 0 && height <= 1) {
689
- x = x * processingWidth;
690
- y = y * processingHeight;
691
- width = width * processingWidth;
692
- height = height * processingHeight;
693
- }
694
-
695
- // Generate a color based on the class name
696
- const hue = stringToHue(label);
697
- ctx.strokeStyle = `hsl(${hue}, 100%, 40%)`;
698
- ctx.fillStyle = `hsla(${hue}, 100%, 40%, 0.3)`;
699
-
700
- // Draw bounding box
701
- ctx.beginPath();
702
- ctx.rect(x, y, width, height);
703
- ctx.stroke();
704
- ctx.fill();
705
-
706
- // Format confidence value
707
- let confidenceText = "";
708
- if (confidence !== null && confidence !== undefined) {
709
- // Convert to percentage if it's a probability (0-1)
710
- if (confidence <= 1) {
711
- confidence = (confidence * 100).toFixed(0);
712
- } else {
713
- confidence = confidence.toFixed(0);
714
  }
715
- confidenceText = ` ${confidence}%`;
716
- }
717
 
718
- // Format distance value
719
- let distanceText = "";
720
- if (distance !== null && distance !== undefined) {
721
- distanceText = ` : ${distance.toFixed(2)} m`;
722
- }
723
-
724
- // Create label text
725
- const labelText = `${label}${confidenceText}${distanceText}`;
726
 
727
- // Measure text width
728
- const textWidth = ctx.measureText(labelText).width + 10;
 
729
 
730
- // Draw label background
731
- ctx.fillStyle = `hsl(${hue}, 100%, 40%)`;
732
- ctx.fillRect(x, y - 20, textWidth, 20);
733
 
734
- // Draw label text
735
- ctx.fillStyle = "white";
736
- ctx.fillText(labelText, x + 5, y - 5);
 
737
  }
738
  });
739
-
740
  } catch (error) {
741
  console.error('Error visualizing results:', error);
 
742
  debugOutput.style.display = 'block';
743
- debugOutput.textContent += `VISUALIZATION ERROR: ${error.message}\n`;
744
- debugOutput.textContent += `Error stack: ${error.stack}\n`;
745
  }
746
  }
747
 
 
748
  // Generate consistent hue for string
749
  function stringToHue(str) {
750
  let hash = 0;
@@ -753,21 +756,21 @@
753
  }
754
  return hash % 360;
755
  }
756
-
757
  // Clear canvas
758
  function clearCanvas() {
759
  if (detectionCanvas.getContext) {
760
  ctx.clearRect(0, 0, detectionCanvas.width, detectionCanvas.height);
761
  }
762
  }
763
-
764
  // Show message function
765
  function showMessage(text, type) {
766
  message.textContent = text;
767
  message.className = '';
768
  message.classList.add(type);
769
  message.style.display = 'block';
770
-
771
  if (type === 'info') {
772
  setTimeout(() => {
773
  message.style.display = 'none';
 
300
  const detectionCanvas = document.getElementById('detectionCanvas');
301
  const ctx = detectionCanvas.getContext('2d');
302
  const debugOutput = document.getElementById('debugOutput');
303
+
304
  // Enable debug mode (set to false in production)
305
  const DEBUG = true;
306
+
307
  // API endpoint URL
308
  const API_URL = '/api/predict';
309
 
 
313
  let processingWidth = 0;
314
  let processingHeight = 0;
315
  let responseData = null;
316
+
317
  // Tab switching functionality
318
  tabButtons.forEach(button => {
319
  button.addEventListener('click', () => {
320
  const tabName = button.getAttribute('data-tab');
321
+
322
  // Update button states
323
  tabButtons.forEach(btn => btn.classList.remove('active'));
324
  button.classList.add('active');
325
+
326
  // Update tab content visibility
327
  tabContents.forEach(content => content.classList.remove('active'));
328
  document.getElementById(tabName + 'Tab').classList.add('active');
329
+
330
  // If switching to visual tab and we have data, ensure visualization is rendered
331
  if (tabName === 'visual' && responseData && originalImage) {
332
  visualizeResults(originalImage, responseData);
333
  }
334
  });
335
  });
336
+
337
  // Handle file input change
338
  fileInput.addEventListener('change', (event) => {
339
  const file = event.target.files[0];
340
+
341
  // Clear previous selections
342
  imageFile = null;
343
  imagePreview.style.display = 'none';
344
  sendButton.disabled = true;
345
  originalImage = null;
346
  responseData = null;
347
+
348
  // Validate file
349
  if (!file) return;
350
+
351
  if (file.size > 2 * 1024 * 1024) {
352
  showMessage('File size exceeds 2MB limit.', 'error');
353
  return;
354
  }
355
+
356
  if (!['image/png', 'image/jpeg'].includes(file.type)) {
357
  showMessage('Only PNG and JPEG formats are supported.', 'error');
358
  return;
359
  }
360
+
361
  // Store file for upload
362
  imageFile = file;
363
+
364
  // Show image preview
365
  const reader = new FileReader();
366
  reader.onload = (e) => {
367
  const image = new Image();
368
  image.src = e.target.result;
369
+
370
  image.onload = () => {
371
  // Store original image for visualization
372
  originalImage = image;
373
+
374
  // Set preview
375
  imagePreview.src = e.target.result;
376
  imagePreview.style.display = 'block';
377
+
378
  // Update image info
379
  imageSizeInfo.textContent = `Original size: ${image.width}x${image.height} pixels`;
380
+
381
+ // Resize image for processing
382
+ resizeImage(image, file.type);
383
 
384
  // Enable send button
385
  sendButton.disabled = false;
 
388
  };
389
  reader.readAsDataURL(file);
390
  });
391
+
392
+ // Resize image function
393
+ function resizeImage(image, fileType) {
394
+ const canvas = document.createElement('canvas');
395
  const maxWidth = 640;
396
  const maxHeight = 320;
397
+
398
+ let width = image.width;
399
+ let height = image.height;
400
+
401
  // Calculate dimensions
402
  if (width > height) {
403
  if (width > maxWidth) {
 
410
  height = maxHeight;
411
  }
412
  }
413
+
414
  // Store processing dimensions for visualization
415
  processingWidth = width;
416
  processingHeight = height;
417
+
418
+ // Set canvas dimensions and draw image
419
+ canvas.width = width;
420
+ canvas.height = height;
421
+ const ctx = canvas.getContext('2d');
422
+ ctx.drawImage(image, 0, 0, width, height);
423
+
424
+ // For API calls, we don't need to convert to binary
425
+ // but we keep this method to ensure dimensions are correctly calculated
426
+ }
427
+
428
  // Handle send button click
429
  sendButton.addEventListener('click', async () => {
430
  if (!imageFile) {
431
  showMessage('No image selected.', 'error');
432
  return;
433
  }
434
+
435
  // Clear previous response
436
  responseOutput.textContent = "// Processing...";
437
  clearCanvas();
438
  responseData = null;
439
  debugOutput.style.display = 'none';
440
+
441
  // Show loading state
442
  loading.style.display = 'block';
443
  message.style.display = 'none';
444
+
445
  // Reset processing time
446
  processingTimeInfo.textContent = '';
447
+
448
  // Record start time
449
  startTime = performance.now();
450
+
451
  // Create form data for HTTP request
452
  const formData = new FormData();
453
  formData.append('file', imageFile);
454
+
455
  try {
456
  // Send HTTP request
457
  const response = await fetch(API_URL, {
458
  method: 'POST',
459
  body: formData
460
  });
461
+
462
  // Handle response
463
  if (!response.ok) {
464
  const errorText = await response.text();
465
  throw new Error(`HTTP error ${response.status}: ${errorText}`);
466
  }
467
+
468
  // Parse JSON response
469
  const data = await response.json();
470
  responseData = data;
471
+
472
  // Calculate processing time
473
  const endTime = performance.now();
474
  const timeTaken = endTime - startTime;
 
476
  // Format and display raw response
477
  responseOutput.textContent = JSON.stringify(data, null, 2);
478
  processingTimeInfo.textContent = `Processing time: ${timeTaken.toFixed(2)} ms`;
479
+
480
  // Visualize the results
481
  if (originalImage) {
482
  visualizeResults(originalImage, data);
483
  }
484
+
485
  // Show success message
486
  showMessage('Image processed successfully!', 'success');
487
  } catch (error) {
 
497
  loading.style.display = 'none';
498
  }
499
  });
500
+
501
  // Visualize detection results
 
502
  function visualizeResults(image, data) {
503
  try {
504
  // Set canvas dimensions
505
  detectionCanvas.width = processingWidth;
506
  detectionCanvas.height = processingHeight;
507
 
508
+ // Draw the resized original image
509
  ctx.drawImage(image, 0, 0, processingWidth, processingHeight);
510
 
511
  // Set styles for bounding boxes
512
  ctx.lineWidth = 3;
513
  ctx.font = 'bold 14px Arial';
514
 
515
+ // Find detections from various possible keys
516
  let detections = [];
 
 
517
  if (data.detections && Array.isArray(data.detections)) {
518
  detections = data.detections;
 
519
  } else if (data.predictions && Array.isArray(data.predictions)) {
520
  detections = data.predictions;
 
521
  } else if (data.objects && Array.isArray(data.objects)) {
522
  detections = data.objects;
 
523
  } else if (data.results && Array.isArray(data.results)) {
524
  detections = data.results;
 
525
  } else {
526
  // Try to look one level deeper if no detections found
527
  for (const key in data) {
528
  if (typeof data[key] === 'object' && data[key] !== null) {
529
  if (Array.isArray(data[key])) {
530
  detections = data[key];
 
531
  break;
532
  } else {
 
533
  for (const subKey in data[key]) {
534
  if (Array.isArray(data[key][subKey])) {
535
  detections = data[key][subKey];
 
536
  break;
537
  }
538
  }
 
541
  }
542
  }
543
 
544
+ // Scaling factors based on original image vs resized processing dimensions
545
+ const scaleX = processingWidth / image.width;
546
+ const scaleY = processingHeight / image.height;
547
+
548
  // Process each detection
549
  detections.forEach((detection, index) => {
 
550
  let bbox = null;
551
  let label = null;
552
  let confidence = null;
553
  let distance = null;
554
 
555
+ // Extract label
556
  if (detection.class !== undefined) {
557
  label = detection.class;
558
  } else {
 
559
  for (const key of ['label', 'name', 'category', 'className']) {
560
  if (detection[key] !== undefined) {
561
  label = detection[key];
 
563
  }
564
  }
565
  }
 
 
566
  if (!label) label = `Object ${index + 1}`;
567
 
568
+ // Extract confidence score
569
  for (const key of ['confidence', 'score', 'probability', 'conf']) {
570
  if (detection[key] !== undefined) {
571
  confidence = detection[key];
 
573
  }
574
  }
575
 
576
+ // Extract distance (using 'distance_estimated' first)
577
  if (detection.distance_estimated !== undefined) {
578
  distance = detection.distance_estimated;
579
  } else {
 
580
  for (const key of ['distance', 'depth', 'z', 'dist', 'range']) {
581
  if (detection[key] !== undefined) {
582
  distance = detection[key];
 
585
  }
586
  }
587
 
588
+ // Attempt to get bounding box coordinates
589
  if (detection.features &&
590
  detection.features.xmin !== undefined &&
591
  detection.features.ymin !== undefined &&
 
599
  ymax: detection.features.ymax
600
  };
601
  } else {
602
+ // Recursive search for bbox-like properties
603
+ function findBBox(obj) {
604
  if (!obj || typeof obj !== 'object') return null;
 
 
605
  if ((obj.x !== undefined && obj.y !== undefined &&
606
  (obj.width !== undefined || obj.w !== undefined ||
607
  obj.height !== undefined || obj.h !== undefined)) ||
 
609
  obj.xmax !== undefined && obj.ymax !== undefined)) {
610
  return obj;
611
  }
 
 
612
  if (Array.isArray(obj) && obj.length === 4 &&
613
  obj.every(item => typeof item === 'number')) {
614
  return obj;
615
  }
 
 
616
  for (const key of ['bbox', 'box', 'bounding_box', 'boundingBox']) {
617
  if (obj[key] !== undefined) {
618
  return obj[key];
619
  }
620
  }
 
 
621
  for (const key in obj) {
622
+ const result = findBBox(obj[key]);
623
  if (result) return result;
624
  }
 
625
  return null;
626
  }
 
 
627
  bbox = findBBox(detection);
628
  }
629
 
630
+ // If bounding box found, process and draw it
631
  if (bbox) {
 
632
  let x, y, width, height;
 
633
  if (Array.isArray(bbox)) {
 
634
  if (bbox.length === 4) {
635
+ // Check if values are normalized (all between 0 and 1)
636
+ const isNormalized = bbox.every(val => val >= 0 && val <= 1);
637
+ if (isNormalized) {
638
  x = bbox[0] * processingWidth;
639
  y = bbox[1] * processingHeight;
640
  width = (bbox[2] - bbox[0]) * processingWidth;
641
  height = (bbox[3] - bbox[1]) * processingHeight;
642
  } else if (bbox[2] > bbox[0] && bbox[3] > bbox[1]) {
643
+ // Absolute coordinates
644
+ x = bbox[0] * scaleX;
645
+ y = bbox[1] * scaleY;
646
+ width = (bbox[2] - bbox[0]) * scaleX;
647
+ height = (bbox[3] - bbox[1]) * scaleY;
648
  } else {
649
+ // Format assumed to be [x, y, width, height]
650
+ x = bbox[0] * scaleX;
651
+ y = bbox[1] * scaleY;
652
+ width = bbox[2] * scaleX;
653
+ height = bbox[3] * scaleY;
654
  }
655
  }
656
  } else {
657
+ // Object format handling
658
  if (bbox.x !== undefined && bbox.y !== undefined) {
659
+ // x,y,width,height format
660
  x = bbox.x;
661
  y = bbox.y;
662
  width = bbox.width || bbox.w || 0;
663
  height = bbox.height || bbox.h || 0;
664
+ // Check if normalized
665
+ if (x <= 1 && y <= 1 && width <= 1 && height <= 1) {
666
+ x *= processingWidth;
667
+ y *= processingHeight;
668
+ width *= processingWidth;
669
+ height *= processingHeight;
670
+ } else {
671
+ // Assume coordinates are based on the original image
672
+ x *= scaleX;
673
+ y *= scaleY;
674
+ width *= scaleX;
675
+ height *= scaleY;
676
+ }
677
  } else if (bbox.xmin !== undefined && bbox.ymin !== undefined) {
678
  x = bbox.xmin;
679
  y = bbox.ymin;
680
  width = (bbox.xmax || 0) - bbox.xmin;
681
  height = (bbox.ymax || 0) - bbox.ymin;
682
+ if (x <= 1 && y <= 1 && bbox.xmax <= 1 && bbox.ymax <= 1) {
683
+ x *= processingWidth;
684
+ y *= processingHeight;
685
+ width *= processingWidth;
686
+ height *= processingHeight;
687
+ } else {
688
+ x *= scaleX;
689
+ y *= scaleY;
690
+ width *= scaleX;
691
+ height *= scaleY;
692
+ }
693
  }
694
  }
695
 
696
+ // Draw the bounding box if coordinates are valid
697
+ if (x !== undefined && y !== undefined &&
698
+ width !== undefined && height !== undefined &&
699
+ width > 0 && height > 0) {
700
+
701
+ // Generate a color based on the label
702
+ const hue = stringToHue(label);
703
+ ctx.strokeStyle = `hsl(${hue}, 100%, 40%)`;
704
+ ctx.fillStyle = `hsla(${hue}, 100%, 40%, 0.3)`;
705
+
706
+ // Draw the bounding box rectangle
707
+ ctx.beginPath();
708
+ ctx.rect(x, y, width, height);
709
+ ctx.stroke();
710
+ ctx.fill();
711
+
712
+ // Format and display confidence value if available
713
+ let confidenceText = "";
714
+ if (confidence !== null && confidence !== undefined) {
715
+ if (confidence <= 1) {
716
+ confidenceText = ` ${(confidence * 100).toFixed(0)}%`;
717
+ } else {
718
+ confidenceText = ` ${confidence.toFixed(0)}%`;
719
+ }
 
 
 
 
 
 
 
 
720
  }
 
 
721
 
722
+ // Format distance if available
723
+ let distanceText = "";
724
+ if (distance !== null && distance !== undefined) {
725
+ distanceText = ` : ${distance.toFixed(2)} m`;
726
+ }
 
 
 
727
 
728
+ // Prepare the label text
729
+ const labelText = `${label}${confidenceText}${distanceText}`;
730
+ const textWidth = ctx.measureText(labelText).width + 10;
731
 
732
+ // Draw label background
733
+ ctx.fillStyle = `hsl(${hue}, 100%, 40%)`;
734
+ ctx.fillRect(x, y - 20, textWidth, 20);
735
 
736
+ // Draw label text
737
+ ctx.fillStyle = "white";
738
+ ctx.fillText(labelText, x + 5, y - 5);
739
+ }
740
  }
741
  });
 
742
  } catch (error) {
743
  console.error('Error visualizing results:', error);
744
+ // Optional: Display debug information if needed
745
  debugOutput.style.display = 'block';
746
+ debugOutput.textContent += `VISUALIZATION ERROR: ${error.message}\n${error.stack}\n`;
 
747
  }
748
  }
749
 
750
+
751
  // Generate consistent hue for string
752
  function stringToHue(str) {
753
  let hash = 0;
 
756
  }
757
  return hash % 360;
758
  }
759
+
760
  // Clear canvas
761
  function clearCanvas() {
762
  if (detectionCanvas.getContext) {
763
  ctx.clearRect(0, 0, detectionCanvas.width, detectionCanvas.height);
764
  }
765
  }
766
+
767
  // Show message function
768
  function showMessage(text, type) {
769
  message.textContent = text;
770
  message.className = '';
771
  message.classList.add(type);
772
  message.style.display = 'block';
773
+
774
  if (type === 'info') {
775
  setTimeout(() => {
776
  message.style.display = 'none';