You can edit the below JavaScript code to customize the image tool.
// Define a flag and a promise in a scope that persists across calls.
// This helps ensure that the face-api.js script and its models are loaded only once.
if (typeof window.areFaceApiModelsLoaded === 'undefined') {
window.areFaceApiModelsLoaded = false;
}
let faceApiSetupPromise = null;
/**
* Helper function to ensure face-api.js script and its models are loaded.
* This function is idempotent and can be called multiple times safely.
* It loads the script and models from a CDN.
*/
async function ensureFaceApiInitialized() {
if (faceApiSetupPromise) {
// If setup is already in progress or completed, return the existing promise
return faceApiSetupPromise;
}
// Start the setup process
faceApiSetupPromise = (async () => {
// 1. Load face-api.js script if it's not already available
if (typeof faceapi === 'undefined') {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js';
script.async = true; // Load asynchronously
script.onload = resolve;
script.onerror = () => reject(new Error("Failed to load face-api.js script. Please check your network connection or the CDN link."));
document.head.appendChild(script);
});
}
// 2. Load the required face detection models if they haven't been loaded yet
if (!window.areFaceApiModelsLoaded) {
const modelPath = 'https://cdn.jsdelivr.net/gh/justadudewhohacks/face-api.js/weights';
// SSD MobilenetV1 is a good general-purpose model (balance of speed and accuracy).
await faceapi.nets.ssdMobilenetv1.loadFromUri(modelPath);
// Optionally, other models for landmarks, recognition, etc., could be loaded here if needed.
// e.g., await faceapi.nets.faceLandmark68Net.loadFromUri(modelPath);
window.areFaceApiModelsLoaded = true; // Mark models as loaded
}
})();
try {
await faceApiSetupPromise; // Wait for the setup to complete
} catch (error) {
faceApiSetupPromise = null; // Reset the promise on failure to allow a retry on a subsequent call
throw error; // Propagate the error so the caller can handle it
}
return faceApiSetupPromise;
}
/**
* Anonymizes faces in an image using specified method and intensity.
*
* @param {Image} originalImg The original HTMLImageElement to process. Ensure it's fully loaded.
* @param {string} anonymizationType Type of anonymization: 'blur', 'pixelate', or 'blackbox'. Defaults to 'blur'.
* @param {number} intensity Intensity for the anonymization effect.
* For 'blur', this is blur radius in pixels.
* For 'pixelate', this is the size of pixel blocks.
* Defaults to 10.
* @returns {Promise<HTMLCanvasElement>} A canvas element with anonymized faces.
* Returns a canvas with original image or an error message if processing fails.
*/
async function processImage(originalImg, anonymizationType = 'blur', intensity = 10) {
try {
// Ensure face-api.js and models are ready
await ensureFaceApiInitialized();
} catch (error) {
console.error("Initialization of face-api components failed:", error);
// Fallback: return original image drawn on a canvas, or an error message canvas
const fallbackCanvas = document.createElement('canvas');
const fbWidth = originalImg.naturalWidth || originalImg.width;
const fbHeight = originalImg.naturalHeight || originalImg.height;
fallbackCanvas.width = (fbWidth && fbWidth > 0) ? fbWidth : 300; // Default size if image dims are invalid
fallbackCanvas.height = (fbHeight && fbHeight > 0) ? fbHeight : 150;
const fallbackCtx = fallbackCanvas.getContext('2d');
try {
// Attempt to draw original image if its dimensions are valid
if (originalImg && originalImg.naturalWidth > 0 && originalImg.naturalHeight > 0) {
fallbackCtx.drawImage(originalImg, 0, 0, fallbackCanvas.width, fallbackCanvas.height);
} else {
throw new Error("Original image has invalid dimensions or is not loaded.");
}
} catch (drawError) {
// If drawing fails (e.g., image not loaded), display an error on the canvas
console.error("Failed to draw original image on fallback canvas:", drawError);
fallbackCtx.fillStyle = 'lightgray';
fallbackCtx.fillRect(0, 0, fallbackCanvas.width, fallbackCanvas.height);
fallbackCtx.fillStyle = 'black';
fallbackCtx.textAlign = 'center';
fallbackCtx.font = '16px Arial';
fallbackCtx.fillText('Error: Could not process image.', fallbackCanvas.width / 2, fallbackCanvas.height / 2);
}
return fallbackCanvas;
}
const canvas = document.createElement('canvas');
const imgWidth = originalImg.naturalWidth;
const imgHeight = originalImg.naturalHeight;
// Validate image dimensions
if (!imgWidth || !imgHeight) {
console.warn("Image dimensions are zero or invalid. Ensure the image is properly loaded before calling processImage.");
canvas.width = 300; // Default placeholder size
canvas.height = 150;
const ctxError = canvas.getContext('2d');
ctxError.fillStyle = 'lightgray';
ctxError.fillRect(0, 0, canvas.width, canvas.height);
ctxError.fillStyle = 'black';
ctxError.textAlign = 'center';
ctxError.font = '16px Arial';
ctxError.fillText("Error: Invalid image source.", canvas.width / 2, canvas.height / 2);
return canvas;
}
canvas.width = imgWidth;
canvas.height = imgHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// Configure face detection options (SsdMobilenetv1)
const detectionOptions = new faceapi.SsdMobilenetv1Options({ minConfidence: 0.5 }); // minConfidence can be adjusted
const detections = await faceapi.detectAllFaces(canvas, detectionOptions);
if (detections.length === 0) {
// No faces detected, return canvas with original image
return canvas;
}
// Validate and prepare intensity value
let numIntensity = parseFloat(intensity);
if (isNaN(numIntensity) || numIntensity <= 0) {
numIntensity = 10; // Default intensity if the provided one is invalid or non-positive
}
numIntensity = Math.max(1, numIntensity); // Ensure intensity is at least 1
// Apply anonymization to each detected face
detections.forEach(detection => {
const box = detection.box; // { x, y, width, height }
// Ensure box dimensions are positive (face-api.js usually provides valid boxes)
if (box.width <= 0 || box.height <= 0) return;
// Sanitize box coordinates and dimensions to ensure they are within canvas bounds.
// This prevents errors if detected face boxes are partially outside the image.
const safeBox = {
x: Math.max(0, box.x),
y: Math.max(0, box.y),
width: Math.min(box.width, canvas.width - Math.max(0, box.x)),
height: Math.min(box.height, canvas.height - Math.max(0, box.y)),
};
// If sanitized box has no area, skip
if (safeBox.width <=0 || safeBox.height <=0) return;
switch (anonymizationType.toLowerCase()) {
case 'blur':
const blurRadius = numIntensity;
ctx.filter = `blur(${blurRadius}px)`;
// Draw the identified region from the canvas back onto itself, now blurred.
// Source and destination rectangles are the same (safeBox dimensions).
ctx.drawImage(canvas, safeBox.x, safeBox.y, safeBox.width, safeBox.height, safeBox.x, safeBox.y, safeBox.width, safeBox.height);
ctx.filter = 'none'; // Reset filter for subsequent drawing operations
break;
case 'pixelate':
const pixelSize = Math.max(1, Math.floor(numIntensity)); // Pixel block size, must be integer >= 1
// Create a tiny temporary canvas for the pixelated version of the face
const tempPixelCanvas = document.createElement('canvas');
// Calculate dimensions for the small canvas (number of blocks)
const smallWidth = Math.max(1, Math.ceil(safeBox.width / pixelSize));
const smallHeight = Math.max(1, Math.ceil(safeBox.height / pixelSize));
tempPixelCanvas.width = smallWidth;
tempPixelCanvas.height = smallHeight;
const tempPixelCtx = tempPixelCanvas.getContext('2d');
// Draw the face region from the original image onto the small temporary canvas (downsampling).
// This creates the pixelated effect when scaled up without smoothing.
tempPixelCtx.drawImage(originalImg,
safeBox.x, safeBox.y, safeBox.width, safeBox.height, // Source rectangle from originalImg
0, 0, smallWidth, smallHeight); // Destination rectangle on tempPixelCanvas
// Disable image smoothing to get sharp, blocky pixels when scaling up.
const oldSmoothingEnabled = ctx.imageSmoothingEnabled;
ctx.imageSmoothingEnabled = false;
// Also set vendor-prefixed versions for broader compatibility, though modern browsers mainly use the standard one.
if (typeof ctx.mozImageSmoothingEnabled !== 'undefined') ctx.mozImageSmoothingEnabled = false;
if (typeof ctx.webkitImageSmoothingEnabled !== 'undefined') ctx.webkitImageSmoothingEnabled = false;
if (typeof ctx.msImageSmoothingEnabled !== 'undefined') ctx.msImageSmoothingEnabled = false;
// Draw the small, pixelated image from tempPixelCanvas back onto the main canvas, scaled up.
ctx.drawImage(tempPixelCanvas,
0, 0, smallWidth, smallHeight, // Source rectangle from tempPixelCanvas
safeBox.x, safeBox.y, safeBox.width, safeBox.height); // Destination rectangle on main canvas
// Restore the previous image smoothing setting.
ctx.imageSmoothingEnabled = oldSmoothingEnabled;
if (typeof ctx.mozImageSmoothingEnabled !== 'undefined') ctx.mozImageSmoothingEnabled = oldSmoothingEnabled;
if (typeof ctx.webkitImageSmoothingEnabled !== 'undefined') ctx.webkitImageSmoothingEnabled = oldSmoothingEnabled;
if (typeof ctx.msImageSmoothingEnabled !== 'undefined') ctx.msImageSmoothingEnabled = oldSmoothingEnabled;
break;
case 'blackbox':
ctx.fillStyle = 'black';
ctx.fillRect(safeBox.x, safeBox.y, safeBox.width, safeBox.height);
break;
default:
// Fallback to 'blur' if an unknown anonymizationType is provided
console.warn(`Unknown anonymizationType: "${anonymizationType}". Defaulting to blur.`);
const defaultBlurRadius = Math.max(1, numIntensity || 10); // Use numIntensity or a hardcoded default
ctx.filter = `blur(${defaultBlurRadius}px)`;
ctx.drawImage(canvas, safeBox.x, safeBox.y, safeBox.width, safeBox.height, safeBox.x, safeBox.y, safeBox.width, safeBox.height);
ctx.filter = 'none';
break;
}
});
return canvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Face Anonymization Tool allows users to anonymize faces in images through various methods such as blurring, pixelating, or covering faces with black boxes. This is particularly useful for enhancing privacy in photographs shared online or within documents. Whether you are a journalist protecting sources, a developer looking to comply with privacy regulations, or simply someone wanting to maintain anonymity in shared images, this tool provides a straightforward solution to effectively anonymize faces in your images.