| <!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> |