mrdbourke commited on
Commit
55cae17
·
verified ·
1 Parent(s): 6dfc798

add dots on boxes

Browse files
Files changed (1) hide show
  1. index.html +94 -80
index.html CHANGED
@@ -1,5 +1,6 @@
1
  <!DOCTYPE html>
2
  <html>
 
3
  <head>
4
  <style>
5
  * {
@@ -12,14 +13,14 @@
12
  font-family: system-ui;
13
  background-color: #f5f5f5;
14
  min-height: 100vh;
15
- padding: 1rem; /* Reduced padding for a more compact layout */
16
  }
17
 
18
  .page-title {
19
  text-align: center;
20
  padding: 0.5rem 0;
21
  color: #333;
22
- margin-bottom: 1.5rem; /* Reduced margin for compactness */
23
  font-size: 1.8rem;
24
  }
25
 
@@ -31,13 +32,13 @@
31
  /* Top Section: Image and Controls Side by Side */
32
  .top-section {
33
  display: flex;
34
- gap: 20px; /* Slightly increased gap for better separation */
35
- margin-bottom: 30px; /* Increased margin to separate from the table */
36
  }
37
 
38
  /* Image Section */
39
  .image-section {
40
- flex: 0 0 640px; /* Fixed width for image container */
41
  display: flex;
42
  flex-direction: column;
43
  align-items: center;
@@ -105,15 +106,15 @@
105
  }
106
 
107
  .radio-group {
108
- margin-bottom: 15px; /* Reduced margin */
109
- padding: 10px; /* Reduced padding */
110
  background: #f9f9f9;
111
  border-radius: 4px;
112
  border: 1px solid #ddd;
113
  }
114
 
115
  .radio-option {
116
- margin-bottom: 8px; /* Reduced margin */
117
  }
118
 
119
  .radio-option label {
@@ -126,35 +127,35 @@
126
  }
127
 
128
  .format {
129
- margin-bottom: 15px; /* Reduced margin */
130
  }
131
 
132
  .format-title {
133
  font-weight: bold;
134
- margin-bottom: 5px; /* Reduced margin */
135
  color: #333;
136
  }
137
 
138
  .coords {
139
  font-family: monospace;
140
  background: #f9f9f9;
141
- padding: 8px; /* Reduced padding */
142
  border-radius: 4px;
143
  border: 1px solid #ddd;
144
- min-height: 20px; /* Reduced min-height */
145
  }
146
 
147
  .clear-button-container {
148
  display: flex;
149
  justify-content: center;
150
- padding-top: 10px; /* Reduced padding */
151
  }
152
 
153
  .clear-button {
154
  background-color: #f44336;
155
  color: white;
156
  border: none;
157
- padding: 8px 16px; /* Reduced padding */
158
  border-radius: 4px;
159
  cursor: pointer;
160
  font-size: 14px;
@@ -169,9 +170,9 @@
169
  position: absolute;
170
  background: rgba(0, 0, 0, 0.7);
171
  color: #fff;
172
- padding: 3px 5px; /* Reduced padding */
173
  border-radius: 3px;
174
- font-size: 11px; /* Reduced font size */
175
  pointer-events: none;
176
  white-space: nowrap;
177
  }
@@ -192,13 +193,13 @@
192
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
193
  border-radius: 8px;
194
  overflow: hidden;
195
- font-size: 0.9rem; /* Slightly smaller font for compactness */
196
  }
197
 
198
- .model-explanation th,
199
  .model-explanation td {
200
  border: 1px solid #ddd;
201
- padding: 8px; /* Further reduced padding */
202
  text-align: left;
203
  }
204
 
@@ -223,16 +224,12 @@
223
  margin: 0 auto;
224
  }
225
 
226
- .image-section {
227
- flex: none;
228
- }
229
-
230
  .controls-section {
231
  flex: none;
232
  }
233
  }
234
 
