Created
January 12, 2026 18:11
-
-
Save AdoHaha/16ea674d9a836e04d5088bb802d7bfdb to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>JSON Bounding Box Drawer</title> | |
| <style> | |
| body { | |
| font-family: sans-serif; | |
| margin: 20px; | |
| background: #f0f0f0; | |
| } | |
| .controls { | |
| background: white; | |
| padding: 20px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
| margin-bottom: 20px; | |
| display: flex; | |
| gap: 20px; | |
| flex-wrap: wrap; | |
| } | |
| .input-group { | |
| flex: 1; | |
| min-width: 300px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| textarea { | |
| width: 100%; | |
| height: 150px; | |
| font-family: monospace; | |
| padding: 10px; | |
| border: 1px solid #ccc; | |
| border-radius: 4px; | |
| } | |
| .canvas-container { | |
| position: relative; | |
| display: inline-block; | |
| background: white; | |
| border: 1px solid #ccc; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
| overflow: auto; | |
| max-width: 100%; | |
| } | |
| canvas { | |
| display: block; | |
| } | |
| button { | |
| padding: 10px 20px; | |
| background: #007bff; | |
| color: white; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background: #0056b3; | |
| } | |
| .error { | |
| color: red; | |
| margin-top: 5px; | |
| display: none; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="controls"> | |
| <div class="input-group"> | |
| <label for="imageInput"><strong>1. Select Image:</strong></label> | |
| <input type="file" id="imageInput" accept="image/*"> | |
| <p style="font-size: 0.9em; color: #666;">Select 'aniaprawa_pos_z.png' or similar.</p> | |
| </div> | |
| <div class="input-group"> | |
| <label for="jsonInput"><strong>2. Paste JSON:</strong></label> | |
| <textarea id="jsonInput" placeholder='[{"box_2d": [535, 638, 561, 663], "label": "pinky finger fingertip"}]'></textarea> | |
| <button onclick="draw()">Render Boxes</button> | |
| <div id="errorMsg" class="error"></div> | |
| </div> | |
| </div> | |
| <div class="canvas-container"> | |
| <canvas id="outputCanvas"></canvas> | |
| </div> | |
| <script> | |
| const imageInput = document.getElementById('imageInput'); | |
| const jsonInput = document.getElementById('jsonInput'); | |
| const canvas = document.getElementById('outputCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const errorMsg = document.getElementById('errorMsg'); | |
| let currentImage = null; | |
| // Default JSON example | |
| jsonInput.value = JSON.stringify([ | |
| {"box_2d": [535, 638, 561, 663], "label": "pinky finger fingertip"} | |
| ], null, 2); | |
| imageInput.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| const img = new Image(); | |
| img.onload = function() { | |
| currentImage = img; | |
| draw(); | |
| } | |
| img.src = event.target.result; | |
| } | |
| reader.readAsDataURL(file); | |
| }); | |
| function draw() { | |
| errorMsg.style.display = 'none'; | |
| errorMsg.textContent = ''; | |
| if (!currentImage) { | |
| // Try to clear if no image, or just wait | |
| canvas.width = 800; | |
| canvas.height = 600; | |
| ctx.fillStyle = '#eee'; | |
| ctx.fillRect(0,0, 800, 600); | |
| ctx.fillStyle = '#666'; | |
| ctx.font = '20px sans-serif'; | |
| ctx.fillText('Please select an image first', 280, 300); | |
| return; | |
| } | |
| // Resize canvas to match image | |
| canvas.width = currentImage.width; | |
| canvas.height = currentImage.height; | |
| // Draw Image | |
| ctx.drawImage(currentImage, 0, 0); | |
| // Parse JSON | |
| let data = []; | |
| try { | |
| data = JSON.parse(jsonInput.value); | |
| } catch (e) { | |
| errorMsg.textContent = 'Invalid JSON: ' + e.message; | |
| errorMsg.style.display = 'block'; | |
| return; | |
| } | |
| if (!Array.isArray(data)) { | |
| errorMsg.textContent = 'JSON must be an array.'; | |
| errorMsg.style.display = 'block'; | |
| return; | |
| } | |
| // Draw Boxes | |
| // Coordinate system is 0-1000 | |
| const scaleX = currentImage.width / 1000; | |
| const scaleY = currentImage.height / 1000; | |
| ctx.lineWidth = 2; | |
| ctx.font = '14px sans-serif'; | |
| data.forEach((item, index) => { | |
| if (!item.box_2d || item.box_2d.length !== 4) return; | |
| const [x1_raw, y1_raw, x2_raw, y2_raw] = item.box_2d; | |
| const x1 = x1_raw * scaleX; | |
| const y1 = y1_raw * scaleY; | |
| const x2 = x2_raw * scaleX; | |
| const y2 = y2_raw * scaleY; | |
| const w = x2 - x1; | |
| const h = y2 - y1; | |
| // Pick a color (hashed from label or index) | |
| const hue = (index * 137.5) % 360; | |
| const color = `hsla(${hue}, 70%, 50%, 1)`; | |
| const bgColor = `hsla(${hue}, 70%, 50%, 0.2)`; | |
| // Draw Box | |
| ctx.strokeStyle = color; | |
| ctx.fillStyle = bgColor; | |
| ctx.strokeRect(x1, y1, w, h); | |
| ctx.fillRect(x1, y1, w, h); | |
| // Draw Label | |
| if (item.label) { | |
| const text = item.label; | |
| const textWidth = ctx.measureText(text).width; | |
| const textHeight = 16; | |
| ctx.fillStyle = color; | |
| ctx.fillRect(x1, y1 - textHeight - 4, textWidth + 8, textHeight + 4); | |
| ctx.fillStyle = 'white'; | |
| ctx.textBaseline = 'top'; | |
| ctx.fillText(text, x1 + 4, y1 - textHeight - 2); | |
| } | |
| }); | |
| } | |
| // Initial Draw call to show placeholder | |
| draw(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment