Please bookmark this page to avoid losing your image tool!

Light Bill Image Extractor

(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, language = 'eng', enhanceMode = 'Grayscale', contrast = '1.2', illuminanceSmoothing = '15') {
    // Create the main wrapper element
    const container = document.createElement('div');
    container.style.display = 'flex';
    container.style.flexDirection = 'column';
    container.style.gap = '20px';
    container.style.fontFamily = 'system-ui, -apple-system, sans-serif';
    container.style.width = '100%';
    container.style.padding = '20px';
    container.style.boxSizing = 'border-box';
    container.style.backgroundColor = '#f0f2f5';
    container.style.borderRadius = '12px';

    const headerTitle = document.createElement('h2');
    headerTitle.textContent = "Light Bill Data Extractor";
    headerTitle.style.margin = '0 0 10px 0';
    headerTitle.style.color = '#333';
    headerTitle.style.fontSize = '22px';
    container.appendChild(headerTitle);

    const splitView = document.createElement('div');
    splitView.style.display = 'flex';
    splitView.style.flexWrap = 'wrap';
    splitView.style.gap = '20px';
    splitView.style.width = '100%';

    // Visual extraction (Document Scanner filter layout)
    const canvasWrapper = document.createElement('div');
    canvasWrapper.style.flex = '1 1 320px';
    canvasWrapper.style.display = 'flex';
    canvasWrapper.style.flexDirection = 'column';
    canvasWrapper.style.backgroundColor = '#fff';
    canvasWrapper.style.padding = '15px';
    canvasWrapper.style.borderRadius = '8px';
    canvasWrapper.style.boxShadow = '0 2px 5px rgba(0,0,0,0.05)';
    
    const canvasHeader = document.createElement('div');
    canvasHeader.innerHTML = '<strong style="color: #222;">Enhanced Document Image</strong><br><small style="color: #666;">Illumination flattened for clear reading</small>';
    canvasHeader.style.marginBottom = '12px';
    canvasHeader.style.lineHeight = '1.4';
    canvasWrapper.appendChild(canvasHeader);

    // Context & Canvas setup
    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 original image
    ctx.drawImage(originalImg, 0, 0);
    const origData = ctx.getImageData(0, 0, width, height).data;
    
    // Create computationally efficient illumination map (Background Subtraction)
    // Scale image down heavily for fast and smooth glowing blur
    const scaleDown = Math.min(1, 500 / Math.max(width, height)); 
    const blurCanvas = document.createElement('canvas');
    blurCanvas.width = width * scaleDown;
    blurCanvas.height = height * scaleDown;
    const blurCtx = blurCanvas.getContext('2d');
    
    // Apply blur relative to scaled canvas context
    blurCtx.filter = `blur(${parseFloat(illuminanceSmoothing)}px)`;
    blurCtx.drawImage(originalImg, 0, 0, blurCanvas.width, blurCanvas.height);
    
    // Upscale the smoothed illumination map cleanly
    const bigBlurCanvas = document.createElement('canvas');
    bigBlurCanvas.width = width;
    bigBlurCanvas.height = height;
    const bigBlurCtx = bigBlurCanvas.getContext('2d', { willReadFrequently: true });
    bigBlurCtx.imageSmoothingEnabled = true;
    bigBlurCtx.imageSmoothingQuality = "high";
    bigBlurCtx.drawImage(blurCanvas, 0, 0, width, height);

    const blurData = bigBlurCtx.getImageData(0, 0, width, height).data;
    const resultImgData = ctx.createImageData(width, height);
    const resultData = resultImgData.data;
    
    const bWhiteThresh = 210;
    const bBlackThresh = 110;
    const cVal = parseFloat(contrast);
    
    // Apply adaptive division filter 
    for (let i = 0; i < origData.length; i += 4) {
        let r = origData[i], g = origData[i + 1], b = origData[i + 2];
        let br = blurData[i] || 1, bg = blurData[i + 1] || 1, bb = blurData[i + 2] || 1;
        
        // Normalize against illumination map to remove shadowing
        let nr = (r / br) * 230; 
        let ng = (g / bg) * 230;
        let nb = (b / bb) * 230;
        
        // Apply target contrast
        nr = ((nr / 255 - 0.5) * cVal + 0.5) * 255;
        ng = ((ng / 255 - 0.5) * cVal + 0.5) * 255;
        nb = ((nb / 255 - 0.5) * cVal + 0.5) * 255;
        
        let finalR = Math.min(255, Math.max(0, nr));
        let finalG = Math.min(255, Math.max(0, ng));
        let finalB = Math.min(255, Math.max(0, nb));
        
        // Convert to Luminance (Grayscale)
        const luma = finalR * 0.299 + finalG * 0.587 + finalB * 0.114;

        if (enhanceMode === 'B&W') {
            let finalVal;
            if (luma > bWhiteThresh) finalVal = 255;
            else if (luma < bBlackThresh) finalVal = 0;
            else finalVal = ((luma - bBlackThresh) / (bWhiteThresh - bBlackThresh)) * 255;
            
            resultData[i] = finalVal;
            resultData[i+1] = finalVal;
            resultData[i+2] = finalVal;
        } else {
            resultData[i] = luma;
            resultData[i+1] = luma;
            resultData[i+2] = luma;
        }
        resultData[i + 3] = origData[i + 3];
    }
    
    ctx.putImageData(resultImgData, 0, 0);

    // Style and inject canvas
    canvas.style.width = '100%';
    canvas.style.height = 'auto';
    canvas.style.border = '1px solid #e1e4e8';
    canvas.style.borderRadius = '4px';
    canvasWrapper.appendChild(canvas);

    // Text extraction (OCR section)
    const txtWrapper = document.createElement('div');
    txtWrapper.style.flex = '1 1 320px';
    txtWrapper.style.display = 'flex';
    txtWrapper.style.flexDirection = 'column';
    txtWrapper.style.backgroundColor = '#fff';
    txtWrapper.style.padding = '15px';
    txtWrapper.style.borderRadius = '8px';
    txtWrapper.style.boxShadow = '0 2px 5px rgba(0,0,0,0.05)';

    const txtHeader = document.createElement('div');
    txtHeader.innerHTML = '<strong style="color: #222;">Extracted Bill Data (OCR)</strong><br><small style="color: #666;">Reading enhanced image data automatically</small>';
    txtHeader.style.marginBottom = '12px';
    txtHeader.style.lineHeight = '1.4';
    txtWrapper.appendChild(txtHeader);

    const textOutput = document.createElement('textarea');
    textOutput.style.flexGrow = '1';
    textOutput.style.width = '100%';
    textOutput.style.minHeight = '250px';
    textOutput.style.padding = '12px';
    textOutput.style.border = '1px solid #e1e4e8';
    textOutput.style.borderRadius = '6px';
    textOutput.style.boxSizing = 'border-box';
    textOutput.style.fontSize = '14px';
    textOutput.style.fontFamily = 'monospace';
    textOutput.style.color = '#333';
    textOutput.style.resize = 'vertical';
    textOutput.style.backgroundColor = '#fafafa';
    textOutput.value = "Initializing extraction engine...\nThis may take a moment to download library files.";
    txtWrapper.appendChild(textOutput);

    splitView.appendChild(canvasWrapper);
    splitView.appendChild(txtWrapper);
    container.appendChild(splitView);

    // Dynamically Load Tesseract.js (v5) and process text asynchronously
    const loadTesseract = (src) => new Promise((resolve, reject) => {
        if (window.Tesseract) { resolve(); return; }
        const script = document.createElement('script');
        script.src = src;
        script.crossOrigin = "anonymous";
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });

    loadTesseract('https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js')
        .then(() => {
            textOutput.value = "OCR library initialized. Processing image...";
            return Tesseract.recognize(
                canvas,
                language,
                { 
                    logger: m => {
                        if (m.status) {
                            let progressMsg = m.progress ? ` (${Math.round(m.progress * 100)}%)` : '';
                            textOutput.value = `Status: ${m.status}${progressMsg}\nWorking...`;
                        }
                    }
                }
            );
        })
        .then(({ data: { text } }) => {
            if (text.trim()) {
                textOutput.value = text;
                textOutput.style.backgroundColor = '#fff';
            } else {
                textOutput.value = "Extraction finished, but no actionable text was found in the image.";
            }
        })
        .catch(err => {
            textOutput.value = "Error during text extraction:\n" + err.message;
            textOutput.style.color = '#d32f2f';
        });

    return container;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Light Bill Image Extractor is a specialized utility designed to digitize information from utility bills and similar documents. The tool enhances document images by flattening illumination and removing shadows to improve readability, and then utilizes Optical Character Recognition (OCR) technology to automatically extract and display the text contained within the image. This tool is particularly useful for automating data entry, managing expense reports, or digitizing physical records into editable text formats.

Leave a Reply

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