235
- /* Ensure the entire layout fits within typical laptop screen heights */
236
  @media (max-height: 900px) {
237
  body {
238
  overflow: auto;
@@ -240,22 +237,26 @@
240
  }
241
  </style>
242
  </head>
 
243
  <body>
244
  <h1 class="page-title">Bounding Box Coordinate Tool</h1>
245
-
246
  <div class="main-container">
247
  <!-- Top Section: Image and Controls -->
248
  <div class="top-section">
249
  <!-- Image Section -->
250
  <div class="image-section">
251
  <div class="image-container">
252
- <img id="image" src="demo-image.jpg" alt="Image created by Flux Pro with prompt 'an image of a living room with plenty of different furniture objects'" width="640">
 
 
253
  <canvas id="canvas"></canvas>
254
  <div id="labels-container" class="labels-container"></div>
255
  </div>
256
- <p>Try drawing a box on the image by left clicking and dragging.</p>
 
257
  </div>
258
-
259
  <!-- Controls Section -->
260
  <div class="controls-section">
261
  <div class="coordinates">
@@ -282,22 +283,25 @@
282
 
283
  <h2>Bounding Box Coordinates</h2>
284
  <div class="format">
285
- <div class="format-title">XYXY (x1, y1, x2, y2) -> top left: (x1, y1), bottom right: (x2, y2)</div>
 
286
  <div id="xyxy" class="coords">No box drawn</div>
287
  </div>
288
-
289
  <div class="format">
290
- <div class="format-title">XYWH (x, y, width, height) -> top left: (x, y), width and height of box</div>
 
291
  <div id="xywh" class="coords">No box drawn</div>
292
  </div>
293
-
294
  <div class="format">
295
  <div class="format-title">Normalized XYWH (pixel values in range of 0-1)</div>
296
  <div id="normalized" class="coords">No box drawn</div>
297
  </div>
298
-
299
  <div class="format">
300
- <div class="format-title">Center XYWH (cx, cy, w, h) -> (center x, center y, width, height)</div>
 
301
  <div id="center" class="coords">No box drawn</div>
302
  </div>
303
  </div>
@@ -323,27 +327,32 @@
323
  <tbody>
324
  <tr>
325
  <td><strong>YOLO</strong></td>
326
- <td>Real-time object detection system. Training format uses normalized coordinates for better generalization across different image sizes.</td>
 
327
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
328
  </tr>
329
  <tr>
330
  <td><strong>Faster R-CNN</strong></td>
331
- <td>Region proposal network based detection. Typically uses absolute coordinates during inference, but may use normalized coordinates during training.</td>
 
332
  <td><strong>XYXY</strong> (x1, y1, x2, y2) or normalized coordinates</td>
333
  </tr>
334
  <tr>
335
  <td><strong>SSD</strong></td>
336
- <td>Single Shot MultiBox Detector uses normalized coordinates for anchor boxes and predictions to handle multiple scales efficiently.</td>
 
337
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
338
  </tr>
339
  <tr>
340
  <td><strong>RetinaNet</strong></td>
341
- <td>Dense object detector with focal loss. Uses normalized coordinates for anchor boxes and predictions.</td>
 
342
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
343
  </tr>
344
  <tr>
345
  <td><strong>CornerNet</strong></td>
346
- <td>Anchor-free detector that predicts keypoint heatmaps. Uses normalized coordinates for better scale invariance.</td>
 
347
  <td><strong>Normalized Corners</strong> (x1, y1, x2, y2) in range [0,1]</td>
348
  </tr>
349
  </tbody>
@@ -357,7 +366,7 @@
357
  const canvas = document.getElementById('canvas');
358
  const ctx = canvas.getContext('2d');
359
  const labelsContainer = document.getElementById('labels-container');
360
-
361
  let isDrawing = false;
362
  let startX = 0;
363
  let startY = 0;
@@ -371,8 +380,9 @@
371
  canvas.style.height = image.height + 'px';
372
  labelsContainer.style.width = image.width + 'px';
373
  labelsContainer.style.height = image.height + 'px';
 
374
  }
375
-
376
  if (image.complete) {
377
  initCanvas();
378
  } else {
@@ -395,36 +405,40 @@
395
  label.textContent = text;
396
  label.style.left = `${x}px`;
397
  label.style.top = `${y}px`;
398
-
399
- const labelRect = label.getBoundingClientRect();
400
  const containerRect = labelsContainer.getBoundingClientRect();
401
-
402
  label.style.visibility = 'hidden';
403
  labelsContainer.appendChild(label);
404
  const tempRect = label.getBoundingClientRect();
405
  label.style.visibility = 'visible';
406
-
407
  if (x + tempRect.width > containerRect.width) {
408
  label.style.left = `${containerRect.width - tempRect.width - 5}px`;
409
  }
410
-
411
  if (y + tempRect.height > containerRect.height) {
412
  label.style.top = `${containerRect.height - tempRect.height - 5}px`;
413
  }
414
-
415
  labelsContainer.appendChild(label);
416
  labelElements.push(label);
417
  }
