|
| 1 | +document.addEventListener('DOMContentLoaded', () => { |
| 2 | + const imageUpload = document.getElementById('imageUpload'); |
| 3 | + const processButton = document.getElementById('processButton'); |
| 4 | + const originalImage = document.getElementById('originalImage'); |
| 5 | + const processedCanvas = document.getElementById('processedCanvas'); |
| 6 | + const promptInput = document.getElementById('promptInput'); |
| 7 | + const responseTextDiv = document.getElementById('responseText'); |
| 8 | + const ctx = processedCanvas.getContext('2d'); |
| 9 | + |
| 10 | + let imageBase64 = ''; |
| 11 | + let originalImageURL = ''; |
| 12 | + let originalImageObj; |
| 13 | + |
| 14 | + // Initially hide the original image |
| 15 | + originalImage.style.display = 'none'; |
| 16 | + |
| 17 | + // Handle image upload |
| 18 | + imageUpload.addEventListener('change', (event) => { |
| 19 | + const file = event.target.files[0]; |
| 20 | + if (file) { |
| 21 | + const reader = new FileReader(); |
| 22 | + reader.onload = (e) => { |
| 23 | + imageBase64 = e.target.result.split(',')[1]; |
| 24 | + originalImageURL = e.target.result; |
| 25 | + originalImage.src = originalImageURL; |
| 26 | + |
| 27 | + originalImageObj = new Image(); |
| 28 | + originalImageObj.onload = () => { |
| 29 | + // Keep aspect ratio while scaling |
| 30 | + const originalWidth = originalImageObj.width; |
| 31 | + const originalHeight = originalImageObj.height; |
| 32 | + |
| 33 | + // Maximum dimensions for the display area |
| 34 | + const maxWidth = 600; |
| 35 | + const maxHeight = 400; |
| 36 | + |
| 37 | + // Calculate aspect ratio |
| 38 | + const aspectRatio = originalWidth / originalHeight; |
| 39 | + |
| 40 | + let displayWidth = maxWidth; |
| 41 | + let displayHeight = maxWidth / aspectRatio; |
| 42 | + |
| 43 | + if (displayHeight > maxHeight) { |
| 44 | + displayHeight = maxHeight; |
| 45 | + displayWidth = maxHeight * aspectRatio; |
| 46 | + } |
| 47 | + |
| 48 | + processedCanvas.width = displayWidth; |
| 49 | + processedCanvas.height = displayHeight; |
| 50 | + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); |
| 51 | + ctx.drawImage(originalImageObj, 0, 0, displayWidth, displayHeight); // Draw the original image on the canvas |
| 52 | + |
| 53 | + // Show the original image after it's loaded for preview |
| 54 | + originalImage.style.display = 'block'; |
| 55 | + }; |
| 56 | + originalImageObj.src = originalImageURL; |
| 57 | + |
| 58 | + // Enable the process button |
| 59 | + processButton.disabled = false; |
| 60 | + responseTextDiv.style.display = 'none'; |
| 61 | + responseTextDiv.innerHTML = ''; |
| 62 | + promptInput.value = ''; |
| 63 | + }; |
| 64 | + reader.readAsDataURL(file); |
| 65 | + } |
| 66 | + }); |
| 67 | + |
| 68 | + // Handle process button click |
| 69 | + processButton.addEventListener('click', async () => { |
| 70 | + if (!imageBase64) { |
| 71 | + alert("Please upload an image first."); |
| 72 | + return; |
| 73 | + } |
| 74 | + |
| 75 | + // Clear off previous results. |
| 76 | + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); |
| 77 | + responseTextDiv.innerHTML = 'Analyzing...'; |
| 78 | + responseTextDiv.style.display = 'block'; |
| 79 | + |
| 80 | + |
| 81 | + let prompt = promptInput.value || ""; |
| 82 | + if (prompt.toLowerCase().includes("detect")) { |
| 83 | + const labelMatch = prompt.match(/detect\s+(.*)/i); |
| 84 | + const label = labelMatch ? labelMatch[1] : 'Unknown'; |
| 85 | + prompt = `<image>detect ${label}`; |
| 86 | + } else { |
| 87 | + prompt = `<image>${prompt}`; |
| 88 | + } |
| 89 | + |
| 90 | + try { |
| 91 | + const response = await fetch('/process-image', { |
| 92 | + method: 'POST', |
| 93 | + headers: { |
| 94 | + 'Content-Type': 'application/json', |
| 95 | + }, |
| 96 | + body: JSON.stringify({ image: imageBase64, prompt: prompt, targetWidth: processedCanvas.width, targetHeight: processedCanvas.height, originalWidth: originalImageObj.width, originalHeight: originalImageObj.height }), |
| 97 | + }); |
| 98 | + |
| 99 | + if (response.ok) { |
| 100 | + const data = await response.json(); |
| 101 | + |
| 102 | + if (data.success) { |
| 103 | + if (prompt.includes("<image>detect")) { |
| 104 | + const { boundingBox } = data; |
| 105 | + drawBoundingBox(boundingBox, ctx) |
| 106 | + |
| 107 | + responseTextDiv.style.display = 'block'; |
| 108 | + responseTextDiv.innerHTML = "Response: " + escapeHtml(data.message); |
| 109 | + } |
| 110 | + else { |
| 111 | + processedCanvas.width = 0; |
| 112 | + processedCanvas.height = 0; |
| 113 | + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); |
| 114 | + responseTextDiv.style.display = 'block'; |
| 115 | + responseTextDiv.innerHTML = data.message; |
| 116 | + } |
| 117 | + } |
| 118 | + else { |
| 119 | + processedCanvas.width = 0; |
| 120 | + processedCanvas.height = 0; |
| 121 | + ctx.clearRect(0, 0, processedCanvas.width, processedCanvas.height); |
| 122 | + responseTextDiv.style.display = 'block'; |
| 123 | + responseTextDiv.innerHTML = "Response: " + data.message; |
| 124 | + } |
| 125 | + } |
| 126 | + else { |
| 127 | + alert('Error processing image.'); |
| 128 | + } |
| 129 | + |
| 130 | + } catch (error) { |
| 131 | + console.error('Error:', error); |
| 132 | + alert('Error processing image.'); |
| 133 | + } finally { |
| 134 | + |
| 135 | + } |
| 136 | + }); |
| 137 | + |
| 138 | + |
| 139 | + // Function to draw the bounding box on canvas |
| 140 | + function drawBoundingBox(boundingBox, ctx) { |
| 141 | + |
| 142 | + const { x1, y1, x2, y2, label } = boundingBox; |
| 143 | + |
| 144 | + // Generate random color for the bounding box and label background |
| 145 | + const randomColor = getRandomColor(); |
| 146 | + |
| 147 | + // Set styles for the bounding box (random color stroke) |
| 148 | + ctx.strokeStyle = randomColor; |
| 149 | + ctx.lineWidth = 5; |
| 150 | + ctx.strokeRect(x1, y1, x2 - x1, y2 - y1); |
| 151 | + |
| 152 | + // Adjust label background height to fit the text properly |
| 153 | + const labelPadding = 10; |
| 154 | + const textWidth = ctx.measureText(label.charAt(0).toUpperCase() + label.slice(1)).width; |
| 155 | + const labelWidth = textWidth * 3; |
| 156 | + const labelHeight = 30; |
| 157 | + const labelY = y1 - labelHeight; |
| 158 | + |
| 159 | + // Draw background for the label (same random color as bounding box) |
| 160 | + ctx.fillStyle = randomColor; |
| 161 | + ctx.fillRect(x1, labelY, labelWidth, labelHeight); |
| 162 | + |
| 163 | + // Set the text color to white |
| 164 | + ctx.fillStyle = "white"; |
| 165 | + ctx.font = "bold 20px Arial"; |
| 166 | + ctx.fillText(label.charAt(0).toUpperCase() + label.slice(1), x1 + labelPadding, labelY + labelHeight - labelPadding); |
| 167 | + } |
| 168 | + |
| 169 | + // Function to generate a random RGB color |
| 170 | + function getRandomColor() { |
| 171 | + const r = Math.floor(Math.random() * 256); |
| 172 | + const g = Math.floor(Math.random() * 256); |
| 173 | + const b = Math.floor(Math.random() * 256); |
| 174 | + return `rgb(${r},${g},${b})`; |
| 175 | + } |
| 176 | + |
| 177 | + function escapeHtml(unsafe) { |
| 178 | + return unsafe |
| 179 | + .replace(/&/g, "&") |
| 180 | + .replace(/</g, "<") |
| 181 | + .replace(/>/g, ">") |
| 182 | + .replace(/"/g, """) |
| 183 | + .replace(/'/g, "'"); |
| 184 | + } |
| 185 | + |
| 186 | +}); |
0 commit comments