Please bookmark this page to avoid losing your image tool!

Image Edge Refinement Tool

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, threshold = 50, strength = 0.2) {
    const width = originalImg.width;
    const height = originalImg.height;

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    // Add { willReadFrequently: true } for potential performance optimization if available
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    ctx.drawImage(originalImg, 0, 0);

    if (width < 3 || height < 3) {
        // Sobel operator needs at least a 3x3 neighborhood.
        // Return the original image drawn on canvas if it's too small for processing.
        console.warn("Image is too small for edge refinement (minimum 3x3 required).");
        return canvas;
    }

    const originalImageData = ctx.getImageData(0, 0, width, height);
    const originalPixels = originalImageData.data;

    // 1. Create grayscale version of the image
    // We use a Uint8ClampedArray, as grayscale values are typically 0-255.
    const grayscaleData = new Uint8ClampedArray(width * height);
    for (let i = 0; i < originalPixels.length; i += 4) {
        const r = originalPixels[i];
        const g = originalPixels[i + 1];
        const b = originalPixels[i + 2];
        // Standard luminance calculation
        const grayscale = 0.299 * r + 0.587 * g + 0.114 * b;
        grayscaleData[i / 4] = grayscale; // Store one grayscale value per pixel
    }

    // Sobel kernels for edge detection
    const sobelX = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];
    const sobelY = [
        [-1, -2, -1],
        [0, 0, 0],
        [1, 2, 1]
    ];

    // Stores raw (non-normalized) gradient magnitudes.
    // Float32Array is used because magnitudes can exceed 255 and require decimal precision.
    const magnitudeData = new Float32Array(width * height); // Initialized to zeros
    let maxMagnitude = 0;

    // 2. Apply Sobel operator to detect edges
    // Iterate over interior pixels (borders are not processed by the 3x3 kernel)
    for (let y = 1; y < height - 1; y++) {
        for (let x = 1; x < width - 1; x++) {
            let gx = 0;
            let gy = 0;
            // Apply 3x3 kernel
            for (let ky = -1; ky <= 1; ky++) {
                for (let kx = -1; kx <= 1; kx++) {
                    // Get grayscale value of neighbor pixel
                    const G_val = grayscaleData[(y + ky) * width + (x + kx)];
                    gx += G_val * sobelX[ky + 1][kx + 1];
                    gy += G_val * sobelY[ky + 1][kx + 1];
                }
            }
            const magnitude = Math.sqrt(gx * gx + gy * gy);
            magnitudeData[y * width + x] = magnitude;
            if (magnitude > maxMagnitude) {
                maxMagnitude = magnitude;
            }
        }
    }

    // 3. Normalize magnitudes to a 0-255 range
    // This makes the `threshold` parameter more intuitive.
    // Uint8ClampedArray is suitable for normalized 0-255 values.
    const normalizedMagnitudeData = new Uint8ClampedArray(width * height); // Initialized to zeros
    if (maxMagnitude > 0) { // Avoid division by zero for flat images
        for (let y = 1; y < height - 1; y++) { // Only normalize non-border pixels that were processed
            for (let x = 1; x < width - 1; x++) {
                 const index = y * width + x;
                 normalizedMagnitudeData[index] = (magnitudeData[index] / maxMagnitude) * 255;
            }
        }
    }
    // Border pixels in normalizedMagnitudeData remain 0, so they won't be affected by refinement.
    
    const outputImageData = ctx.createImageData(width, height);
    const outputPixels = outputImageData.data;

    // 4. Apply refinement (darken edges)
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const i = (y * width + x);       // Index for 1D arrays (grayscale, magnitude)
            const pixelIdx = i * 4;          // Base index for RGBA pixel data in originalPixels/outputPixels

            const m = normalizedMagnitudeData[i]; // Edge magnitude at this pixel (0-255)

            // Get original pixel values
            const r_orig = originalPixels[pixelIdx];
            const g_orig = originalPixels[pixelIdx + 1];
            const b_orig = originalPixels[pixelIdx + 2];
            const a_orig = originalPixels[pixelIdx + 3];

            // Set current output pixel to original initially
            outputPixels[pixelIdx]     = r_orig;
            outputPixels[pixelIdx + 1] = g_orig;
            outputPixels[pixelIdx + 2] = b_orig;
            outputPixels[pixelIdx + 3] = a_orig; // Preserve alpha channel

            if (m > threshold) {
                // This pixel is considered part of an edge.
                // Darken the pixel based on its edge strength (m) and the overall refinement strength.
                // `strength` (0-1) scales `m` (0-255). Max `darkenAmount` is `strength * 255`.
                // E.g., strength=0.2, m=200 -> darkenAmount = 40.
                const darkenAmount = strength * m;

                outputPixels[pixelIdx]     = Math.max(0, r_orig - darkenAmount);
                outputPixels[pixelIdx + 1] = Math.max(0, g_orig - darkenAmount);
                outputPixels[pixelIdx + 2] = Math.max(0, b_orig - darkenAmount);
            }
        }
    }

    ctx.putImageData(outputImageData, 0, 0);
    return canvas;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Edge Refinement Tool enhances images by applying edge detection and refinement techniques. This tool utilizes the Sobel operator to detect edges in images, allowing users to emphasize boundaries and details within their images. Users can adjust parameters like edge detection threshold and refinement strength to achieve desired results. It is useful for graphic designers, photographers, or any users looking to improve the clarity and definition of images by making edges more pronounced, which can enhance visual appeal in digital artwork, editorial content, or web graphics.

Leave a Reply

Your email address will not be published. Required fields are marked *