418
 
419
- function drawBox(x1, y1, x2, y2) {
420
- clearCanvas();
421
- ctx.strokeStyle = '#00ff00';
422
- ctx.lineWidth = 2;
423
- const width = x2 - x1;
424
- const height = y2 - y1;
425
- ctx.strokeRect(x1, y1, width, height);
426
- createLabels(x1, y1, x2, y2);
427
- lastCoords = { x1, y1, x2, y2 };
 
 
 
 
 
 
 
 
 
 
428
  }
429
 
430
  function createLabels(x1, y1, x2, y2) {
@@ -432,8 +446,7 @@
432
  const height = y2 - y1;
433
  const format = document.querySelector('input[name="format"]:checked').value;
434
  let labels = [];
435
-
436
- switch(format) {
437
  case 'xyxy':
438
  labels = [
439
  { text: `(${Math.round(x1)}, ${Math.round(y1)})`, x: x1, y: y1, position: 'top-left' },
@@ -465,12 +478,23 @@
465
  ];
466
  break;
467
  }
468
-
469
  labels.forEach(label => {
470
  createLabel(label.text, label.x, label.y, label.position);
471
  });
472
  }
473
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  function updateCoordinates(x1, y1, x2, y2) {
475
  if (x1 === null || y1 === null || x2 === null || y2 === null) {
476
  document.getElementById('xyxy').textContent = 'No box drawn';
@@ -479,29 +503,24 @@
479
  document.getElementById('center').textContent = 'No box drawn';
480
  return;
481
  }
482
-
483
  const width = x2 - x1;
484
  const height = y2 - y1;
485
-
486
- document.getElementById('xyxy').textContent =
487
  `[${Math.round(x1)}, ${Math.round(y1)}, ${Math.round(x2)}, ${Math.round(y2)}]`;
488
-
489
- document.getElementById('xywh').textContent =
490
  `[${Math.round(x1)}, ${Math.round(y1)}, ${Math.round(width)}, ${Math.round(height)}]`;
491
-
492
  const normalizedX = (x1 / canvas.width).toFixed(3);
493
  const normalizedY = (y1 / canvas.height).toFixed(3);
494
  const normalizedW = (width / canvas.width).toFixed(3);
495
  const normalizedH = (height / canvas.height).toFixed(3);
496
- document.getElementById('normalized').textContent =
497
  `[${normalizedX}, ${normalizedY}, ${normalizedW}, ${normalizedH}]`;
498
-
499
  const centerX = x1 + width / 2;
500
  const centerY = y1 + height / 2;
501
- document.getElementById('center').textContent =
502
  `[${Math.round(centerX)}, ${Math.round(centerY)}, ${Math.round(width)}, ${Math.round(height)}]`;
503
  }
504
-
505
  document.querySelectorAll('input[name="format"]').forEach(radio => {
506
  radio.addEventListener('change', () => {
507
  if (lastCoords) {
@@ -510,7 +529,7 @@
510
  }
511
  });
512
  });
513
-
514
  canvas.addEventListener('mousedown', (e) => {
515
  isDrawing = true;
516
  const rect = canvas.getBoundingClientRect();
@@ -519,36 +538,30 @@
519
  clearCanvas();
520
  updateCoordinates(null, null, null, null);
521
  });
522
-
523
  canvas.addEventListener('mousemove', (e) => {
524
  if (!isDrawing) return;
525
-
526
  const rect = canvas.getBoundingClientRect();
527
  const currentX = e.clientX - rect.left;
528
  const currentY = e.clientY - rect.top;
529
-
530
  drawBox(startX, startY, currentX, currentY);
531
  updateCoordinates(startX, startY, currentX, currentY);
532
  });
533
-
534
  canvas.addEventListener('mouseup', (e) => {
535
  if (!isDrawing) return;
536
-
537
  const rect = canvas.getBoundingClientRect();
538
  const endX = e.clientX - rect.left;
539
  const endY = e.clientY - rect.top;
540
-
541
  drawBox(startX, startY, endX, endY);
542
  updateCoordinates(startX, startY, endX, endY);
543
-
544
  isDrawing = false;
545
  });
546
-
547
  canvas.addEventListener('mouseleave', () => {
548
  isDrawing = false;
549
  });
550
 
551
- // Event listener for the clear button
552
  document.getElementById('clear-button').addEventListener('click', () => {
553
  clearCanvas();
554
  lastCoords = null;
@@ -556,4 +569,5 @@
556
  });
557
  </script>
558
  </body>
559
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html>
3
+
4
  <head>
5
  <style>
6
  * {
 
13
  font-family: system-ui;
14
  background-color: #f5f5f5;
15
  min-height: 100vh;
16
+ padding: 1rem;
17
  }
18
 
19
  .page-title {
20
  text-align: center;
21
  padding: 0.5rem 0;
22
  color: #333;
23
+ margin-bottom: 1.5rem;
24
  font-size: 1.8rem;
25
  }
26
 
 
32
  /* Top Section: Image and Controls Side by Side */
33
  .top-section {
34
  display: flex;
35
+ gap: 20px;
36
+ margin-bottom: 30px;
37
  }
38
 
39
  /* Image Section */
40
  .image-section {
41
+ flex: 0 0 640px;
42
  display: flex;
43
  flex-direction: column;
44
  align-items: center;
 
106
  }
107
 
108
  .radio-group {
109
+ margin-bottom: 15px;
110
+ padding: 10px;
111
  background: #f9f9f9;
112
  border-radius: 4px;
113
  border: 1px solid #ddd;
114
  }
115
 
116
  .radio-option {
117
+ margin-bottom: 8px;
118
  }
119
 
120
  .radio-option label {
 
127
  }
128
 
129
  .format {
130
+ margin-bottom: 15px;
131
  }
132
 
133
  .format-title {
134
  font-weight: bold;
135
+ margin-bottom: 5px;
136
  color: #333;
137
  }
138
 
139
  .coords {
140
  font-family: monospace;
141
  background: #f9f9f9;
142
+ padding: 8px;
143
  border-radius: 4px;
144
  border: 1px solid #ddd;
145
+ min-height: 20px;
146
  }
147
 
148
  .clear-button-container {
149
  display: flex;
150
  justify-content: center;
151
+ padding-top: 10px;
152
  }
153
 
154
  .clear-button {
155
  background-color: #f44336;
156
  color: white;
157
  border: none;
158
+ padding: 8px 16px;
159
  border-radius: 4px;
160
  cursor: pointer;
161
  font-size: 14px;
 
170
  position: absolute;
171
  background: rgba(0, 0, 0, 0.7);
172
  color: #fff;
173
+ padding: 3px 5px;
174
  border-radius: 3px;
175
+ font-size: 11px;
176
  pointer-events: none;
177
  white-space: nowrap;
178
  }
 
193
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
194
  border-radius: 8px;
195
  overflow: hidden;
196
+ font-size: 0.9rem;
197
  }
198
 
199
+ .model-explanation th,
200
  .model-explanation td {
201
  border: 1px solid #ddd;
202
+ padding: 8px;
203
  text-align: left;
204
  }
205
 
 
224
  margin: 0 auto;
225
  }
226
 
227
+ .image-section,
 
 
 
228
  .controls-section {
229
  flex: none;
230
  }
231
  }
