Please bookmark this page to avoid losing your image tool!

Image Persian Miniature Painting Creator

(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, paletteColorsStr = "00529B,40E0D0,C8143C,008000,DAA520,F5DCC4,8B4513,FFFAE6", outlineColorHex = "361E14", outlineThreshold = 50, posterizeLevels = 5) {
    // 1. Canvas Setup
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    
    // Use naturalWidth/Height for <img> elements, fallback to width/height for Image objects
    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    if (canvas.width === 0 || canvas.height === 0) {
        console.error("Image has zero width or height. Ensure the image is loaded before processing.");
        // Return an empty (or minimally sized) canvas to avoid errors downstream
        canvas.width = canvas.width || 1;
        canvas.height = canvas.height || 1;
        return canvas;
    }

    // Helper: Parse Hex to RGB
    function hexToRgb(hex) {
        hex = hex.replace(/^#/, '');
        if (hex.length === 3) {
            hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
        }
        const bigint = parseInt(hex, 16);
        if (isNaN(bigint)) {
            console.warn(`Invalid hex color: "${hex}", defaulting to black.`);
            return { r: 0, g: 0, b: 0 };
        }
        return { r: (bigint >> 16) & 255, g: (bigint >> 8) & 255, b: bigint & 255 };
    }

    // Helper: Color distance squared (avoids Math.sqrt for faster comparisons)
    function colorDistanceSquared(c1, c2) {
        return Math.pow(c1.r - c2.r, 2) + Math.pow(c1.g - c2.g, 2) + Math.pow(c1.b - c2.b, 2);
    }

    // Helper: Find closest color in palette
    function findClosestPaletteColor(rgb, palette) {
        if (!palette || palette.length === 0) {
            return rgb; // Should not happen with default and fallback
        }
        let closestColor = palette[0];
        let minDistance = colorDistanceSquared(rgb, closestColor);
        for (let i = 1; i < palette.length; i++) {
            const distance = colorDistanceSquared(rgb, palette[i]);
            if (distance < minDistance) {
                minDistance = distance;
                closestColor = palette[i];
            }
            if (minDistance === 0) break; // Exact match
        }
        return closestColor;
    }

    // Parse parameters
    let finalPalette = paletteColorsStr.split(',')
        .map(hex => hex.trim())
        .filter(hex => hex.length > 0)
        .map(hex => hexToRgb(hex));
    
    if (finalPalette.length === 0) {
        console.warn("Provided palette string resulted in an empty palette. Using a default internal palette.");
        // Fallback to simple black & white if parsing fails completely
        finalPalette = [{ r: 0, g: 0, b: 0 }, { r: 255, g: 255, b: 255 }];
    }

    const outlineRgb = hexToRgb(outlineColorHex);
    const pLevels = Math.max(2, Math.round(posterizeLevels)); // Ensure integer and at least 2 levels

    // --- Step 1: Color Quantization/Posterization and Palette Mapping ---
    const colorCanvas = document.createElement('canvas');
    const colorCtx = colorCanvas.getContext('2d', { willReadFrequently: true });
    colorCanvas.width = canvas.width;
    colorCanvas.height = canvas.height;
    colorCtx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    const imageData = colorCtx.getImageData(0, 0, colorCanvas.width, colorCanvas.height);
    const data = imageData.data;

    const posterizationFactor = (pLevels > 1) ? (255 / (pLevels - 1)) : 255; // Avoid division by zero if pLevels is 1 (though we enforced >=2)

    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i+1];
        let b = data[i+2];

        // Apply posterization
        if (pLevels > 1) {
            r = Math.round(Math.round(r / posterizationFactor) * posterizationFactor);
            g = Math.round(Math.round(g / posterizationFactor) * posterizationFactor);
            b = Math.round(Math.round(b / posterizationFactor) * posterizationFactor);
        }
        
        const closest = findClosestPaletteColor({ r, g, b }, finalPalette);
        data[i] = closest.r;
        data[i + 1] = closest.g;
        data[i + 2] = closest.b;
        // Alpha (data[i+3]) remains unchanged
    }
    colorCtx.putImageData(imageData, 0, 0);

    // --- Step 2: Edge Detection (Sobel on original grayscale) ---
    // Temporary canvas for original image data for grayscale conversion
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    tempCanvas.width = canvas.width;
    tempCanvas.height = canvas.height;
    tempCtx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    const originalImageData = tempCtx.getImageData(0, 0, canvas.width, canvas.height);
    
    const grayData = new Uint8ClampedArray(canvas.width * canvas.height); // Store grayscale values
    for (let i = 0; i < originalImageData.data.length; i += 4) {
        const r_orig = originalImageData.data[i];
        const g_orig = originalImageData.data[i+1];
        const b_orig = originalImageData.data[i+2];
        grayData[i / 4] = Math.round(0.299 * r_orig + 0.587 * g_orig + 0.114 * b_orig); // Luminance
    }

    // Canvas for drawing edges
    const edgeCanvas = document.createElement('canvas');
    const edgeCtx = edgeCanvas.getContext('2d');
    edgeCanvas.width = canvas.width;
    edgeCanvas.height = canvas.height;
    const edgePixelImageData = edgeCtx.createImageData(canvas.width, canvas.height);
    const edgePixelData = edgePixelImageData.data;

    const sobelX = [
        [-1, 0, 1],
        [-2, 0, 2],
        [-1, 0, 1]
    ];
    const sobelY = [
        [-1, -2, -1],
        [0,  0,  0],
        [1,  2,  1]
    ];

    for (let y = 1; y < canvas.height - 1; y++) { // Skip 1-pixel border
        for (let x = 1; x < canvas.width - 1; x++) { // Skip 1-pixel border
            let gx = 0;
            let gy = 0;
            for (let ky = -1; ky <= 1; ky++) {
                for (let kx = -1; kx <= 1; kx++) {
                    const pixelIndex = (y + ky) * canvas.width + (x + kx);
                    const pixelVal = grayData[pixelIndex];
                    gx += pixelVal * sobelX[ky + 1][kx + 1];
                    gy += pixelVal * sobelY[ky + 1][kx + 1];
                }
            }
            const magnitude = Math.sqrt(gx * gx + gy * gy);
            const idx = (y * canvas.width + x) * 4;

            if (magnitude > outlineThreshold) {
                edgePixelData[idx] = outlineRgb.r;
                edgePixelData[idx + 1] = outlineRgb.g;
                edgePixelData[idx + 2] = outlineRgb.b;
                edgePixelData[idx + 3] = 255; // Opaque outline
            } else {
                edgePixelData[idx + 3] = 0; // Transparent if not an edge
            }
        }
    }
    edgeCtx.putImageData(edgePixelImageData, 0, 0);

    // --- Step 3: Combine ---
    ctx.drawImage(colorCanvas, 0, 0); // Draw color-mapped image
    ctx.drawImage(edgeCanvas, 0, 0);  // Draw outlines on top

    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 Persian Miniature Painting Creator is an online tool that transforms standard images into Persian miniature-style artwork. It utilizes advanced processing techniques, including color quantization and edge detection, to create a visually appealing output. Users can customize the color palette and outline color, allowing for a unique artistic rendition of their original images. This tool is suitable for artists, designers, and anyone interested in creating digital art inspired by traditional Persian painting styles. Whether for personal projects, educational purposes, or professional design work, this tool provides an accessible way to explore and create distinctive art forms.

Leave a Reply

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

Other Image Tools:

Image Sci-Fi Movie Poster Template Creator

Image Horror Movie Poster Template

Image Social Media Milestone Certificate Creator

Halloween Death Certificate Template

Image Anatomical Illustration Frame Creator

Image Romance Novel Cover Template Creator

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

Image Birth Certificate Template Generator

Image Driver’s License Template Creator

Image Scout Explorer Badge Template Creator

Image Steampunk Document Frame Creator

Image Vintage Scientific Illustration Frame Creator

Image Magazine Cover Template Creator

Image Album Cover Template Creator

See All →