You can edit the below JavaScript code to customize the image tool.
Apply Changes
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;
}
Apply Changes