Please bookmark this page to avoid losing your image tool!

Image Position Finder Between Two Images

(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, searchImgUrl = "", highlightColor = "red", highlightLineWidth = 2) {
    const outputCanvas = document.createElement('canvas');
    const initialErrorState = {
        width: 300,
        height: 100,
        message: "An unknown error occurred.",
        logToConsole: false,
        consoleMsg: ""
    };

    if (!originalImg || typeof originalImg.width === 'undefined' || originalImg.naturalWidth === 0) {
        initialErrorState.message = "Error: Original image is not valid or not loaded properly.";
        outputCanvas.width = initialErrorState.width;
        outputCanvas.height = initialErrorState.height;
        const ctx = outputCanvas.getContext('2d');
        ctx.fillStyle = "#f0f0f0";
        ctx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
        ctx.fillStyle = "black";
        ctx.font = "14px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText(initialErrorState.message, outputCanvas.width / 2, outputCanvas.height / 2);
        return outputCanvas;
    }

    outputCanvas.width = originalImg.width;
    outputCanvas.height = originalImg.height;
    const ctxOutput = outputCanvas.getContext('2d');
    try {
        ctxOutput.drawImage(originalImg, 0, 0);
    } catch (e) {
        // This can happen if originalImg itself is from a tainted canvas
        initialErrorState.message = "Error: Could not draw original image. It might be tainted.";
        initialErrorState.logToConsole = true;
        initialErrorState.consoleMsg = e.message;
        // Fallback to drawing error on potentially resized canvas
        outputCanvas.width = initialErrorState.width; // Reset to default error size
        outputCanvas.height = initialErrorState.height;
        // ctxOutput is already obtained, just re-clear and draw error
        ctxOutput.fillStyle = "#f0f0f0";
        ctxOutput.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
        ctxOutput.fillStyle = "black";
        ctxOutput.font = "14px Arial";
        ctxOutput.textAlign = "center";
        ctxOutput.textBaseline = "middle";
        ctxOutput.fillText(initialErrorState.message, outputCanvas.width / 2, outputCanvas.height / 2);
        if(initialErrorState.logToConsole) console.error(initialErrorState.message, initialErrorState.consoleMsg);
        return outputCanvas;
    }
    

    const ow = originalImg.width;
    const oh = originalImg.height;

    function drawMessageOnCanvas(message) {
        // Use outputCanvas, originalImg already drawn
        ctxOutput.fillStyle = highlightColor;
        const fontSize = Math.min(16, Math.max(10, Math.floor(ow / 25)));
        ctxOutput.font = `${fontSize}px Arial`;
        ctxOutput.textAlign = "center";
        ctxOutput.textBaseline = "top";
        const textYPosition = Math.min(oh * 0.1, 20); // Position text near top
        ctxOutput.fillText(message, ow / 2, textYPosition);
    }

    if (!searchImgUrl || searchImgUrl.trim() === "") {
        drawMessageOnCanvas("Search image URL not provided.");
        return outputCanvas;
    }

    let searchImg;
    try {
        searchImg = await new Promise((resolve, reject) => {
            const img = new Image();
            img.crossOrigin = "Anonymous"; // Important for getImageData if URL is cross-origin
            img.onload = () => resolve(img);
            img.onerror = () => reject(new Error("Failed to load search image from URL."));
            img.src = searchImgUrl;
        });
    } catch (error) {
        drawMessageOnCanvas(error.message);
        console.error(error);
        return outputCanvas;
    }

    if (!searchImg.width || !searchImg.height || searchImg.naturalWidth === 0) {
        drawMessageOnCanvas("Search image is invalid (e.g., 0 dimensions or failed to decode).");
        return outputCanvas;
    }

    const sw = searchImg.width;
    const sh = searchImg.height;

    if (sw > ow || sh > oh) {
        drawMessageOnCanvas(`Search image (${sw}x${sh}) is larger than original image (${ow}x${oh}). Not found.`);
        return outputCanvas;
    }

    // Get image data for comparison
    let originalImageData, searchImageData;
    try {
        const tempOriginalCanvas = document.createElement('canvas');
        tempOriginalCanvas.width = ow;
        tempOriginalCanvas.height = oh;
        const tempOriginalCtx = tempOriginalCanvas.getContext('2d', { willReadFrequently: true });
        tempOriginalCtx.drawImage(originalImg, 0, 0);
        originalImageData = tempOriginalCtx.getImageData(0, 0, ow, oh);
    } catch (e) {
        // Handle potential CROS issue with originalImg if not caught by initial drawImage
        ctxOutput.clearRect(0, 0, ow, oh); // Clear drawn originalImg
        ctxOutput.fillStyle = "#f0f0f0";
        ctxOutput.fillRect(0, 0, ow, oh); // Background for error
        ctxOutput.fillStyle = "black";
        const fontSize = Math.min(14, Math.max(10, Math.floor(ow / 30)));
        ctxOutput.font = `${fontSize}px Arial`;
        ctxOutput.textAlign = "center";
        ctxOutput.textBaseline = "middle";
        let msg = "Error: Original image cannot be processed. Possible cross-origin issue.";
        if (originalImg.src && !originalImg.src.startsWith("data:") && (!originalImg.crossOrigin || originalImg.crossOrigin.toLowerCase() !== "anonymous")) {
             msg = `Error: Original image from '${new URL(originalImg.src).hostname}' needs 'crossOrigin="anonymous"' attribute set *before* loading, or server must provide permissive CORS headers.`;
        }
        // Basic text wrapping
        const words = msg.split(' ');
        let line = '';
        let textBlockY = oh/2 - ( (Math.ceil(msg.length / (ow*0.8 / (fontSize*0.6)))) * fontSize )/2 ; // Estimate Y start

        for(let n = 0; n < words.length; n++) {
            const testLine = line + words[n] + ' ';
            const metrics = ctxOutput.measureText(testLine);
            const testWidth = metrics.width;
            if (testWidth > ow * 0.9 && n > 0) {
                ctxOutput.fillText(line, ow / 2, textBlockY);
                line = words[n] + ' ';
                textBlockY += fontSize * 1.2; // Line height
            } else {
                line = testLine;
            }
        }
        ctxOutput.fillText(line, ow/2, textBlockY);

        console.error("Error getting original image data:", e);
        return outputCanvas;
    }

    try {
        const tempSearchCanvas = document.createElement('canvas');
        tempSearchCanvas.width = sw;
        tempSearchCanvas.height = sh;
        const tempSearchCtx = tempSearchCanvas.getContext('2d', { willReadFrequently: true });
        tempSearchCtx.drawImage(searchImg, 0, 0);
        searchImageData = tempSearchCtx.getImageData(0, 0, sw, sh);
    } catch (e) {
        // This could happen if server providing searchImgUrl doesn't send CORS headers
        // despite img.crossOrigin = "Anonymous"
        drawMessageOnCanvas("Error processing search image. Possible CORS issue with search image server.");
        console.error("Error getting search image data:", e);
        return outputCanvas;
    }
    
    const originalData = originalImageData.data;
    const searchData = searchImageData.data;
    let foundX = -1, foundY = -1;

    // Search loop
    for (let y = 0; y <= oh - sh; y++) {
        for (let x = 0; x <= ow - sw; x++) {
            let match = true;
            for (let sy = 0; sy < sh; sy++) {
                for (let sx = 0; sx < sw; sx++) {
                    const originalPixelStartIndex = ((y + sy) * ow + (x + sx)) * 4;
                    const searchPixelStartIndex = (sy * sw + sx) * 4;
                    if (originalData[originalPixelStartIndex]     !== searchData[searchPixelStartIndex] ||     // R
                        originalData[originalPixelStartIndex + 1] !== searchData[searchPixelStartIndex + 1] || // G
                        originalData[originalPixelStartIndex + 2] !== searchData[searchPixelStartIndex + 2] || // B
                        originalData[originalPixelStartIndex + 3] !== searchData[searchPixelStartIndex + 3]) {  // A
                        match = false;
                        break;
                    }
                }
                if (!match) break;
            }
            if (match) {
                foundX = x;
                foundY = y;
                break;
            }
        }
        if (foundX !== -1) break;
    }

    // Draw result (originalImg is already on ctxOutput)
    if (foundX !== -1 && foundY !== -1) {
        ctxOutput.strokeStyle = highlightColor;
        ctxOutput.lineWidth = highlightLineWidth;
        ctxOutput.strokeRect(foundX, foundY, sw, sh);

        const foundTextFontSize = Math.max(10, Math.min(16, Math.floor(sw / 6), Math.floor(ow / 30)));
        ctxOutput.font = `${foundTextFontSize}px Arial`;
        ctxOutput.fillStyle = highlightColor;

        const text = `Found: (${foundX},${foundY})`;
        let tx = foundX;
        let ty = foundY - 5 - highlightLineWidth; 
        ctxOutput.textAlign = 'left';
        ctxOutput.textBaseline = 'bottom';

        if (ty < foundTextFontSize) { 
            ty = foundY + sh + 5 + highlightLineWidth + foundTextFontSize;
            ctxOutput.textBaseline = 'top';
        }
        
        if (ty < foundTextFontSize || ty > oh ) { 
            ty = oh / 2; 
            ctxOutput.textBaseline = 'middle';
        }
        
        const textWidth = ctxOutput.measureText(text).width;
        if (tx + textWidth + 5 > ow) {
            tx = Math.max(5, ow - textWidth - 5); 
        }
        if (tx < 5) {
            tx = 5;
        }
        ctxOutput.fillText(text, tx, ty);

    } else {
        drawMessageOnCanvas("Search image not found within original image.");
    }

    return outputCanvas;
}

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 Position Finder Between Two Images is a useful tool that allows users to locate the position of a smaller image (search image) within a larger original image. This tool is ideal for scenarios such as image processing, where an aspect of an image needs to be identified, or for developers and designers who need to verify image compositions and layouts. Users can provide an original image and a search image through a URL, and the tool will highlight and indicate the coordinates of the search image if found within the original. This is particularly beneficial in tasks related to graphic design, computer vision, and image analysis.

Leave a Reply

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