Please bookmark this page to avoid losing your image tool!

Image Background Replacement With Realistic Shadow 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.
async function processImage(originalImg, newBgColor = "transparent", shadowOffsetX = 10, shadowOffsetY = 10, shadowBlur = 15, shadowColor = "rgba(0,0,0,0.5)") {

    // Helper to ensure MediaPipe script is loaded and segmenter is initialized
    // This is defined outside processImage to act as a singleton loader/initializer.
    // Encapsulating it inside an immediately-invoked function expression (IIFE)
    // or a class would be cleaner in a larger application, but for a single function,
    // module-scoped variables are common.
    if (typeof window.__mediaPipeHelper === 'undefined') {
        window.__mediaPipeHelper = {
            initializationPromise: null,
            segmenterInstance: null,
            async ensureMediaPipeIsReady() {
                if (this.segmenterInstance) {
                    return this.segmenterInstance;
                }

                if (!this.initializationPromise) {
                    this.initializationPromise = (async () => {
                        try {
                            const { SelfieSegmentation } = await import('https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js');
                            
                            const segmenter = new SelfieSegmentation({
                                locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/${file}`
                            });

                            segmenter.setOptions({
                                modelSelection: 0, // 0 for general model (faster, broader poses, 256x256 input)
                                                   // 1 for landscape model (more accurate for full body, 144x256 input)
                            });
                            
                            // Model loading is often implicit on the first .send() call.
                            // An explicit await segmenter.initialize(); is not standard for this version.
                            this.segmenterInstance = segmenter;
                            return segmenter;
                        } catch (e) {
                            console.error("Failed to load or initialize MediaPipe SelfieSegmentation:", e);
                            this.initializationPromise = null; // Reset promise so it can be retried
                            throw e;
                        }
                    })();
                }
                return this.initializationPromise;
            }
        };
    }


    const width = originalImg.naturalWidth || originalImg.width;
    const height = originalImg.naturalHeight || originalImg.height;

    if (width === 0 || height === 0) {
        console.error("Original image has zero width or height. Ensure the image is fully loaded and valid before calling processImage.");
        // Create a small canvas indicating an error
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = Math.max(100, width); // Use original width if > 100, else 100
        errorCanvas.height = Math.max(30, height); // Use original height if > 30, else 30
        const errorCtx = errorCanvas.getContext('2d');
        errorCtx.fillStyle = "lightcoral";
        errorCtx.fillRect(0,0,errorCanvas.width, errorCanvas.height);
        errorCtx.font = "12px Arial";
        errorCtx.fillStyle = "black";
        errorCtx.textAlign = "center";
        errorCtx.fillText("Image Error", errorCanvas.width / 2, errorCanvas.height / 2 + 5);
        return errorCanvas;
    }

    // Canvas for the final result
    const resultCanvas = document.createElement('canvas');
    resultCanvas.width = width;
    resultCanvas.height = height;
    const resultCtx = resultCanvas.getContext('2d');

    try {
        const segmenter = await window.__mediaPipeHelper.ensureMediaPipeIsReady();
        
        // originalImg must be a loaded HTMLImageElement, HTMLVideoElement, or HTMLCanvasElement.
        // Caller should ensure originalImg.complete is true if it's an HTMLImageElement.
        const segmentation = await segmenter.send({ image: originalImg });

        if (!segmentation || !segmentation.segmentationMask) {
            console.error("Segmentation failed: No mask returned from MediaPipe.");
            resultCtx.drawImage(originalImg, 0, 0, width, height); // Draw original as fallback
            return resultCanvas;
        }

        // segmentation.segmentationMask is an HTMLCanvasElement containing the mask.
        const maskOutputCanvas = segmentation.segmentationMask;

        // Create a canvas for the person (foreground) with a transparent background
        const personCanvas = document.createElement('canvas');
        personCanvas.width = width;
        personCanvas.height = height;
        const personCtx = personCanvas.getContext('2d');

        // 1. Draw the original image onto the personCanvas.
        personCtx.drawImage(originalImg, 0, 0, width, height);
        // 2. Apply the mask: 'destination-in' keeps pixels from originalImg where maskOutputCanvas is opaque.
        personCtx.globalCompositeOperation = 'destination-in';
        personCtx.drawImage(maskOutputCanvas, 0, 0, width, height);
        personCtx.globalCompositeOperation = 'source-over'; // Reset composite operation.

        // Create Shadow Layer. The shadow is based on the silhouette from the mask.
        const shadowShapeCanvas = document.createElement('canvas');
        shadowShapeCanvas.width = width;
        shadowShapeCanvas.height = height;
        const shadowShapeCtx = shadowShapeCanvas.getContext('2d');

        // 1. Draw the maskOutputCanvas onto shadowShapeCanvas. This provides the silhouette.
        shadowShapeCtx.drawImage(maskOutputCanvas, 0, 0, width, height);
        // 2. Color the opaque parts of this mask with shadowColor.
        // 'source-in': draws the new shape (fillRect) only where the existing content (maskOutputCanvas) is opaque.
        shadowShapeCtx.globalCompositeOperation = 'source-in';
        shadowShapeCtx.fillStyle = shadowColor;
        shadowShapeCtx.fillRect(0, 0, width, height);
        shadowShapeCtx.globalCompositeOperation = 'source-over'; // Reset.

        // Create a canvas for the (potentially) blurred shadow. Necessary because filter applies to whole canvas.
        const finalShadowCanvas = document.createElement('canvas');
        finalShadowCanvas.width = width;
        finalShadowCanvas.height = height;
        const finalShadowCtx = finalShadowCanvas.getContext('2d');

        if (shadowBlur > 0) {
            finalShadowCtx.filter = `blur(${shadowBlur}px)`;
        }
        finalShadowCtx.drawImage(shadowShapeCanvas, 0, 0); // Draw the colored silhouette.
        if (shadowBlur > 0) { // Reset filter on this context if it was applied.
            finalShadowCtx.filter = 'none';
        }
        
        // --- Composite layers onto the resultCanvas ---

        // 1. Fill with new background color.
        // Canvas fillStyle 'transparent' or 'rgba(...,0)' is handled correctly by fillRect,
        // resulting in transparent pixels.
        resultCtx.fillStyle = newBgColor;
        resultCtx.fillRect(0, 0, width, height);

        // 2. Draw Shadow (offset).
        // The finalShadowCanvas contains the shadow shape, centered. We draw it offset.
        resultCtx.drawImage(finalShadowCanvas, shadowOffsetX, shadowOffsetY);

        // 3. Draw Person (foreground).
        resultCtx.drawImage(personCanvas, 0, 0);

        return resultCanvas;

    } catch (error) {
        console.error("Error during image processing with MediaPipe:", error);
        // Fallback: Clear canvas and draw original image if anything critical fails.
        try {
            resultCtx.clearRect(0,0,width,height); // Clear any partial drawing
            resultCtx.drawImage(originalImg, 0, 0, width, height);
        } catch (e) {
            // If even drawing original image fails (e.g., originalImg is invalid).
            console.error("Fallback drawing of original image also failed:", e);
            // resultCanvas might be blank or partially drawn. Could draw an error message here too.
        }
        return resultCanvas; // Return the canvas, possibly with original image or error state.
    }
}

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 Background Replacement With Realistic Shadow Tool allows users to seamlessly remove the background from images and replace it with a new background color of their choice. This tool generates realistic shadows based on the segmented image, enhancing the integration of the subject with its new background. It is particularly useful for graphic designers, marketers, and social media managers who want to create visually appealing images for advertisements, website banners, or personal projects. Users can adjust shadow properties to achieve the desired effect, making their compositions look more professional.

Leave a Reply

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