232
 
 
233
  @media (max-height: 900px) {
234
  body {
235
  overflow: auto;
 
237
  }
238
  </style>
239
  </head>
240
+
241
  <body>
242
  <h1 class="page-title">Bounding Box Coordinate Tool</h1>
243
+
244
  <div class="main-container">
245
  <!-- Top Section: Image and Controls -->
246
  <div class="top-section">
247
  <!-- Image Section -->
248
  <div class="image-section">
249
  <div class="image-container">
250
+ <img id="image" src="demo-image.jpg"
251
+ alt="Image created by Flux Pro with prompt 'an image of a living room with plenty of different furniture objects'"
252
+ width="640">
253
  <canvas id="canvas"></canvas>
254
  <div id="labels-container" class="labels-container"></div>
255
  </div>
256
+ <p>Try drawing a box on the image by left clicking and dragging.</p>
257
+ <p id="image-dimensions"></p>
258
  </div>
259
+
260
  <!-- Controls Section -->
261
  <div class="controls-section">
262
  <div class="coordinates">
 
283
 
284
  <h2>Bounding Box Coordinates</h2>
285
  <div class="format">
286
+ <div class="format-title">XYXY (x1, y1, x2, y2) top left: (x1, y1), bottom right: (x2, y2)
287
+ </div>
288
  <div id="xyxy" class="coords">No box drawn</div>
289
  </div>
290
+
291
  <div class="format">
292
+ <div class="format-title">XYWH (x, y, width, height) top left: (x, y), width and height of
293
+ box</div>
294
  <div id="xywh" class="coords">No box drawn</div>
295
  </div>
296
+
297
  <div class="format">
298
  <div class="format-title">Normalized XYWH (pixel values in range of 0-1)</div>
299
  <div id="normalized" class="coords">No box drawn</div>
300
  </div>
301
+
302
  <div class="format">
303
+ <div class="format-title">Center XYWH (cx, cy, w, h) (center x, center y, width, height)
304
+ </div>
305
  <div id="center" class="coords">No box drawn</div>
306
  </div>
307
  </div>
 
327
  <tbody>
328
  <tr>
329
  <td><strong>YOLO</strong></td>
330
+ <td>Real-time object detection system. Training format uses normalized coordinates for
331
+ better generalization across different image sizes.</td>
332
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
333
  </tr>
334
  <tr>
335
  <td><strong>Faster R-CNN</strong></td>
336
+ <td>Region proposal network based detection. Typically uses absolute coordinates during
337
+ inference, but may use normalized coordinates during training.</td>
338
  <td><strong>XYXY</strong> (x1, y1, x2, y2) or normalized coordinates</td>
339
  </tr>
340
  <tr>
341
  <td><strong>SSD</strong></td>
342
+ <td>Single Shot MultiBox Detector uses normalized coordinates for anchor boxes and
343
+ predictions to handle multiple scales efficiently.</td>
344
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
345
  </tr>
346
  <tr>
347
  <td><strong>RetinaNet</strong></td>
348
+ <td>Dense object detector with focal loss. Uses normalized coordinates for anchor boxes and
349
+ predictions.</td>
350
  <td><strong>Normalized XYWH</strong> (x, y, w, h) in range [0,1]</td>
351
  </tr>
352
  <tr>
353
  <td><strong>CornerNet</strong></td>
354
+ <td>Anchor-free detector that predicts keypoint heatmaps. Uses normalized coordinates for
355
+ better scale invariance.</td>
356
  <td><strong>Normalized Corners</strong> (x1, y1, x2, y2) in range [0,1]</td>
357
  </tr>
358
  </tbody>
 
366
  const canvas = document.getElementById('canvas');
367
  const ctx = canvas.getContext('2d');
368
  const labelsContainer = document.getElementById('labels-container');
369
+
370
  let isDrawing = false;
371
  let startX = 0;
372
  let startY = 0;
 
380
  canvas.style.height = image.height + 'px';
381
  labelsContainer.style.width = image.width + 'px';
382
  labelsContainer.style.height = image.height + 'px';
383
+ document.getElementById('image-dimensions').textContent = 'Image dimensions: ' + image.width + ' x ' + image.height;
384
  }
385
+
386
  if (image.complete) {
387
  initCanvas();
388
  } else {
 
405
  label.textContent = text;
406
  label.style.left = `${x}px`;
407
  label.style.top = `${y}px`;
 
 
408
  const containerRect = labelsContainer.getBoundingClientRect();
 
409
  label.style.visibility = 'hidden';
410
  labelsContainer.appendChild(label);
411
  const tempRect = label.getBoundingClientRect();
412
  label.style.visibility = 'visible';
 
413
  if (x + tempRect.width > containerRect.width) {
414
  label.style.left = `${containerRect.width - tempRect.width - 5}px`;
415
  }
 
416
  if (y + tempRect.height > containerRect.height) {
417
  label.style.top = `${containerRect.height - tempRect.height - 5}px`;
418
  }
 
419
  labelsContainer.appendChild(label);
420
  labelElements.push(label);
421
  }
422
 
423
+ function drawDot(x, y) {
424
+ ctx.beginPath();
425
+ ctx.arc(x, y, 4, 0, 2 * Math.PI);
426
+ ctx.fillStyle = 'black';
427
+ ctx.fill();
428
+ }
429
+
430
+ // Draw dots at the positions corresponding to the selected format.
431
+ function drawDots(x1, y1, x2, y2) {
432
+ const format = document.querySelector('input[name="format"]:checked').value;
433
+ if (format === 'center') {
434
+ const centerX = x1 + (x2 - x1) / 2;
435
+ const centerY = y1 + (y2 - y1) / 2;
436
+ drawDot(centerX, centerY);
437
+ drawDot(x1, y1);
438
+ } else {
439
+ drawDot(x1, y1);
440
+ drawDot(x2, y2);
441
+ }
442
  }
