Please bookmark this page to avoid losing your image tool!

Image Batik Filter Application

(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, posterizeLevels = 6, edgeThreshold = 80, edgeColorStr = "#3A271C", edgeThickness = 1) {
    let width = originalImg.naturalWidth || originalImg.width;
    let height = originalImg.naturalHeight || originalImg.height;

    // Handle cases where the image might not be fully loaded yet (common for HTMLImageElement)
    if ((!width || !height) && (originalImg instanceof HTMLImageElement && !originalImg.complete && originalImg.src)) {
        try {
            await new Promise((resolve, reject) => {
                const loadHandler = () => {
                    originalImg.removeEventListener('load', loadHandler);
                    originalImg.removeEventListener('error', errorHandler);
                    resolve();
                };
                const errorHandler = (err) => {
                    originalImg.removeEventListener('load', loadHandler);
                    originalImg.removeEventListener('error', errorHandler);
                    reject(err);
                };
                originalImg.addEventListener('load', loadHandler);
                originalImg.addEventListener('error', errorHandler);
                // If image already has src and loading has started, these listeners will catch it.
                // If src is set *after* this function call, this won't help unless processImage is called again.
            });
            width = originalImg.naturalWidth;
            height = originalImg.naturalHeight;
        } catch (e) {
            console.error("Error loading image:", e);
            // Fall through to create a minimal error canvas
        }
    }
    
    // If dimensions are still invalid, return a minimal canvas
    if (!width || !height) {
        console.error("Image has invalid dimensions or failed to load.");
        const errCanvas = document.createElement('canvas');
        errCanvas.width = 1; errCanvas.height = 1;
        const errCtx = errCanvas.getContext('2d');
        if (errCtx) { // Make it visibly an error if possible
            errCtx.fillStyle = 'red';
            errCtx.fillRect(0,0,1,1);
        }
        return errCanvas;
    }

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

    ctx.drawImage(originalImg, 0, 0, width, height);

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

    // 1. Posterize Image
    const posterizedImageData = ctx.createImageData(width, height);
    const posterizedData = posterizedImageData.data;
    
    const numLevels = Math.max(2, Math.min(255, Math.floor(posterizeLevels)));
    const step = (numLevels <= 1) ? 256 : 255 / (numLevels - 1); // Avoid division by zero if numLevels is 1

    for (let i = 0; i < data.length; i += 4) {
        if (numLevels <= 1) { // effectively no change or single color (though Math.max(2,..) prevents this)
             posterizedData[i]   = data[i];
             posterizedData[i+1] = data[i+1];
             posterizedData[i+2] = data[i+2];
        } else {
             posterizedData[i]   = Math.min(255, Math.round(data[i] / step) * step);
             posterizedData[i+1] = Math.min(255, Math.round(data[i+1] / step) * step);
             posterizedData[i+2] = Math.min(255, Math.round(data[i+2] / step) * step);
        }
        posterizedData[i+3] = data[i+3]; // Preserve alpha
    }

    // 2. Grayscale Data for Edge Detection
    const grayData = 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];
        grayData[i / 4] = 0.299 * r + 0.587 * g + 0.114 * b; // Luminosity
    }

    // 3. Sobel Edge Detection
    const edgeMap = new Uint8Array(width * height).fill(0);
    const Gx = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];
    const Gy = [
        [-1, -2, -1],
        [0,  0,  0],
        [1,  2,  1]
    ];
    const clampedEdgeThreshold = Math.max(0, Math.min(255, edgeThreshold));

    for (let y = 1; y < height - 1; y++) {
        for (let x = 1; x < width - 1; x++) {
            let sumX = 0;
            let sumY = 0;
            for (let ky = -1; ky <= 1; ky++) {
                for (let kx = -1; kx <= 1; kx++) {
                    const pixelVal = grayData[(y + ky) * width + (x + kx)];
                    sumX += pixelVal * Gx[ky + 1][kx + 1];
                    sumY += pixelVal * Gy[ky + 1][kx + 1];
                }
            }
            const magnitude = Math.sqrt(sumX * sumX + sumY * sumY);
            if (magnitude > clampedEdgeThreshold) {
                edgeMap[y * width + x] = 1;
            }
        }
    }

    // 4. Combine on output canvas
    // Draw posterized image first
    ctx.putImageData(posterizedImageData, 0, 0);

    // Draw edges on top
    ctx.fillStyle = edgeColorStr;
    const finalEdgeThickness = Math.max(1, Math.floor(edgeThickness));
    
    for (let y = 0; y < height; y++) {
        for (let x = 0; x < width; x++) {
            if (edgeMap[y * width + x] === 1) {
                // Center the thicker line block around the detected edge pixel
                const rectX = x - Math.floor(finalEdgeThickness / 2);
                const rectY = y - Math.floor(finalEdgeThickness / 2);
                ctx.fillRect(rectX, rectY, finalEdgeThickness, finalEdgeThickness);
            }
        }
    }
    
    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 Batik Filter Application is a tool that applies a unique batik-style filter to images. It allows users to posterize the image into a specified number of levels, enhancing colors in a bold, artistic manner. Additionally, it utilizes edge detection to outline features in the image, creating striking visual contrasts. This tool can be used for artistic projects, graphic design, or to transform photos into a stylized format suitable for prints, digital art, or creative presentations.

Leave a Reply

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