Please bookmark this page to avoid losing your image tool!

Image Architectural Plan Filter Effect 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, edgeThreshold = 80, invertColorsStr = "false") {
    const invert = invertColorsStr === "true";

    const canvas = document.createElement('canvas');
    // Use willReadFrequently hint for potential performance improvements
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Determine image dimensions, preferring natural dimensions if available
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    // Handle cases where image might not be loaded or is genuinely 0-size
    if (width === 0 || height === 0) {
        canvas.width = 0;
        canvas.height = 0;
        console.warn("processImage: Image has zero width or height. Returning an empty (0x0) canvas.");
        return canvas; // Return an empty 0x0 canvas
    }

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

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, width, height);

    let imageData;
    try {
        // Get pixel data from the canvas
        imageData = ctx.getImageData(0, 0, width, height);
    } catch (e) {
        // This error can occur if the image is cross-origin and the canvas becomes tainted
        console.error("processImage: Error getting image data. Likely a cross-origin issue.", e);
        
        // Draw an error message on the canvas over the original image
        ctx.fillStyle = "rgba(255, 0, 0, 0.7)"; // Semi-transparent red overlay
        ctx.fillRect(0, 0, width, height);
        
        ctx.font = "16px Arial";
        ctx.fillStyle = "white";
        ctx.textAlign = "center";
        
        const message = "Error: Cannot process image.";
        const message2 = "(Cross-origin restrictions?)";
        
        // Basic centering of text
        const textY = height / 2 - (height > 30 ? 8 : 0); // Adjust Y for two lines if space
        ctx.fillText(message, width / 2, textY);
        if (height > 30) { // Only show second line if there's enough vertical space
             ctx.fillText(message2, width / 2, textY + 20);
        }
        return canvas; // Return the canvas with the error indication
    }
    
    const data = imageData.data; // Reference to the pixel data (an RGBA Uint8ClampedArray)

    // Step 1: Grayscale Conversion
    // Create a 1D array to store grayscale values for each pixel.
    const grayscaleData = new Uint8ClampedArray(width * height);
    for (let i = 0; i < data.length; i += 4) { // Iterate over RGBA components
        const r = data[i];
        const g = data[i + 1];
        const b = data[i + 2];
        // Using standard luminance formula for grayscale
        const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
        grayscaleData[i / 4] = gray; // Store one gray value per pixel (index i/4)
    }

    // Step 2: Sobel Edge Detection
    // This array will store the magnitude of gradients calculated by the Sobel operator.
    // Using Float32Array for precision as magnitudes can be non-integers.
    const sobelMagnitudes = new Float32Array(width * height);

    // Sobel kernels for detecting horizontal (Gx) and vertical (Gy) edges
    const Gx_kernel = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];

    const Gy_kernel = [
        [-1, -2, -1],
        [ 0,  0,  0],
        [ 1,  2,  1]
    ];

    // Apply Sobel operator. Iterate over each pixel, skipping a 1-pixel border
    // because the Sobel operator uses a 3x3 neighborhood.
    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 to the neighborhood of the current pixel
            for (let ky = -1; ky <= 1; ky++) { // Kernel y-offset
                for (let kx = -1; kx <= 1; kx++) { // Kernel x-offset
                    // Calculate index of the neighbor pixel in grayscaleData
                    const pixelIndex = (y + ky) * width + (x + kx);
                    const pixelVal = grayscaleData[pixelIndex];
                    
                    Gx += pixelVal * Gx_kernel[ky + 1][kx + 1];
                    Gy += pixelVal * Gy_kernel[ky + 1][kx + 1];
                }
            }
            // Calculate the gradient magnitude: sqrt(Gx^2 + Gy^2)
            const magnitude = Math.sqrt(Gx * Gx + Gy * Gy);
            sobelMagnitudes[y * width + x] = magnitude; // Store magnitude for the current pixel
        }
    }

    // Step 3: Thresholding and Final Image Construction
    // Determine line and background colors based on the 'invert' flag.
    // Architectural plans are typically black lines on white, or white lines on a dark background (blueprint).
    const lineColor = invert ? 255 : 0;       // White (255) lines if inverted, else black (0)
    const backgroundColor = invert ? 0 : 255; // Black (0) background if inverted, else white (255)

    // Iterate over all pixels to set final image data based on Sobel magnitudes.
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            const i = (y * width + x) * 4; // Index for the pixel in the RGBA data array
            let value; // This will be the color value (0 or 255) for R, G, B channels

            // Pixels at the border (where Sobel wasn't fully applied or is less reliable)
            // are set to the background color.
            if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
                value = backgroundColor;
            } else {
                // For inner pixels, compare Sobel magnitude to the threshold
                const magnitude = sobelMagnitudes[y * width + x];
                // If magnitude is above threshold, it's an edge (line), otherwise background.
                value = (magnitude > edgeThreshold) ? lineColor : backgroundColor;
            }

            // Set R, G, B channels to the determined value
            data[i]     = value; // Red
            data[i + 1] = value; // Green
            data[i + 2] = value; // Blue
            data[i + 3] = 255;   // Alpha channel (fully opaque)
        }
    }

    // Write the modified pixel data back to the canvas
    ctx.putImageData(imageData, 0, 0);

    return canvas; // Return the processed 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 Architectural Plan Filter Effect Tool allows users to apply a filter that enhances architectural plans by converting them into a high-contrast edge-detected version. This tool performs grayscale conversion and applies Sobel edge detection, emphasizing lines and structures present in the original images. Users can adjust parameters such as edge detection thresholds and whether to invert colors, making it suitable for creating crisp and clear representations of architectural designs. This tool is particularly useful for architects, engineers, and designers looking to present or analyze technical drawings and architectural sketches.

Leave a Reply

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