Please bookmark this page to avoid losing your image tool!

Image Indian Miniature 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, saturation = 1.8, contrast = 1.4, warmth = 0.2, outlineColor = "#201005", outlineStrength = 0.3, posterizeLevels = 0) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const w = originalImg.naturalWidth || originalImg.width;
    const h = originalImg.naturalHeight || originalImg.height;

    if (w === 0 || h === 0) {
        console.error("Image has zero width or height. Ensure the image is loaded before processing.");
        // Return a minimal canvas or handle as an error
        canvas.width = 1; 
        canvas.height = 1;
        return canvas;
    }
    canvas.width = w;
    canvas.height = h;

    // 1. Initial draw of the original image
    ctx.drawImage(originalImg, 0, 0, w, h);

    // 2. Apply base filters (saturation, contrast) using ctx.filter
    // These filters affect subsequent drawing operations.
    let filterString = "";
    if (saturation !== 1.0) { // Default saturation is 1
        filterString += `saturate(${saturation}) `;
    }
    if (contrast !== 1.0) { // Default contrast is 1
        filterString += `contrast(${contrast}) `;
    }
    
    if (filterString.trim() !== "") {
        ctx.filter = filterString.trim();
        // Redraw the image (which is currently on the canvas) onto itself with the filter applied.
        // This effectively filters the existing canvas content.
        ctx.drawImage(canvas, 0, 0, w, h);
        ctx.filter = 'none'; // Reset filter for any subsequent direct drawing
    }

    // 3. Posterization
    // Reduces the number of colors in the image.
    // posterizeLevels = 0 or 1 means no posterization.
    if (posterizeLevels > 1 && posterizeLevels <= 256) {
        const imageData = ctx.getImageData(0, 0, w, h);
        const data = imageData.data;
        // Ensure num_levels is an integer between 2 and 255.
        const num_levels = Math.max(2, Math.min(255, Math.floor(posterizeLevels)));
        const step = 255 / (num_levels - 1);

        for (let i = 0; i < data.length; i += 4) {
            data[i] = Math.round(data[i] / step) * step;     // Red
            data[i+1] = Math.round(data[i+1] / step) * step; // Green
            data[i+2] = Math.round(data[i+2] / step) * step; // Blue
            // Alpha (data[i+3]) remains unchanged
        }
        ctx.putImageData(imageData, 0, 0);
    }

    // 4. Warmth overlay
    // Applies a warm color tint using overlay blending.
    if (warmth > 0) {
        ctx.globalCompositeOperation = 'overlay'; 
        const warmR = 230, warmG = 180, warmB = 100; // An ochre/golden yellow color
        // Clamp warmth value to [0, 1] for alpha
        const effectiveWarmth = Math.min(1, Math.max(0, warmth));
        ctx.fillStyle = `rgba(${warmR}, ${warmG}, ${warmB}, ${effectiveWarmth})`;
        ctx.fillRect(0, 0, w, h);
        ctx.globalCompositeOperation = 'source-over'; // Reset composite operation
    }
    
    // 5. Outlines
    // Detects edges and draws them in the specified outlineColor.
    if (outlineStrength > 0) {
        const tempCanvas = document.createElement('canvas');
        const tempCtx = tempCanvas.getContext('2d');
        tempCanvas.width = w;
        tempCanvas.height = h;

        // Draw current state of main canvas (with all previous effects) to temp canvas for processing
        tempCtx.drawImage(canvas, 0, 0, w, h);
        const imageData = tempCtx.getImageData(0, 0, w, h);
        const pixels = imageData.data; // Pixel data from the image with effects
        
        // This will store the edge mask (RGB will be black, Alpha will indicate edge presence)
        const outlinePixelData = new Uint8ClampedArray(pixels.length);

        const getLuminance = (data, idx) => 0.299 * data[idx] + 0.587 * data[idx+1] + 0.114 * data[idx+2];
        
        // Clamp outlineStrength to [0, 1] for calculations
        const effectiveOutlineStrength = Math.min(1, Math.max(0, outlineStrength));
        
        // Adjust edge detection threshold based on outlineStrength
        // Higher strength means a lower threshold, thus more edges are detected.
        const baseEdgeThreshold = 75; // Max threshold (for low strength)
        const minEdgeThreshold = 15;  // Min threshold (for high strength)
        const actualThreshold = minEdgeThreshold + (baseEdgeThreshold - minEdgeThreshold) * (1 - effectiveOutlineStrength);

        // Iterate over pixels, skipping borders to avoid out-of-bounds access
        for (let y = 1; y < h - 1; y++) {
            for (let x = 1; x < w - 1; x++) {
                const i = (y * w + x) * 4; // Index of current pixel's R channel
                
                const lumCurrent = getLuminance(pixels, i);
                const lumRight = getLuminance(pixels, i + 4);         // Pixel to the right
                const lumDown = getLuminance(pixels, i + w * 4);    // Pixel below
                
                // Simple gradient calculation
                const Gx = Math.abs(lumCurrent - lumRight);
                const Gy = Math.abs(lumCurrent - lumDown);
                const grad = Gx + Gy; // Sum of absolute differences

                if (grad > actualThreshold) {
                    outlinePixelData[i+3] = 255; // Set alpha to full for edge pixels
                } else {
                    outlinePixelData[i+3] = 0;   // Transparent for non-edge pixels
                }
                // RGB values of outlinePixelData remain 0 (black) as initialized
            }
        }
        
        const edgeImageData = new ImageData(outlinePixelData, w, h);
        
        tempCtx.clearRect(0,0,w,h); // Clear the temporary canvas
        tempCtx.putImageData(edgeImageData, 0, 0); // Draw the edge mask (black areas with alpha representing edges)

        // Color the mask: Fill with outlineColor, but only where alpha (from edgeImageData) is non-zero
        tempCtx.globalCompositeOperation = 'source-in'; 
        tempCtx.fillStyle = outlineColor;
        tempCtx.fillRect(0, 0, w, h);
        tempCtx.globalCompositeOperation = 'source-over'; // Reset composite operation

        // Draw the colored outlines from tempCanvas onto the main canvas
        // The opacity of this outline layer is controlled by effectiveOutlineStrength
        ctx.globalAlpha = effectiveOutlineStrength; 
        ctx.drawImage(tempCanvas, 0, 0);
        ctx.globalAlpha = 1.0; // Reset globalAlpha
    }

    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 Indian Miniature Filter Effect Tool allows users to apply a distinct artistic effect to their images, reminiscent of traditional Indian miniature paintings. This tool enhances the image by adjusting saturation, contrast, and warmth, while also allowing for posterization to reduce color depth. It features an outline effect to emphasize edges, making images appear more illustrative. This tool can be used for enhancing personal photographs, creating unique visual artwork, or preparing images for graphic design projects, social media, and marketing materials.

Leave a Reply

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