You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, filterType = 'sobel', threshold = 128, overlayColor = 'rgba(255, 0, 0, 0.7)', blurRadius = 1) {
// 1. Canvas Setup
const width = originalImg.naturalWidth;
const height = originalImg.naturalHeight;
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d', { willReadFrequently: true });
// Draw original image on the canvas which will serve as the background
ctx.drawImage(originalImg, 0, 0);
// Get the image data to work with
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// 2. Grayscale Conversion
// We create both a raw array for convolution and an ImageData object for blurring.
const grayDataForConv = new Uint8ClampedArray(width * height);
const grayImageDataForBlur = ctx.createImageData(width, height);
const grayDataForBlur = grayImageDataForBlur.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
const index = i / 4;
grayDataForConv[index] = gray;
// Populate the ImageData for canvas-based blurring
grayDataForBlur[i] = gray;
grayDataForBlur[i + 1] = gray;
grayDataForBlur[i + 2] = gray;
grayDataForBlur[i + 3] = 255;
}
// 3. Optional Pre-processing Blur
// Applying a blur helps reduce noise and leads to cleaner edge detection.
let sourceData = grayDataForConv;
if (blurRadius > 0) {
const offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = width;
offscreenCanvas.height = height;
const offscreenCtx = offscreenCanvas.getContext('2d');
offscreenCtx.putImageData(grayImageDataForBlur, 0, 0);
offscreenCtx.filter = `blur(${blurRadius}px)`;
// Drawing onto itself is required to apply the filter
offscreenCtx.drawImage(offscreenCanvas, 0, 0);
const blurredImageData = offscreenCtx.getImageData(0, 0, width, height);
const blurredData = blurredImageData.data;
// Extract the blurred grayscale values back into a simple array
const blurredGrayData = new Uint8ClampedArray(width * height);
for(let i = 0; i < blurredData.length; i += 4) {
blurredGrayData[i/4] = blurredData[i];
}
sourceData = blurredGrayData;
}
// 4. Edge Detection (Convolution)
const kernels = {
sobel: {
x: [[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]],
y: [[-1, -2, -1], [0, 0, 0], [1, 2, 1]]
},
prewitt: {
x: [[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]],
y: [[-1, -1, -1], [0, 0, 0], [1, 1, 1]]
},
laplacian: {
// Laplacian uses a single kernel
single: [[0, 1, 0], [1, -4, 1], [0, 1, 0]]
}
};
const selectedKernel = kernels[filterType.toLowerCase()] || kernels.sobel;
const gradientData = new Float32Array(width * height);
// Iterate over each pixel (excluding borders)
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
let magnitude = 0;
if (selectedKernel.single) {
// Handle single-kernel filters like Laplacian
let convValue = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const val = sourceData[(y + ky) * width + (x + kx)];
convValue += val * selectedKernel.single[ky + 1][kx + 1];
}
}
magnitude = Math.abs(convValue);
} else {
// Handle dual-kernel filters like Sobel and Prewitt
let gx = 0;
let gy = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const val = sourceData[(y + ky) * width + (x + kx)];
gx += val * selectedKernel.x[ky + 1][kx + 1];
gy += val * selectedKernel.y[ky + 1][kx + 1];
}
}
magnitude = Math.sqrt(gx * gx + gy * gy);
}
const index = y * width + x;
gradientData[index] = magnitude;
}
}
// 5. Create Overlay based on Threshold
// First, parse the overlayColor string into RGBA components
let r = 255, g = 0, b = 0, a = 178; // Default: semi-transparent red
try {
const colorParserCanvas = document.createElement('canvas');
colorParserCanvas.width = 1;
colorParserCanvas.height = 1;
const cpcCtx = colorParserCanvas.getContext('2d');
cpcCtx.fillStyle = overlayColor;
cpcCtx.fillRect(0, 0, 1, 1);
[r, g, b, a] = cpcCtx.getImageData(0, 0, 1, 1).data;
} catch(e) {
console.error("Invalid overlayColor provided:", overlayColor);
}
const overlayImageData = ctx.createImageData(width, height);
const overlayData = overlayImageData.data;
for (let i = 0; i < gradientData.length; i++) {
if (gradientData[i] > threshold) {
overlayData[i * 4] = r;
overlayData[i * 4 + 1] = g;
overlayData[i * 4 + 2] = b;
overlayData[i * 4 + 3] = a;
}
}
// 6. Composite the final image
// Put the overlay data onto a temporary canvas, then draw it over the main one.
const overlayCanvas = document.createElement('canvas');
overlayCanvas.width = width;
overlayCanvas.height = height;
overlayCanvas.getContext('2d').putImageData(overlayImageData, 0, 0);
ctx.drawImage(overlayCanvas, 0, 0);
// 7. Return the final canvas
return canvas;
}
Apply Changes