Please bookmark this page to avoid losing your image tool!

JPG Main Background Color Remover To Transparent PNG Converter

(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, tolerance = 40, feather = 20, mode = "contiguous", targetColor = "auto") {
    // Create a canvas to process the image data
    const canvas = document.createElement("canvas");
    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;
    
    canvas.width = width;
    canvas.height = height;
    
    const ctx = canvas.getContext("2d", { willReadFrequently: true });
    ctx.drawImage(originalImg, 0, 0, width, height);
    
    // Extract the image data
    const imgData = ctx.getImageData(0, 0, width, height);
    const data = imgData.data;
    
    let bgR = 0, bgG = 0, bgB = 0;

    // Determine target background color
    if (String(targetColor).trim().toLowerCase() === "auto") {
        // Automatically detect the most frequent background color on the borders
        const buckets = new Map();
        const bucketSize = 15;
        
        const addColorToBucket = (r, g, b) => {
            const br = Math.round(r / bucketSize) * bucketSize;
            const bg = Math.round(g / bucketSize) * bucketSize;
            const bb = Math.round(b / bucketSize) * bucketSize;
            const key = `${br},${bg},${bb}`;
            
            if (!buckets.has(key)) {
                buckets.set(key, { count: 0, r: 0, g: 0, b: 0 });
            }
            
            const bkt = buckets.get(key);
            bkt.count++;
            bkt.r += r;
            bkt.g += g;
            bkt.b += b;
        };

        // Sample edges
        for (let x = 0; x < width; x++) {
            let idxTop = x * 4;
            addColorToBucket(data[idxTop], data[idxTop+1], data[idxTop+2]);
            let idxBot = ((height - 1) * width + x) * 4;
            addColorToBucket(data[idxBot], data[idxBot+1], data[idxBot+2]);
        }
        for (let y = 1; y < height - 1; y++) {
            let idxLeft = (y * width) * 4;
            addColorToBucket(data[idxLeft], data[idxLeft+1], data[idxLeft+2]);
            let idxRight = (y * width + (width - 1)) * 4;
            addColorToBucket(data[idxRight], data[idxRight+1], data[idxRight+2]);
        }

        let maxCount = 0;
        let bestBkt = null;
        for (const bkt of buckets.values()) {
            if (bkt.count > maxCount) {
                maxCount = bkt.count;
                bestBkt = bkt;
            }
        }
        
        // Exact average of the most prominent bucket color handles JPG compression noise well
        bgR = Math.round(bestBkt.r / maxCount);
        bgG = Math.round(bestBkt.g / maxCount);
        bgB = Math.round(bestBkt.b / maxCount);
    } else {
        // Parse hex color if manually provided (e.g. "#FFFFFF")
        let hex = String(targetColor).replace(/^#/, '');
        if (hex.length === 3) {
            hex = hex.split('').map(c => c + c).join('');
        }
        bgR = parseInt(hex.substring(0, 2), 16) || 0;
        bgG = parseInt(hex.substring(2, 4), 16) || 0;
        bgB = parseInt(hex.substring(4, 6), 16) || 0;
    }

    // Function to calculate Euclidean distance between a pixel and target color
    const colorMatchDist = (r, g, b) => {
        return Math.sqrt((r - bgR) ** 2 + (g - bgG) ** 2 + (b - bgB) ** 2);
    };

    const parsedTolerance = Number(tolerance);
    const parsedFeather = Number(feather);
    const maxDist = parsedTolerance + parsedFeather;

    if (String(mode).trim().toLowerCase() === "global") {
        // Global Replacement: Replaces all matching colors uniformly regardless of location
        for (let i = 0; i < data.length; i += 4) {
            const dist = colorMatchDist(data[i], data[i+1], data[i+2]);
            if (dist <= parsedTolerance) {
                data[i+3] = 0; // Fully transparent
            } else if (dist <= maxDist && parsedFeather > 0) {
                // Anti-Aliasing (Feathering) the edges for a smoother look
                const ratio = (dist - parsedTolerance) / parsedFeather;
                data[i+3] = Math.min(data[i+3], Math.round(ratio * 255));
            }
        }
    } else {
        // Contiguous (Flood Fill): Replaces matching background connected to the borders
        const visited = new Uint8Array(width * height);
        const stack = [];

        const checkAndPush = (x, y) => {
            const idx = y * width + x;
            if (!visited[idx]) {
                visited[idx] = 1;
                const pIdx = idx * 4;
                const dist = colorMatchDist(data[pIdx], data[pIdx+1], data[pIdx+2]);
                
                if (dist <= parsedTolerance) {
                    data[pIdx+3] = 0; // Fully transparent
                    stack.push(x, y); 
                } else if (dist <= maxDist && parsedFeather > 0) {
                    const ratio = (dist - parsedTolerance) / parsedFeather;
                    data[pIdx+3] = Math.min(data[pIdx+3], Math.round(ratio * 255));
                    // Intentionally NOT pushing onto the stack because it acts as the soft boundary edge
                }
            }
        };

        // Initialize the stack by checking the borders
        for (let x = 0; x < width; x++) {
            checkAndPush(x, 0);
            checkAndPush(x, height - 1);
        }
        for (let y = 1; y < height - 1; y++) {
            checkAndPush(0, y);
            checkAndPush(width - 1, y);
        }

        // Iterative DFS stack based fill to prevent call-stack exceeded errors
        while (stack.length > 0) {
            const y = stack.pop();
            const x = stack.pop();

            if (x > 0) checkAndPush(x - 1, y);
            if (x < width - 1) checkAndPush(x + 1, y);
            if (y > 0) checkAndPush(x, y - 1);
            if (y < height - 1) checkAndPush(x, y + 1);
        }
    }

    // Apply modifying data
    ctx.putImageData(imgData, 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

This tool allows you to remove the background color from JPG images and convert them into transparent PNG files. It can automatically detect the dominant background color from the edges of your image or allow you to specify a custom target color. With features like color tolerance and edge feathering, you can achieve smooth, professional-looking cutouts. This tool is ideal for graphic designers, e-commerce sellers needing product photos with transparent backgrounds, or anyone looking to create overlays and stickers for digital projects.

Leave a Reply

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