You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Detects edges in an image using the Sobel operator and transforms their color.
*
* @param {Image} originalImg The source Image object to process.
* @param {string} [edgeColor='#000000'] The color for the detected edges (e.g., '#FF0000', 'red').
* @param {number} [threshold=50] The sensitivity of the edge detection, from 0 to 255. Higher values result in fewer, sharper edges.
* @param {string} [backgroundColor='#FFFFFF'] The background color for non-edge areas. Use 'transparent' for a transparent background.
* @returns {HTMLCanvasElement} A canvas element with the transformed image.
*/
function processImage(originalImg, edgeColor = '#000000', threshold = 50, backgroundColor = '#FFFFFF') {
/**
* Helper function to parse various color string formats (hex, rgb, names) into an RGBA object.
* @param {string} colorStr The color string to parse.
* @returns {{r: number, g: number, b: number, a: number}} An object with RGBA components.
*/
const parseColor = (colorStr) => {
if (typeof colorStr !== 'string') {
return { r: 0, g: 0, b: 0, a: 255 };
}
if (colorStr.toLowerCase() === 'transparent') {
return { r: 0, g: 0, b: 0, a: 0 };
}
const tempElem = document.createElement('div');
tempElem.style.color = colorStr;
document.body.appendChild(tempElem);
const computedColor = window.getComputedStyle(tempElem).color;
document.body.removeChild(tempElem);
const match = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
if (match) {
return {
r: parseInt(match[1], 10),
g: parseInt(match[2], 10),
b: parseInt(match[3], 10),
a: match[4] !== undefined ? Math.round(parseFloat(match[4]) * 255) : 255
};
}
return { r: 0, g: 0, b: 0, a: 255 }; // Fallback to black
};
const width = originalImg.naturalWidth || originalImg.width;
const height = originalImg.naturalHeight || originalImg.height;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.drawImage(originalImg, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// Create a grayscale representation for edge detection
const grayscaleData = new Uint8ClampedArray(width * height);
for (let i = 0; i < data.length; i += 4) {
const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
grayscaleData[i / 4] = gray;
}
// Sobel operator kernels
const Gx = [ [-1, 0, 1], [-2, 0, 2], [-1, 0, 1] ];
const Gy = [ [-1, -2, -1], [0, 0, 0], [1, 2, 1] ];
// Use a Float32Array to store magnitudes which can exceed 255
const magnitudeData = new Float32Array(width * height);
let maxMagnitude = 0;
// Apply Sobel operator to each pixel (ignoring the 1px border)
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let gx = 0;
let gy = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const grayValue = grayscaleData[(y + ky) * width + (x + kx)];
gx += grayValue * Gx[ky + 1][kx + 1];
gy += grayValue * Gy[ky + 1][kx + 1];
}
}
// Using Math.abs for performance over Math.sqrt
const magnitude = Math.abs(gx) + Math.abs(gy);
const currentIndex = y * width + x;
magnitudeData[currentIndex] = magnitude;
if (magnitude > maxMagnitude) {
maxMagnitude = magnitude;
}
}
}
const newImageData = ctx.createImageData(width, height);
const newData = newImageData.data;
const edgeRgba = parseColor(edgeColor);
const bgRgba = parseColor(backgroundColor);
// Build the final image, coloring pixels based on normalized magnitude and threshold
for (let i = 0; i < magnitudeData.length; i++) {
const offset = i * 4;
// Normalize magnitude to a 0-255 range to make the threshold consistent
const normalizedMagnitude = (magnitudeData[i] / maxMagnitude) * 255;
if (normalizedMagnitude > threshold) {
newData[offset] = edgeRgba.r;
newData[offset + 1] = edgeRgba.g;
newData[offset + 2] = edgeRgba.b;
newData[offset + 3] = edgeRgba.a;
} else {
newData[offset] = bgRgba.r;
newData[offset + 1] = bgRgba.g;
newData[offset + 2] = bgRgba.b;
newData[offset + 3] = bgRgba.a;
}
}
ctx.putImageData(newImageData, 0, 0);
return canvas;
}
Apply Changes