Please bookmark this page to avoid losing your image tool!

Image Edge Detection Filter

(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.
async function processImage(originalImg, threshold = -1, invertOutput = "false") {
    // The function is async to align with potential needs for dynamic imports or Web Workers,
    // though this specific implementation doesn't require them.

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Use originalImg.width and originalImg.height for Image objects.
    // naturalWidth/Height are more for <img> elements in the DOM.
    // For an Image object, .width and .height give its intrinsic dimensions once loaded.
    const width = originalImg.width;
    const height = originalImg.height;
    
    if (width === 0 || height === 0) {
        // Handle unloaded or zero-size image
        canvas.width = 0;
        canvas.height = 0;
        // Return an empty canvas, which is valid and displayable.
        return canvas;
    }

    canvas.width = width;
    canvas.height = height;

    try {
        // Draw the image onto the canvas to access its pixel data
        ctx.drawImage(originalImg, 0, 0, width, height);
    } catch (e) {
        console.error("Error drawing image to canvas:", e);
        // Fallback for drawing error: return a canvas with an error message
        // This might happen if originalImg is not a valid image source (e.g. broken data URI)
        ctx.fillStyle = "rgba(200, 200, 200, 1)"; // Light gray background
        ctx.fillRect(0, 0, width, height);
        ctx.fillStyle = "black";
        ctx.textAlign = "center";
        ctx.font = "14px Arial";
        ctx.fillText("Error: Invalid image source.", width / 2, height / 2);
        return canvas;
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, width, height);
    } catch (e) {
        // This error often occurs due to CORS policy if the image is from a different origin
        // and the server doesn't send appropriate CORS headers.
        console.error("Error getting ImageData:", e);
        
        // The original image is already drawn on 'canvas'. We'll overlay an error message.
        ctx.fillStyle = "rgba(255, 0, 0, 0.6)"; // Semi-transparent red overlay
        ctx.fillRect(0, 0, width, height);
        
        ctx.fillStyle = "white";
        ctx.textAlign = "center";
        ctx.font = "bold 16px Arial";
        
        let errorMessage = "Error Processing Image";
        if (e.name === 'SecurityError') {
            errorMessage = "Cannot process: Image is from another domain (CORS issue).\nEnsure the image server allows cross-origin access.";
        } else {
            errorMessage = `Cannot process: ${e.name}\n${e.message}`;
        }

        const lines = errorMessage.split('\n');
        const lineHeight = 20; // Approximate line height
        const totalTextHeight = lines.length * lineHeight;
        let startY = height / 2 - totalTextHeight / 2 + lineHeight / 2 + 5; // Adjusted for better centering

        lines.forEach((line, index) => {
            ctx.fillText(line, width / 2, startY + index * lineHeight);
        });
        return canvas; // Return the canvas with the original image + error overlay
    }
    
    const data = imageData.data; // Pixel data (RGBA sequence)
    const outputImageData = ctx.createImageData(width, height);
    const outputData = outputImageData.data;

    // 1. Convert image to grayscale
    // We'll store grayscale values in a separate array for easier access during convolution.
    const grayscaleData = new Uint8ClampedArray(width * height);
    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 (coefficients for perceived brightness)
        const gray = 0.299 * r + 0.587 * g + 0.114 * b;
        grayscaleData[i / 4] = Math.round(gray); // Store as integer 0-255
    }

    // 2. Define Sobel kernels for edge detection
    const Gx = [ // Kernel for detecting horizontal edges
        -1, 0, 1,
        -2, 0, 2,
        -1, 0, 1
    ];
    const Gy = [ // Kernel for detecting vertical edges
        -1, -2, -1,
         0,  0,  0,
         1,  2,  1
    ];

    // Parse parameters
    const shouldInvert = invertOutput.toString().toLowerCase() === "true";
    // A threshold must be a number between 0 and 255 (inclusive) to be active.
    // Default threshold is -1, which means grayscale output (no thresholding).
    const useThreshold = typeof threshold === 'number' && threshold >= 0 && threshold <= 255;

    // 3. Apply Sobel operator (convolution)
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            let sumX = 0; // Gradient in X direction
            let sumY = 0; // Gradient in Y direction

            // Apply 3x3 kernel for Gx and Gy
            for (let ky = -1; ky <= 1; ky++) { // Kernel Y offset
                for (let kx = -1; kx <= 1; kx++) { // Kernel X offset
                    const Gx_val = Gx[(ky + 1) * 3 + (kx + 1)];
                    const Gy_val = Gy[(ky + 1) * 3 + (kx + 1)];
                    
                    // Get coordinates of the neighbor pixel, clamping to image bounds
                    const currentPixelX = Math.min(width - 1, Math.max(0, x + kx));
                    const currentPixelY = Math.min(height - 1, Math.max(0, y + ky));
                    
                    // Get grayscale value of the neighbor pixel
                    const grayVal = grayscaleData[currentPixelY * width + currentPixelX];

                    sumX += grayVal * Gx_val;
                    sumY += grayVal * Gy_val;
                }
            }

            // Calculate gradient magnitude: sqrt(Gx^2 + Gy^2)
            const magnitude = Math.sqrt(sumX * sumX + sumY * sumY);
            let finalValue;

            if (useThreshold) {
                // Binarize the output based on the threshold
                if (magnitude > threshold) {
                    finalValue = shouldInvert ? 0 : 255; // Edge pixel
                } else {
                    finalValue = shouldInvert ? 255 : 0; // Non-edge pixel
                }
            } else {
                // Output grayscale gradient magnitude (clamped to 0-255)
                finalValue = Math.min(255, Math.round(magnitude)); 
            }

            // Set the output pixel (R, G, B all same for grayscale/binary)
            const N = (y * width + x) * 4; // Index for the current pixel in outputData
            outputData[N]     = finalValue; // Red
            outputData[N + 1] = finalValue; // Green
            outputData[N + 2] = finalValue; // Blue
            outputData[N + 3] = 255;        // Alpha (fully opaque)
        }
    }

    // 4. Put the processed pixel data back onto the canvas
    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 Detection Filter is a web-based tool that allows users to apply edge detection effects to images. By processing images with algorithms such as the Sobel operator, this tool can convert images to grayscale and detect edges based on gradients. Users can customize the output by adjusting the threshold for edge detection and choosing to invert the colors of the output. Real-world use cases for this tool include enhancing images for better visibility of shapes and outlines, creating unique artistic effects, and assisting in various image analysis tasks such as preparing images for computer vision applications.

Leave a Reply

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