Please bookmark this page to avoid losing your image tool!

Image House Analysis 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, colorCount = 5, edgeThreshold = 40) {

    /**
     * Finds the most dominant colors in an image's pixel data using quantization.
     * @param {Uint8ClampedArray} pixels - The pixel data from ImageData.
     * @param {number} count - The number of dominant colors to return.
     * @returns {Array<Object>} An array of dominant color objects { rgb, hex, count }.
     */
    const getDominantColors = (pixels, count) => {
        const colorMap = new Map();
        // Quantization factor: higher value means fewer distinct colors and faster processing.
        const quant = 32;

        for (let i = 0; i < pixels.length; i += 4) {
            // Ignore transparent or semi-transparent pixels
            if (pixels[i + 3] < 128) {
                continue;
            }

            const r = Math.round(pixels[i] / quant) * quant;
            const g = Math.round(pixels[i + 1] / quant) * quant;
            const b = Math.round(pixels[i + 2] / quant) * quant;

            const key = `${r},${g},${b}`;
            colorMap.set(key, (colorMap.get(key) || 0) + 1);
        }

        const sortedColors = Array.from(colorMap.entries()).sort((a, b) => b[1] - a[1]);
        
        return sortedColors.slice(0, count).map(entry => {
            const [r, g, b] = entry[0].split(',').map(Number);
            // Helper to convert RGB component to a two-digit hex string
            const toHex = c => ('0' + c.toString(16)).slice(-2);
            const hex = `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
            return { rgb: `rgb(${r},${g},${b})`, hex: hex, count: entry[1] };
        });
    };

    /**
     * Calculates the brightness histogram for the image pixel data.
     * @param {Uint8ClampedArray} pixels - The pixel data from ImageData.
     * @returns {Array<number>} An array of 256 numbers representing the histogram.
     */
    const getBrightnessHistogram = (pixels) => {
        const histogram = new Array(256).fill(0);
        for (let i = 0; i < pixels.length; i += 4) {
             if (pixels[i + 3] < 128) continue;
            const r = pixels[i];
            const g = pixels[i + 1];
            const b = pixels[i + 2];
            // Using the luminosity formula for perceived brightness
            const brightness = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
            histogram[brightness]++;
        }
        return histogram;
    };
    
    /**
     * Creates a simple edge-detected image from source image data.
     * @param {ImageData} sourceImageData - The source image data to process.
     * @param {number} threshold - The sensitivity for edge detection.
     * @returns {ImageData} A new ImageData object containing the black and white edges.
     */
    const getEdgeData = (sourceImageData, threshold) => {
        const { width, height, data: sourceData } = sourceImageData;
        const edgeImageData = new ImageData(width, height);
        const edgeData = edgeImageData.data;

        const toGray = (r, g, b) => 0.299 * r + 0.587 * g + 0.114 * b;

        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                const i = (y * width + x) * 4;

                // Set image borders to white
                if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
                    edgeData[i] = edgeData[i + 1] = edgeData[i + 2] = 255;
                    edgeData[i + 3] = 255;
                    continue;
                }

                const currentGray = toGray(sourceData[i], sourceData[i + 1], sourceData[i + 2]);
                const rightGray = toGray(sourceData[i + 4], sourceData[i + 5], sourceData[i + 6]);
                const bottomGray = toGray(sourceData[i + (width * 4)], sourceData[i + (width * 4) + 1], sourceData[i + (width * 4) + 2]);

                // Calculate magnitude using a simple difference method
                const magnitude = Math.abs(currentGray - rightGray) + Math.abs(currentGray - bottomGray);
                
                const color = magnitude > threshold ? 0 : 255; // Black for edges, white for non-edges
                
                edgeData[i] = edgeData[i + 1] = edgeData[i + 2] = color;
                edgeData[i + 3] = 255;
            }
        }
        return edgeImageData;
    };

    // 1. Setup Canvas
    const w = originalImg.width;
    const h = originalImg.height;
    const analysisPaneWidth = Math.max(300, w * 0.5);
    const canvas = document.createElement('canvas');
    canvas.width = w + analysisPaneWidth;
    canvas.height = h;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    
    // 2. Draw background and original image
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(originalImg, 0, 0, w, h);
    ctx.fillStyle = '#f8f9fa';
    ctx.fillRect(w, 0, analysisPaneWidth, h);
    ctx.strokeStyle = '#dee2e6';
    ctx.lineWidth = 1;
    ctx.strokeRect(w, 0, analysisPaneWidth, h);

    // 3. Create a downscaled temporary canvas for faster analysis
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
    const scale = Math.min(200 / w, 200 / h, 1);
    tempCanvas.width = w * scale;
    tempCanvas.height = h * scale;
    tempCtx.drawImage(originalImg, 0, 0, tempCanvas.width, tempCanvas.height);
    const imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
    const pixels = imageData.data;

    // --- RENDER ANALYSIS PANE ---
    let currentY = 15;
    const leftMargin = w + 20;
    const contentWidth = analysisPaneWidth - 40;

    // A. Title
    ctx.fillStyle = '#212529';
    ctx.font = 'bold 18px "Segoe UI", Arial, sans-serif';
    ctx.fillText('Image Analysis Report', leftMargin, currentY + 18);
    currentY += 45;

    // B. Dominant Colors
    if (currentY + colorCount * 33 < h - 50) {
        ctx.font = 'bold 14px "Segoe UI", Arial, sans-serif';
        ctx.fillText('Dominant Colors', leftMargin, currentY);
        currentY += 25;
        const dominantColors = getDominantColors(pixels, colorCount);
        dominantColors.forEach(color => {
            ctx.fillStyle = color.hex;
            ctx.fillRect(leftMargin, currentY, 25, 25);
            ctx.font = '13px "Courier New", monospace';
            ctx.fillStyle = '#343a40';
            ctx.textBaseline = 'middle';
            ctx.fillText(color.hex, leftMargin + 35, currentY + 12.5);
            ctx.textBaseline = 'alphabetic';
            currentY += 33;
        });
        currentY += 10;
    }

    // C. Brightness Distribution
    if (currentY < h - 110) {
      ctx.fillStyle = '#212529';
      ctx.font = 'bold 14px "Segoe UI", Arial, sans-serif';
      ctx.fillText('Brightness Distribution', leftMargin, currentY);
      currentY += 25;
      const histogram = getBrightnessHistogram(pixels);
      const histHeight = 60;
      const histWidth = contentWidth;
      const maxHistValue = Math.max(...histogram);
      
      ctx.fillStyle = '#e9ecef';
      ctx.fillRect(leftMargin, currentY, histWidth, histHeight);
      
      ctx.strokeStyle = '#adb5bd';
      ctx.lineWidth = 1;
      ctx.beginPath();
      ctx.moveTo(leftMargin, currentY + histHeight);
      for(let i = 0; i < 256; i++) {
        const barHeight = (histogram[i] / maxHistValue) * histHeight;
        ctx.lineTo(leftMargin + (i / 255) * histWidth, currentY + histHeight - barHeight);
      }
      ctx.stroke();

      ctx.font = '11px "Segoe UI", Arial, sans-serif';
      ctx.fillStyle = '#6c757d';
      ctx.fillText('Dark', leftMargin, currentY + histHeight + 15);
      ctx.textAlign = 'right';
      ctx.fillText('Bright', leftMargin + histWidth, currentY + histHeight + 15);
      ctx.textAlign = 'left';
      currentY += histHeight + 30;
    }

    // D. Structural Outline
    if (currentY < h - 100) {
      ctx.fillStyle = '#212529';
      ctx.font = 'bold 14px "Segoe UI", Arial, sans-serif';
      ctx.fillText('Structural Outline', leftMargin, currentY);
      currentY += 25;

      const edgeImageData = getEdgeData(imageData, edgeThreshold);
      const edgeImageBitmap = await createImageBitmap(edgeImageData);
      const previewHeight = contentWidth * (edgeImageBitmap.height / edgeImageBitmap.width);
      if (currentY + previewHeight < h - 10) {
          ctx.drawImage(edgeImageBitmap, leftMargin, currentY, contentWidth, previewHeight);
      }
    }
    
    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 House Analysis Tool allows users to analyze images by extracting critical data, including dominant colors, brightness distribution, and structural edges. This tool is particularly useful for graphic designers, photographers, and artists who want to understand the color palette and brightness characteristics of their images. By providing a visual analysis report, users can make informed decisions on color usage and adjustments, enhancing their creative projects.

Leave a Reply

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