Please bookmark this page to avoid losing your image tool!

Image Quality 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.
function processImage(originalImg) {
    const resultDiv = document.createElement('div');
    resultDiv.style.fontFamily = "Arial, sans-serif";
    resultDiv.style.padding = "10px";
    resultDiv.style.border = "1px solid #ccc";
    resultDiv.style.borderRadius = "5px";
    resultDiv.style.backgroundColor = "#f9f9f9";
    resultDiv.style.maxWidth = "400px"; // Max width for better layout in containers

    // Helper function to create and return an error message element
    const createErrorElement = (message) => {
        const errorP = document.createElement('p');
        errorP.style.color = "red";
        errorP.textContent = message;
        resultDiv.innerHTML = ''; // Clear any previous content (like a title)
        resultDiv.appendChild(errorP);
        return resultDiv;
    };
    
    const canvas = document.createElement('canvas');
    // Use willReadFrequently for potential performance gain, alpha:false as we don't need transparency for analysis.
    const ctx = canvas.getContext('2d', { willReadFrequently: true, alpha: false });

    // Use naturalWidth/Height for intrinsic dimensions, fallback to width/height if natural* are 0
    canvas.width = originalImg.naturalWidth || originalImg.width;
    canvas.height = originalImg.naturalHeight || originalImg.height;

    // Check if image dimensions are valid
    if (canvas.width === 0 || canvas.height === 0) {
        return createErrorElement("Error: Image has zero width or height. It might not be loaded properly.");
    }

    // Draw image to canvas to access pixel data
    try {
        ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    } catch (e) {
         return createErrorElement(`Error: Could not draw image on canvas. Details: ${e.message}`);
    }

    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    } catch (e) {
        // This can happen due to tainted canvas (CORS issues)
        return createErrorElement(`Error: Could not get image data. This often occurs with cross-origin images if CORS is not configured. Details: ${e.message}`);
    }
    
    const data = imageData.data;
    const width = canvas.width;
    const height = canvas.height;
    const numPixels = width * height;

    // This check is likely redundant given the initial dimension check, but ensures numPixels is positive.
    if (numPixels === 0) { 
        return createErrorElement("Error: Image has no pixels (width or height is effectively zero after processing).");
    }

    // Add title to the result div
    const titleEl = document.createElement('h3');
    titleEl.textContent = "Image Quality Analysis";
    titleEl.style.marginTop = "0";
    titleEl.style.borderBottom = "1px solid #eee";
    titleEl.style.paddingBottom = "5px";
    titleEl.style.marginBottom = "10px";
    resultDiv.appendChild(titleEl);

    const ul = document.createElement('ul');
    ul.style.listStyleType = "none";
    ul.style.paddingLeft = "0";
    ul.style.margin = "0";

    // Helper function to add analysis items to the list
    function addItem(label, value, interpretation = "") {
        const li = document.createElement('li');
        li.style.marginBottom = "10px";
        li.style.fontSize = "14px";
        
        const strong = document.createElement('strong');
        strong.textContent = `${label}: `;
        li.appendChild(strong);
        
        li.appendChild(document.createTextNode(String(value)));
        
        if (interpretation) {
            const small = document.createElement('small');
            small.style.display = "block"; // Interpretation on a new line
            small.style.color = "#555";     // Softer color for interpretation
            small.style.marginTop = "2px";
            small.innerHTML = `<em>${interpretation}</em>`; // Use innerHTML for em tag
            li.appendChild(small);
        }
        ul.appendChild(li);
    }

    // 1. Brightness and Contrast Analysis
    let luminanceSum = 0;
    const luminances = new Array(numPixels); // Array to store luminance of each pixel
    const grayscaleData = new Uint8Array(numPixels); // For Laplacian filter

    for (let i = 0; i < numPixels; i++) {
        const r = data[i * 4];
        const g = data[i * 4 + 1];
        const b = data[i * 4 + 2];
        // Using standard luminance formula (Rec. 601)
        const lum = 0.299 * r + 0.587 * g + 0.114 * b;
        luminances[i] = lum;
        luminanceSum += lum;
        grayscaleData[i] = Math.round(lum);
    }

    const avgBrightness = luminanceSum / numPixels;

    let sumSqDiffLuminance = 0;
    for (let i = 0; i < numPixels; i++) {
        sumSqDiffLuminance += (luminances[i] - avgBrightness) * (luminances[i] - avgBrightness);
    }
    const stdDevLuminance = Math.sqrt(sumSqDiffLuminance / numPixels);

    // 2. Sharpness Analysis (Laplacian Variance)
    let varianceLaplacian = 0; // Default to 0 if not computable

    // Only compute Laplacian if image is large enough for the kernel (at least 3x3)
    if (width >= 3 && height >= 3) {
        const tempLaplacianValues = []; 
        const getGrayscalePixel = (x, y) => grayscaleData[y * width + x];
        
        // Apply Laplacian kernel: [0, 1, 0], [1, -4, 1], [0, 1, 0]
        // Iterate from 1 to width-2 and 1 to height-2 to avoid edge issues
        for (let y = 1; y < height - 1; y++) {
            for (let x = 1; x < width - 1; x++) {
                const centerVal = getGrayscalePixel(x, y);
                const topVal = getGrayscalePixel(x, y - 1);
                const bottomVal = getGrayscalePixel(x, y + 1);
                const leftVal = getGrayscalePixel(x - 1, y);
                const rightVal = getGrayscalePixel(x + 1, y);
                
                const laplacianValue = topVal + bottomVal + leftVal + rightVal - 4 * centerVal;
                tempLaplacianValues.push(laplacianValue);
            }
        }

        if (tempLaplacianValues.length > 0) {
            let sumLaplacian = 0;
            tempLaplacianValues.forEach(val => sumLaplacian += val);
            const meanLaplacian = sumLaplacian / tempLaplacianValues.length;

            let sumSqDiffLaplacian = 0;
            tempLaplacianValues.forEach(val => sumSqDiffLaplacian += (val - meanLaplacian) * (val - meanLaplacian));
            varianceLaplacian = sumSqDiffLaplacian / tempLaplacianValues.length;
        }
    }
    
    // 3. Outputting results
    addItem("Dimensions", `${width}px x ${height}px`);

    let brightnessInterpretation;
    if (avgBrightness < 70) brightnessInterpretation = "Suggests a dark image.";
    else if (avgBrightness > 180) brightnessInterpretation = "Suggests a bright image.";
    else brightnessInterpretation = "Suggests well-balanced brightness.";
    addItem("Average Brightness", avgBrightness.toFixed(2) + " (0-255)", brightnessInterpretation);

    let contrastInterpretation;
    if (stdDevLuminance < 20) contrastInterpretation = "Suggests low contrast (potentially flat).";
    else if (stdDevLuminance < 50) contrastInterpretation = "Suggests medium contrast.";
    else contrastInterpretation = "Suggests high contrast.";
    addItem("Luminance StdDev (Contrast)", stdDevLuminance.toFixed(2), contrastInterpretation);
    
    let sharpnessInterpretation;
    let sharpnessValueDisplay = "N/A";

    if (width < 3 || height < 3) {
        sharpnessInterpretation = "Image too small for reliable sharpness analysis (min 3x3 required).";
    } else {
        sharpnessValueDisplay = varianceLaplacian.toFixed(2);
        // These thresholds are empirical and can be adjusted.
        if (varianceLaplacian < 100) { 
            sharpnessInterpretation = "Suggests lower sharpness or very smooth textures.";
        } else if (varianceLaplacian < 500) { 
            sharpnessInterpretation = "Suggests moderate sharpness/detail.";
        } else {
            sharpnessInterpretation = "Suggests higher sharpness/detail.";
        }
        sharpnessInterpretation += " (Higher values typically indicate more edges/detail; very dependent on image content)";
    }
    addItem("Laplacian Variance (Sharpness)", sharpnessValueDisplay, sharpnessInterpretation);

    resultDiv.appendChild(ul);
    return resultDiv;
}

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 Quality Analysis Tool provides users with a detailed examination of an image’s quality by evaluating various aspects such as brightness, contrast, and sharpness. It analyzes the pixel data from an uploaded image to calculate average brightness, luminance standard deviation for contrast, and Laplacian variance to determine sharpness. This tool is useful for photographers, graphic designers, and anyone interested in assessing image quality, ensuring that images meet certain standards for brightness, clarity, and overall visual appeal.

Leave a Reply

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