Please bookmark this page to avoid losing your image tool!

Image Exam 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, mode = "document_scan", val1 = "15", val2 = "5") {
    const canvas = document.createElement('canvas');
    const width = originalImg.width;
    const height = originalImg.height;
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0);

    mode = mode.toString().toLowerCase();

    if (mode === "ela" || mode === "examine") {
        // Mode 1: Error Level Analysis (Image Examine Tool / Forensics)
        // Helps to detect manipulation in images like cheated exam proof/documents
        return new Promise((resolve, reject) => {
            const quality = (parseFloat(val2) || 90) / 100;
            const multiplier = parseFloat(val1) || 15;
            
            const jpegDataUrl = canvas.toDataURL('image/jpeg', quality);
            const tempImg = new Image();
            tempImg.onload = () => {
                const tempCanvas = document.createElement('canvas');
                tempCanvas.width = width;
                tempCanvas.height = height;
                const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
                tempCtx.drawImage(tempImg, 0, 0);
                
                const origData = ctx.getImageData(0, 0, width, height);
                const tempData = tempCtx.getImageData(0, 0, width, height);
                const outData = ctx.createImageData(width, height);
                
                for (let i = 0; i < origData.data.length; i += 4) {
                    outData.data[i] = Math.abs(origData.data[i] - tempData.data[i]) * multiplier;     // R
                    outData.data[i+1] = Math.abs(origData.data[i+1] - tempData.data[i+1]) * multiplier; // G
                    outData.data[i+2] = Math.abs(origData.data[i+2] - tempData.data[i+2]) * multiplier; // B
                    outData.data[i+3] = 255;                                                            // A
                }
                ctx.putImageData(outData, 0, 0);
                resolve(canvas);
            };
            tempImg.onerror = reject;
            tempImg.src = jpegDataUrl;
        });
    } 
    else if (mode === "watermark") {
        // Mode 2: Literally stamp "ЭКЗАМЕН" (Exam) across the image
        const text = (val1 === "15" || !val1) ? "ЭКЗАМЕН" : val1.toString();
        const opacity = parseFloat(val2) || 0.6;
        
        ctx.fillStyle = `rgba(200, 30, 30, ${opacity})`;
        ctx.font = `bold ${Math.max(width, height) / 8}px 'Courier New', Courier, monospace`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        
        ctx.translate(width / 2, height / 2);
        ctx.rotate(-Math.PI / 6);
        
        const textMetrics = ctx.measureText(text);
        const padX = textMetrics.width * 0.15;
        const ascent = textMetrics.actualBoundingBoxAscent || (parseInt(ctx.font) * 0.7);
        const descent = textMetrics.actualBoundingBoxDescent || (parseInt(ctx.font) * 0.2);
        const padY = ascent * 0.4;
        
        ctx.lineWidth = Math.max(width, height) / 80;
        ctx.strokeStyle = `rgba(200, 30, 30, ${opacity})`;
        ctx.strokeRect(
            -(textMetrics.width / 2) - padX, 
            -ascent - padY, 
            textMetrics.width + (padX * 2), 
            (ascent + descent) + (padY * 2)
        );
        
        ctx.fillText(text, 0, 0);
        return canvas;
    }
    else {
        // Mode 3: Document Scanner / Outline tool (Default Mode)
        // Extremely useful for analyzing/printing text from exam note photos (Adaptive Binarization)
        const imgData = ctx.getImageData(0, 0, width, height);
        const data = imgData.data;
        
        // Generate Integral Image for fast local thresholding computation (Bradley-Roth algorithm)
        const integral = new Int32Array(width * height);
        for (let y = 0; y < height; y++) {
            let sum = 0;
            for (let x = 0; x < width; x++) {
                let idx = y * width + x;
                let i4 = idx * 4;
                // Grayscale equivalent
                let gray = data[i4] * 0.299 + data[i4 + 1] * 0.587 + data[i4 + 2] * 0.114;
                sum += gray;
                integral[idx] = (y === 0) ? sum : integral[idx - width] + sum;
            }
        }
        
        const t = parseFloat(val1) || 15; // Threshold control
        const windowPercent = parseFloat(val2) || 5; 
        const s = Math.max(2, Math.floor(width * (windowPercent / 100)));
        const sHalf = Math.floor(s / 2);
        
        const outData = ctx.createImageData(width, height);
        const outBase = outData.data;
        
        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                let idx = y * width + x;
                let i4 = idx * 4;
                
                let x1 = Math.max(x - sHalf, 0);
                let y1 = Math.max(y - sHalf, 0);
                let x2 = Math.min(x + sHalf, width - 1);
                let y2 = Math.min(y + sHalf, height - 1);
                
                let count = (x2 - x1 + 1) * (y2 - y1 + 1);
                
                let valX1_Y2 = (x1 > 0) ? integral[y2 * width + (x1 - 1)] : 0;
                let valX2_Y1 = (y1 > 0) ? integral[(y1 - 1) * width + x2] : 0;
                let valX1_Y1 = (x1 > 0 && y1 > 0) ? integral[(y1 - 1) * width + (x1 - 1)] : 0;
                
                let windowSum = integral[y2 * width + x2] - valX1_Y2 - valX2_Y1 + valX1_Y1;
                let gray = data[i4] * 0.299 + data[i4 + 1] * 0.587 + data[i4 + 2] * 0.114;
                
                if (gray * count < windowSum * (1.0 - (t / 100.0))) {
                    outBase[i4] = 0;     // Black
                    outBase[i4+1] = 0;
                    outBase[i4+2] = 0;
                    outBase[i4+3] = 255;
                } else {
                    outBase[i4] = 255;   // White
                    outBase[i4+1] = 255;
                    outBase[i4+2] = 255;
                    outBase[i4+3] = 255;
                }
            }
        }
        
        ctx.putImageData(outData, 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

The Image Exam Tool is a versatile utility designed for document analysis and image verification. It offers three primary functions: Error Level Analysis (ELA) to help detect digital manipulations or tampering in images, a watermarking feature to overlay custom text onto documents for security, and a document scanning mode that uses adaptive binarization to enhance text readability and clarity. This tool is useful for forensic examination, securing digital documents, and improving the quality of scanned notes or handwritten exam papers.

Leave a Reply

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