Zlatko
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Image to SVG Outline with Line Control</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .container { display: flex; flex-direction: column; align-items: center; } img, svg { max-width: 100%; margin-top: 20px; } .buttons, .controls { margin-top: 20px; } </style> </head> <body> <div class="container"> <h1>Upload an Image to Convert to SVG Outline</h1> <input type="file" id="upload" accept="image/*"> <!-- Controls for line thickness, sensitivity, and tracing direction --> <div class="controls"> <label for="lineThickness">Line Thickness: </label> <input type="range" id="lineThickness" min="1" max="10" value="1"> <span id="lineThicknessValue">1</span> px <br><br> <label for="sensitivity">Sensitivity (Brightness Threshold): </label> <input type="range" id="sensitivity" min="0" max="255" value="128"> <span id="sensitivityValue">128</span> <br><br> <label for="traceMode">Trace Mode: </label> <select id="traceMode"> <option value="inside">Inside</option> <option value="outside">Outside</option> </select> </div> <canvas id="canvas" style="display:none;"></canvas> <div id="output"></div> <div class="buttons"> <button id="downloadSvg" style="display:none;">Download SVG</button> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/3.1.1/svg.min.js"></script> <script> let lineThickness = 1; let sensitivity = 128; let traceMode = 'inside'; // Default mode // Update line thickness document.getElementById('lineThickness').addEventListener('input', function() { lineThickness = this.value; document.getElementById('lineThicknessValue').textContent = lineThickness; }); // Update sensitivity document.getElementById('sensitivity').addEventListener('input', function() { sensitivity = this.value; document.getElementById('sensitivityValue').textContent = sensitivity; }); // Update trace mode (inside or outside) document.getElementById('traceMode').addEventListener('change', function() { traceMode = this.value; }); // Handle file upload and image processing document.getElementById('upload').addEventListener('change', function(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { const img = new Image(); img.src = e.target.result; img.onload = function() { processImage(img); }; }; reader.readAsDataURL(file); }); // Process image and generate SVG function processImage(image) { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // Set canvas size to match image canvas.width = image.width; canvas.height = image.height; // Draw the image on canvas ctx.drawImage(image, 0, 0); // Get image data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // Convert image to grayscale for edge detection for (let i = 0; i < data.length; i += 4) { const grayscale = data[i] * 0.3 + data[i + 1] * 0.59 + data[i + 2] * 0.11; data[i] = grayscale; // Red data[i + 1] = grayscale; // Green data[i + 2] = grayscale; // Blue } ctx.putImageData(imageData, 0, 0); // Apply edge detection const edges = sobelEdgeDetection(imageData, canvas.width, canvas.height); ctx.putImageData(edges, 0, 0); // Create SVG based on edge lines generateSVGFromEdges(edges, canvas.width, canvas.height); } // Sobel edge detection algorithm function sobelEdgeDetection(imageData, width, height) { const grayscale = imageData.data; const sobelData = []; const kernelX = [ [-1, 0, 1], [-2, 0, 2], [-1, 0, 1] ]; const kernelY = [ [-1, -2, -1], [0, 0, 0], [1, 2, 1] ]; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const pixelX = ( (kernelX[0][0] * getPixel(grayscale, x - 1, y - 1, width)) + (kernelX[0][1] * getPixel(grayscale, x, y - 1, width)) + (kernelX[0][2] * getPixel(grayscale, x + 1, y - 1, width)) + (kernelX[1][0] * getPixel(grayscale, x - 1, y, width)) + (kernelX[1][2] * getPixel(grayscale, x + 1, y, width)) + (kernelX[2][0] * getPixel(grayscale, x - 1, y + 1, width)) + (kernelX[2][1] * getPixel(grayscale, x, y + 1, width)) + (kernelX[2][2] * getPixel(grayscale, x + 1, y + 1, width)) ); const pixelY = ( (kernelY[0][0] * getPixel(grayscale, x - 1, y - 1, width)) + (kernelY[0][1] * getPixel(grayscale, x, y - 1, width)) + (kernelY[0][2] * getPixel(grayscale, x + 1, y - 1, width)) + (kernelY[1][0] * getPixel(grayscale, x - 1, y, width)) + (kernelY[1][2] * getPixel(grayscale, x + 1, y, width)) + (kernelY[2][0] * getPixel(grayscale, x - 1, y + 1, width)) + (kernelY[2][1] * getPixel(grayscale, x, y + 1, width)) + (kernelY[2][2] * getPixel(grayscale, x + 1, y + 1, width)) ); const magnitude = Math.sqrt((pixelX * pixelX) + (pixelY * pixelY)) >>> 0; const index = (y * width + x) * 4; sobelData[index] = magnitude; sobelData[index + 1] = magnitude; sobelData[index + 2] = magnitude; sobelData[index + 3] = 255; // Alpha } } const newImageData = new ImageData(new Uint8ClampedArray(sobelData), width, height); return newImageData; } // Get pixel data at a specific location function getPixel(data, x, y, width) { if (x < 0 || x >= width || y < 0 || y >= width) { return 0; } const index = (y * width + x) * 4; return data[index]; } // Generate SVG from the edges detected function generateSVGFromEdges(edgeData, width, height) { const svg = SVG().size(width, height); for (let y = 0; y < height; y += 2) { for (let x = 0; x < width; x += 2) { const index = (y * width + x) * 4; const color = edgeData.data[index]; if (traceMode === 'inside' && color > sensitivity) { svg.circle(lineThickness).move(x, y).fill('#000'); // Trace inside the object } else if (traceMode === 'outside' && color < sensitivity) { svg.circle(lineThickness).move(x, y).fill('#000'); // Trace outside the object } } } // Output SVG document.getElementById('output').innerHTML = ''; document.getElementById('output').appendChild(svg.node); // Enable download button const svgData = svg.svg(); const blob = new Blob([svgData], { type: 'image/svg+xml' }); const url = URL.createObjectURL(blob); const downloadButton = document.getElementById('downloadSvg'); downloadButton.href = url; downloadButton.download = 'vectorized_image.svg'; downloadButton.style.display = 'inline-block'; } </script> </body> </html>