443
 
444
  function createLabels(x1, y1, x2, y2) {
 
446
  const height = y2 - y1;
447
  const format = document.querySelector('input[name="format"]:checked').value;
448
  let labels = [];
449
+ switch (format) {
 
450
  case 'xyxy':
451
  labels = [
452
  { text: `(${Math.round(x1)}, ${Math.round(y1)})`, x: x1, y: y1, position: 'top-left' },
 
478
  ];
479
  break;
480
  }
 
481
  labels.forEach(label => {
482
  createLabel(label.text, label.x, label.y, label.position);
483
  });
484
  }
485
 
486
+ function drawBox(x1, y1, x2, y2) {
487
+ clearCanvas();
488
+ ctx.strokeStyle = '#00ff00';
489
+ ctx.lineWidth = 2;
490
+ const width = x2 - x1;
491
+ const height = y2 - y1;
492
+ ctx.strokeRect(x1, y1, width, height);
493
+ drawDots(x1, y1, x2, y2);
494
+ createLabels(x1, y1, x2, y2);
495
+ lastCoords = { x1, y1, x2, y2 };
496
+ }
497
+
498
  function updateCoordinates(x1, y1, x2, y2) {
499
  if (x1 === null || y1 === null || x2 === null || y2 === null) {
500
  document.getElementById('xyxy').textContent = 'No box drawn';
 
503
  document.getElementById('center').textContent = 'No box drawn';
504
  return;
505
  }
 
506
  const width = x2 - x1;
507
  const height = y2 - y1;
508
+ document.getElementById('xyxy').textContent =
 
509
  `[${Math.round(x1)}, ${Math.round(y1)}, ${Math.round(x2)}, ${Math.round(y2)}]`;
510
+ document.getElementById('xywh').textContent =
 
511
  `[${Math.round(x1)}, ${Math.round(y1)}, ${Math.round(width)}, ${Math.round(height)}]`;
 
512
  const normalizedX = (x1 / canvas.width).toFixed(3);
513
  const normalizedY = (y1 / canvas.height).toFixed(3);
514
  const normalizedW = (width / canvas.width).toFixed(3);
515
  const normalizedH = (height / canvas.height).toFixed(3);
516
+ document.getElementById('normalized').textContent =
517
  `[${normalizedX}, ${normalizedY}, ${normalizedW}, ${normalizedH}]`;
 
518
  const centerX = x1 + width / 2;
519
  const centerY = y1 + height / 2;
520
+ document.getElementById('center').textContent =
521
  `[${Math.round(centerX)}, ${Math.round(centerY)}, ${Math.round(width)}, ${Math.round(height)}]`;
522
  }
523
+
524
  document.querySelectorAll('input[name="format"]').forEach(radio => {
525
  radio.addEventListener('change', () => {
526
  if (lastCoords) {
 
529
  }
530
  });
531
  });
532
+
533
  canvas.addEventListener('mousedown', (e) => {
534
  isDrawing = true;
535
  const rect = canvas.getBoundingClientRect();
 
538
  clearCanvas();
539
  updateCoordinates(null, null, null, null);
540
  });
541
+
542
  canvas.addEventListener('mousemove', (e) => {
543
  if (!isDrawing) return;
 
544
  const rect = canvas.getBoundingClientRect();
545
  const currentX = e.clientX - rect.left;
546
  const currentY = e.clientY - rect.top;
 
547
  drawBox(startX, startY, currentX, currentY);
548
  updateCoordinates(startX, startY, currentX, currentY);
549
  });
550
+
551
  canvas.addEventListener('mouseup', (e) => {
552
  if (!isDrawing) return;
 
553
  const rect = canvas.getBoundingClientRect();
554
  const endX = e.clientX - rect.left;
555
  const endY = e.clientY - rect.top;
 
556
  drawBox(startX, startY, endX, endY);
557
  updateCoordinates(startX, startY, endX, endY);
 
558
  isDrawing = false;
559
  });
560
+
561
  canvas.addEventListener('mouseleave', () => {
562
  isDrawing = false;
563
  });
564
 
 
565
  document.getElementById('clear-button').addEventListener('click', () => {
566
  clearCanvas();
567
  lastCoords = null;
 
569
  });
570
  </script>
571
  </body>
572
+
573
+ </html>