Please bookmark this page to avoid losing your image tool!

Family Heirloom Image Restorer

(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, colorMode = 'sepia', contrastIntensity = 1, sharpenIntensity = 0.5) {
    // Parse and sanitize parameters
    colorMode = String(colorMode).toLowerCase();
    contrastIntensity = Number(contrastIntensity);
    if (isNaN(contrastIntensity)) contrastIntensity = 1;
    sharpenIntensity = Number(sharpenIntensity);
    if (isNaN(sharpenIntensity)) sharpenIntensity = 0.5;

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = originalImg.width;
    canvas.height = originalImg.height;
    
    // Fill the canvas with white background in case of image transparency
    ctx.fillStyle = '#ffffff';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(originalImg, 0, 0);

    const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const data = imgData.data;

    // Step 1: Compute histograms to perform contrast stretching (fix faded looks)
    const rHist = new Array(256).fill(0);
    const gHist = new Array(256).fill(0);
    const bHist = new Array(256).fill(0);

    for (let i = 0; i < data.length; i += 4) {
        rHist[data[i]]++;
        gHist[data[i+1]]++;
        bHist[data[i+2]]++;
    }

    // Function to safely clip extreme outliers (dust, scratches) from stretching
    function getPercentiles(hist, total, lowerPercent = 0.01, upperPercent = 0.01) {
        let min = 0, max = 255;
        let count = 0;
        for (let i = 0; i < 256; i++) {
            count += hist[i];
            if (count > total * lowerPercent) {
                min = i;
                break;
            }
        }
        count = 0;
        for (let i = 255; i >= 0; i--) {
            count += hist[i];
            if (count > total * upperPercent) {
                max = i;
                break;
            }
        }
        return { min, max };
    }

    const totalPixels = canvas.width * canvas.height;
    const rLims = getPercentiles(rHist, totalPixels);
    const gLims = getPercentiles(gHist, totalPixels);
    const bLims = getPercentiles(bHist, totalPixels);

    // Failsafe to prevent division by zero
    if (rLims.min >= rLims.max) { rLims.min = 0; rLims.max = 255; }
    if (gLims.min >= gLims.max) { gLims.min = 0; gLims.max = 255; }
    if (bLims.min >= bLims.max) { bLims.min = 0; bLims.max = 255; }

    const rRange = rLims.max - rLims.min;
    const gRange = gLims.max - gLims.min;
    const bRange = bLims.max - bLims.min;

    const blend = Math.min(1, Math.max(0, contrastIntensity));

    // Step 2: Apply restoration adjustments per pixel
    for (let i = 0; i < data.length; i += 4) {
        let r = data[i];
        let g = data[i+1];
        let b = data[i+2];

        // 1. Contrast Auto-leveling
        if (contrastIntensity > 0) {
            let nr = ((r - rLims.min) * 255) / rRange;
            let ng = ((g - gLims.min) * 255) / gRange;
            let nb = ((b - bLims.min) * 255) / bRange;

            r = r * (1 - blend) + nr * blend;
            g = g * (1 - blend) + ng * blend;
            b = b * (1 - blend) + nb * blend;

            r = Math.min(255, Math.max(0, r));
            g = Math.min(255, Math.max(0, g));
            b = Math.min(255, Math.max(0, b));
        }

        // 2. Color tint mapping
        if (colorMode === 'grayscale') {
            const lum = 0.299 * r + 0.587 * g + 0.114 * b;
            r = g = b = lum;
        } else if (colorMode === 'sepia') {
            const tr = 0.393 * r + 0.769 * g + 0.189 * b;
            const tg = 0.349 * r + 0.686 * g + 0.168 * b;
            const tb = 0.272 * r + 0.534 * g + 0.131 * b;
            r = Math.min(255, Math.max(0, tr));
            g = Math.min(255, Math.max(0, tg));
            b = Math.min(255, Math.max(0, tb));
        }

        data[i] = r;
        data[i+1] = g;
        data[i+2] = b;
    }

    ctx.putImageData(imgData, 0, 0);

    // Step 3: Unsharp Mask Convolution (Enhancing blurry details)
    if (sharpenIntensity > 0) {
        const amount = sharpenIntensity;
        const centerWeight = 4 + 1 / amount;
        const sideWeight = -1;
        const weightSum = 1 / amount;

        const sharpData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const sData = sharpData.data;
        const w = canvas.width;
        const h = canvas.height;

        for (let y = 1; y < h - 1; y++) {
            let offset = (y * w + 1) * 4;
            for (let x = 1; x < w - 1; x++) {
                
                let r = data[offset] * centerWeight;
                let g = data[offset+1] * centerWeight;
                let b = data[offset+2] * centerWeight;

                // Combine neighbor pixels to identify local contrast differences
                let idx = offset - w * 4; // Top
                r += data[idx] * sideWeight; g += data[idx+1] * sideWeight; b += data[idx+2] * sideWeight;
                
                idx = offset + w * 4; // Bottom
                r += data[idx] * sideWeight; g += data[idx+1] * sideWeight; b += data[idx+2] * sideWeight;
                
                idx = offset - 4;     // Left
                r += data[idx] * sideWeight; g += data[idx+1] * sideWeight; b += data[idx+2] * sideWeight;
                
                idx = offset + 4;     // Right
                r += data[idx] * sideWeight; g += data[idx+1] * sideWeight; b += data[idx+2] * sideWeight;

                sData[offset] = Math.min(255, Math.max(0, r / weightSum));
                sData[offset+1] = Math.min(255, Math.max(0, g / weightSum));
                sData[offset+2] = Math.min(255, Math.max(0, b / weightSum));

                offset += 4;
            }
        }
        ctx.putImageData(sharpData, 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 Family Heirloom Image Restorer is a tool designed to enhance and revitalize old or faded photographs. It utilizes advanced image processing techniques to perform automatic contrast stretching to correct faded colors, apply sharpening filters to bring out blurry details, and offer various color modes such as sepia or grayscale to give photos a classic aesthetic. This tool is ideal for restoring digital scans of historical family photos, improving the clarity of aged portraits, and breathing new life into vintage imagery.

Leave a Reply

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