You can edit the below JavaScript code to customize the image tool.
Apply Changes
/**
* Enhances the resolution of an image using traditional image processing techniques.
* It first upscales the image using high-quality browser interpolation, and then applies
* an Unsharp Mask filter to increase the perceived sharpness and detail.
*
* NOTE: This tool provides a classical image enhancement. True AI-based super-resolution,
* which can create new detail, requires complex, pre-trained models and is beyond the
* scope of a simple browser-based JavaScript function.
*
* @param {HTMLImageElement} originalImg The original low-resolution image object.
* @param {number} [scaleFactor=2] The factor by which to increase the image dimensions (e.g., 2 for 2x resolution).
* @param {number} [sharpenAmount=0.8] The strength of the sharpening effect. 0 means no sharpening. Good values are between 0.5 and 1.5.
* @param {number} [sharpenRadius=1] The radius of the blur used in the sharpening algorithm, affecting the size of details that are enhanced.
* @returns {Promise<HTMLCanvasElement>} A promise that resolves with a canvas element containing the enhanced image.
*/
async function processImage(originalImg, scaleFactor = 2, sharpenAmount = 0.8, sharpenRadius = 1) {
// 1. Parameter validation and setup
const safeScaleFactor = Math.max(1, Number(scaleFactor) || 2);
const safeSharpenAmount = Math.max(0, Number(sharpenAmount) || 0.8);
const safeSharpenRadius = Math.max(0, Math.round(Number(sharpenRadius)) || 1);
const newWidth = originalImg.naturalWidth * safeScaleFactor;
const newHeight = originalImg.naturalHeight * safeScaleFactor;
// 2. Create the final canvas and perform high-quality upscaling
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
const ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
ctx.drawImage(originalImg, 0, 0, newWidth, newHeight);
// If no sharpening is needed, return the upscaled image
if (safeSharpenAmount === 0 || safeSharpenRadius === 0) {
return canvas;
}
// 3. Apply Unsharp Mask for sharpening
/**
* Applies a fast, separable box blur to image data. This is an efficient
* O(N) approximation of a Gaussian blur.
* @param {ImageData} srcImageData The source image data.
* @param {number} radius The blur radius (integer).
* @returns {ImageData} The blurred image data.
*/
function fastBlur(srcImageData, radius) {
const width = srcImageData.width;
const height = srcImageData.height;
const src = srcImageData.data;
const temp = new Uint8ClampedArray(src.length);
const blurredData = new Uint8ClampedArray(src.length);
const windowSize = radius * 2 + 1;
// Horizontal pass
for (let y = 0; y < height; y++) {
let r_sum = 0, g_sum = 0, b_sum = 0, a_sum = 0;
const rowStartIdx = y * width * 4;
for (let i = -radius; i <= radius; i++) {
const x = Math.max(0, Math.min(width - 1, i));
const idx = rowStartIdx + x * 4;
r_sum += src[idx];
g_sum += src[idx + 1];
b_sum += src[idx + 2];
a_sum += src[idx + 3];
}
for (let x = 0; x < width; x++) {
const destIdx = rowStartIdx + x * 4;
temp[destIdx] = r_sum / windowSize;
temp[destIdx + 1] = g_sum / windowSize;
temp[destIdx + 2] = b_sum / windowSize;
temp[destIdx + 3] = a_sum / windowSize;
const oldPixelX = Math.max(0, x - radius);
const newPixelX = Math.min(width - 1, x + 1 + radius);
const oldPixelIdx = rowStartIdx + oldPixelX * 4;
const newPixelIdx = rowStartIdx + newPixelX * 4;
r_sum += src[newPixelIdx] - src[oldPixelIdx];
g_sum += src[newPixelIdx + 1] - src[oldPixelIdx + 1];
b_sum += src[newPixelIdx + 2] - src[oldPixelIdx + 2];
a_sum += src[newPixelIdx + 3] - src[oldPixelIdx + 3];
}
}
// Vertical pass
for (let x = 0; x < width; x++) {
let r_sum = 0, g_sum = 0, b_sum = 0, a_sum = 0;
for (let i = -radius; i <= radius; i++) {
const y = Math.max(0, Math.min(height - 1, i));
const idx = (y * width + x) * 4;
r_sum += temp[idx];
g_sum += temp[idx + 1];
b_sum += temp[idx + 2];
a_sum += temp[idx + 3];
}
for (let y = 0; y < height; y++) {
const destIdx = (y * width + x) * 4;
blurredData[destIdx] = r_sum / windowSize;
blurredData[destIdx + 1] = g_sum / windowSize;
blurredData[destIdx + 2] = b_sum / windowSize;
blurredData[destIdx + 3] = a_sum / windowSize;
const oldPixelY = Math.max(0, y - radius);
const newPixelY = Math.min(height - 1, y + 1 + radius);
const oldPixelIdx = (oldPixelY * width + x) * 4;
const newPixelIdx = (newPixelY * width + x) * 4;
r_sum += temp[newPixelIdx] - temp[oldPixelIdx];
g_sum += temp[newPixelIdx + 1] - temp[oldPixelIdx + 1];
b_sum += temp[newPixelIdx + 2] - temp[oldPixelIdx + 2];
a_sum += temp[newPixelIdx + 3] - temp[oldPixelIdx + 3];
}
}
return new ImageData(blurredData, width, height);
}
// Get pixel data from the upscaled image
const upscaledImageData = ctx.getImageData(0, 0, newWidth, newHeight);
// Create a blurred version for the unsharp mask
const blurredImageData = fastBlur(upscaledImageData, safeSharpenRadius);
const upscaledPixels = upscaledImageData.data;
const blurredPixels = blurredImageData.data;
// The Uint8ClampedArray will automatically clamp values between 0 and 255
const sharpenedPixels = new Uint8ClampedArray(upscaledPixels.length);
// Apply the unsharp mask formula: sharpened = original + (original - blurred) * amount
for (let i = 0; i < upscaledPixels.length; i += 4) {
// Red
const r_detail = upscaledPixels[i] - blurredPixels[i];
sharpenedPixels[i] = upscaledPixels[i] + r_detail * safeSharpenAmount;
// Green
const g_detail = upscaledPixels[i + 1] - blurredPixels[i + 1];
sharpenedPixels[i + 1] = upscaledPixels[i + 1] + g_detail * safeSharpenAmount;
// Blue
const b_detail = upscaledPixels[i + 2] - blurredPixels[i + 2];
sharpenedPixels[i + 2] = upscaledPixels[i + 2] + b_detail * safeSharpenAmount;
// Alpha (unchanged)
sharpenedPixels[i + 3] = upscaledPixels[i + 3];
}
// Create final image data and put it back on the canvas
const sharpenedImageData = new ImageData(sharpenedPixels, newWidth, newHeight);
ctx.putImageData(sharpenedImageData, 0, 0);
return canvas;
}
Apply Changes