You can edit the below JavaScript code to customize the image tool.
Apply Changes
function processImage(originalImg, mehndiColorStr = "101,67,33", backgroundColorStr = "255,235,205", edgeThreshold = 50) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const imgWidth = originalImg.naturalWidth;
const imgHeight = originalImg.naturalHeight;
// Ensure canvas has at least 1x1 dimension, even if original image is 0x0
canvas.width = Math.max(1, imgWidth);
canvas.height = Math.max(1, imgHeight);
// Parse color strings into RGB components
const [mR, mG, mB] = mehndiColorStr.split(',').map(Number);
const [bR, bG, bB] = backgroundColorStr.split(',').map(Number);
// If the image is too small for the Sobel kernel (needs at least 3x3 area)
// or if original dimensions were 0, fill with background color.
if (imgWidth < 3 || imgHeight < 3) {
ctx.fillStyle = `rgb(${bR},${bG},${bB})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
return canvas;
}
// Draw the original image onto the canvas to access its pixel data
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
const imageData = ctx.getImageData(0, 0, imgWidth, imgHeight);
const data = imageData.data; // Pixel data: R,G,B,A, R,G,B,A, ...
// Step 1: Convert the image to grayscale
// Store grayscale values in a 1D array
const grayData = new Uint8ClampedArray(imgWidth * imgHeight);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
// Standard luminance calculation
grayData[i / 4] = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
}
// Step 2: Apply Sobel operator to detect edges
// edgeMagnitudeData will store the calculated magnitude for each non-border pixel
const edgeMagnitudeData = new Float32Array(imgWidth * imgHeight);
let maxMagnitude = 0; // To normalize magnitudes later
// Iterate over interior pixels (Sobel kernel is 3x3)
for (let y = 1; y < imgHeight - 1; y++) {
for (let x = 1; x < imgWidth - 1; x++) {
const i = y * imgWidth + x; // Index for the current pixel in grayData
// Gx (horizontal gradient)
const Gx = (
-grayData[(y - 1) * imgWidth + (x - 1)] + grayData[(y - 1) * imgWidth + (x + 1)] +
-2 * grayData[y * imgWidth + (x - 1)] + 2 * grayData[y * imgWidth + (x + 1)] +
-grayData[(y + 1) * imgWidth + (x - 1)] + grayData[(y + 1) * imgWidth + (x + 1)]
);
// Gy (vertical gradient)
const Gy = (
-grayData[(y - 1) * imgWidth + (x - 1)] - 2 * grayData[(y - 1) * imgWidth + x] - grayData[(y - 1) * imgWidth + (x + 1)] +
grayData[(y + 1) * imgWidth + (x - 1)] + 2 * grayData[(y + 1) * imgWidth + x] + grayData[(y + 1) * imgWidth + (x + 1)]
);
const magnitude = Math.sqrt(Gx * Gx + Gy * Gy);
edgeMagnitudeData[i] = magnitude;
if (magnitude > maxMagnitude) {
maxMagnitude = magnitude;
}
}
}
// Step 3: Normalize edge magnitudes to a 0-255 range
const normalizedEdgeData = new Uint8ClampedArray(imgWidth * imgHeight);
if (maxMagnitude > 0) {
for (let i = 0; i < imgWidth * imgHeight; i++) {
// Pixels on the border were not processed by Sobel, their edgeMagnitudeData[i] is 0.
// Normalizing 0 gives 0, which is correct (no edge detected).
normalizedEdgeData[i] = Math.round((edgeMagnitudeData[i] / maxMagnitude) * 255);
}
}
// If maxMagnitude is 0 (e.g., for a completely flat image), normalizedEdgeData will be all zeros.
// Step 4: Colorize the output image based on normalized edge magnitudes
// Iterate through all pixel positions (0 to width*height - 1)
for (let i = 0; i < grayData.length; i++) {
const dataIdx = i * 4; // Base index for R channel in imageData.data
const x = i % imgWidth;
const y = Math.floor(i / imgWidth);
// Color border pixels with the background color
if (x === 0 || x === imgWidth - 1 || y === 0 || y === imgHeight - 1) {
data[dataIdx] = bR;
data[dataIdx + 1] = bG;
data[dataIdx + 2] = bB;
} else {
// For non-border pixels, use their normalized edge magnitude
const normMagnitude = normalizedEdgeData[i];
if (normMagnitude > edgeThreshold) { // If edge is strong enough
data[dataIdx] = mR; // Use mehndi color for the pattern
data[dataIdx + 1] = mG;
data[dataIdx + 2] = mB;
} else { // Otherwise, use background color
data[dataIdx] = bR;
data[dataIdx + 1] = bG;
data[dataIdx + 2] = bB;
}
}
data[dataIdx + 3] = 255; // Set alpha to fully opaque
}
// Put the modified pixel data back onto the canvas
ctx.putImageData(imageData, 0, 0);
return canvas;
}
Apply Changes