|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8" /> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
|
|
<title>Face Blurring Web App</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script defer src="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js"></script> |
|
|
<style> |
|
|
canvas { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
pointer-events: none; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-100 min-h-screen flex items-center justify-center p-6"> |
|
|
<div class="bg-white p-8 rounded-lg shadow-xl max-w-3xl w-full"> |
|
|
<h1 class="text-3xl font-bold mb-4 text-center">Face Blurring Web App</h1> |
|
|
<p class="text-gray-600 mb-6 text-center">Upload an image and click "Blur Faces" to automatically blur detected faces.</p> |
|
|
|
|
|
<div class="mb-4"> |
|
|
<label class="block text-gray-700 font-medium mb-2" for="imageUpload">Upload Image</label> |
|
|
<input type="file" id="imageUpload" accept="image/*" class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-blue-500 file:text-white hover:file:bg-blue-600" /> |
|
|
</div> |
|
|
|
|
|
<div class="relative mx-auto w-full max-w-md border border-gray-300 rounded overflow-hidden bg-gray-200" style="aspect-ratio: 4/3;"> |
|
|
<img id="uploadedImage" src="" alt="Uploaded Image" class="w-full h-full object-contain hidden" /> |
|
|
<canvas id="overlayCanvas" class="absolute top-0 left-0 w-full h-full"></canvas> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 flex justify-center"> |
|
|
<button id="blurButton" class="bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-2 px-6 rounded-lg shadow-md transition disabled:opacity-50 disabled:cursor-not-allowed" disabled> |
|
|
Blur Faces |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="status" class="mt-4 text-center text-sm text-gray-500"></div> |
|
|
|
|
|
<a id="downloadLink" class="mt-4 block text-center text-blue-600 hover:underline hidden" download="blurred_image.jpg">Download Blurred Image</a> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
const imageUpload = document.getElementById('imageUpload'); |
|
|
const uploadedImage = document.getElementById('uploadedImage'); |
|
|
const overlayCanvas = document.getElementById('overlayCanvas'); |
|
|
const blurButton = document.getElementById('blurButton'); |
|
|
const downloadLink = document.getElementById('downloadLink'); |
|
|
const status = document.getElementById('status'); |
|
|
|
|
|
let imageLoaded = false; |
|
|
let detections = []; |
|
|
|
|
|
|
|
|
Promise.all([ |
|
|
faceapi.nets.tinyFaceDetector.loadFromUri('https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/weights'), |
|
|
]).then(() => { |
|
|
status.textContent = 'Models loaded. Ready to blur faces.'; |
|
|
}); |
|
|
|
|
|
imageUpload.addEventListener('change', (e) => { |
|
|
const file = e.target.files[0]; |
|
|
if (!file) return; |
|
|
|
|
|
const reader = new FileReader(); |
|
|
reader.onload = function (event) { |
|
|
uploadedImage.src = event.target.result; |
|
|
uploadedImage.onload = () => { |
|
|
imageLoaded = true; |
|
|
blurButton.disabled = false; |
|
|
downloadLink.classList.add('hidden'); |
|
|
status.textContent = 'Image loaded. Click "Blur Faces".'; |
|
|
}; |
|
|
}; |
|
|
reader.readAsDataURL(file); |
|
|
}); |
|
|
|
|
|
blurButton.addEventListener('click', async () => { |
|
|
if (!imageLoaded) return; |
|
|
|
|
|
blurButton.disabled = true; |
|
|
status.textContent = 'Detecting faces...'; |
|
|
|
|
|
const img = uploadedImage; |
|
|
const canvas = overlayCanvas; |
|
|
const displaySize = { width: img.width, height: img.height }; |
|
|
faceapi.matchDimensions(canvas, displaySize); |
|
|
|
|
|
detections = await faceapi.detectAllFaces(img, new faceapi.TinyFaceDetectorOptions()); |
|
|
|
|
|
status.textContent = `Detected ${detections.length} face(s). Blurring...`; |
|
|
|
|
|
const offscreenCanvas = document.createElement('canvas'); |
|
|
offscreenCanvas.width = img.width; |
|
|
offscreenCanvas.height = img.height; |
|
|
const ctx = offscreenCanvas.getContext('2d'); |
|
|
ctx.drawImage(img, 0, 0); |
|
|
|
|
|
detections.forEach(detection => { |
|
|
const box = detection.box; |
|
|
const faceImg = ctx.getImageData(box.x, box.y, box.width, box.height); |
|
|
const tempCanvas = document.createElement('canvas'); |
|
|
const tempCtx = tempCanvas.getContext('2d'); |
|
|
tempCanvas.width = box.width; |
|
|
tempCanvas.height = box.height; |
|
|
tempCtx.putImageData(faceImg, 0, 0); |
|
|
|
|
|
|
|
|
tempCtx.filter = 'blur(10px)'; |
|
|
ctx.filter = 'blur(10px)'; |
|
|
ctx.drawImage(tempCanvas, box.x, box.y, box.width, box.height); |
|
|
}); |
|
|
|
|
|
|
|
|
const blurredImage = offscreenCanvas.toDataURL('image/jpeg'); |
|
|
uploadedImage.src = blurredImage; |
|
|
|
|
|
|
|
|
downloadLink.href = blurredImage; |
|
|
downloadLink.classList.remove('hidden'); |
|
|
downloadLink.download = 'blurred_image.jpg'; |
|
|
|
|
|
status.textContent = 'Face blurring completed!'; |
|
|
blurButton.disabled = false; |
|
|
}); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=6ee5ali/face" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |