Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Watermark & Text Removal Tool</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .image-container { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .processing-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| color: white; | |
| font-size: 1.5rem; | |
| z-index: 10; | |
| display: none; | |
| } | |
| .spinner { | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| from { transform: rotate(0deg); } | |
| to { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <h1 class="text-3xl font-bold text-center text-blue-600 mb-8">Watermark & Text Removal Tool</h1> | |
| <div class="bg-white rounded-lg shadow-xl p-6 mb-8"> | |
| <div class="flex flex-col md:flex-row gap-6"> | |
| <!-- Original Image Section --> | |
| <div class="flex-1"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-700">Original Image</h2> | |
| <div class="image-container bg-gray-200 rounded-lg overflow-hidden h-64 md:h-96 flex items-center justify-center"> | |
| <div id="original-image-placeholder" class="text-center p-4"> | |
| <i class="fas fa-image text-5xl text-gray-400 mb-3"></i> | |
| <p class="text-gray-500">No image selected</p> | |
| </div> | |
| <canvas id="original-canvas" class="hidden max-w-full max-h-full"></canvas> | |
| <div class="processing-overlay" id="original-processing"> | |
| <div class="text-center"> | |
| <i class="fas fa-spinner spinner mb-2"></i> | |
| <p>Processing...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Processed Image Section --> | |
| <div class="flex-1"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-700">Processed Image</h2> | |
| <div class="image-container bg-gray-200 rounded-lg overflow-hidden h-64 md:h-96 flex items-center justify-center"> | |
| <div id="processed-image-placeholder" class="text-center p-4"> | |
| <i class="fas fa-magic text-5xl text-gray-400 mb-3"></i> | |
| <p class="text-gray-500">Processed image will appear here</p> | |
| </div> | |
| <canvas id="processed-canvas" class="hidden max-w-full max-h-full"></canvas> | |
| <div class="processing-overlay" id="processed-processing"> | |
| <div class="text-center"> | |
| <i class="fas fa-spinner spinner mb-2"></i> | |
| <p>Processing...</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controls Section --> | |
| <div class="mt-8 grid grid-cols-2 md:grid-cols-3 gap-4"> | |
| <button id="upload-btn" class="bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition"> | |
| <i class="fas fa-upload"></i> | |
| <span>Upload Image (1)</span> | |
| </button> | |
| <button id="remove-watermark-btn" class="bg-green-500 hover:bg-green-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition disabled:opacity-50" disabled> | |
| <i class="fas fa-tint-slash"></i> | |
| <span>Remove Watermark (2)</span> | |
| </button> | |
| <button id="remove-text-btn" class="bg-purple-500 hover:bg-purple-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition disabled:opacity-50" disabled> | |
| <i class="fas fa-font"></i> | |
| <span>Remove Text (3)</span> | |
| </button> | |
| <button id="download-btn" class="bg-indigo-500 hover:bg-indigo-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition disabled:opacity-50" disabled> | |
| <i class="fas fa-download"></i> | |
| <span>Download (4)</span> | |
| </button> | |
| <button id="reset-btn" class="bg-gray-500 hover:bg-gray-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition disabled:opacity-50" disabled> | |
| <i class="fas fa-sync-alt"></i> | |
| <span>New Image (5)</span> | |
| </button> | |
| <button id="remove-clothes-btn" class="bg-red-500 hover:bg-red-600 text-white py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition disabled:opacity-50" disabled> | |
| <i class="fas fa-tshirt"></i> | |
| <span>Remove Clothes (6)</span> | |
| </button> | |
| </div> | |
| <!-- Advanced Options --> | |
| <div class="mt-8 bg-gray-50 p-4 rounded-lg"> | |
| <h3 class="font-semibold text-gray-700 mb-3 flex items-center gap-2"> | |
| <i class="fas fa-cog"></i> | |
| Advanced Options | |
| </h3> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Watermark Detection Sensitivity</label> | |
| <input type="range" id="watermark-sensitivity" min="1" max="100" value="50" class="w-full"> | |
| <div class="flex justify-between text-xs text-gray-500"> | |
| <span>Low</span> | |
| <span>High</span> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Text Detection Accuracy</label> | |
| <input type="range" id="text-sensitivity" min="1" max="100" value="50" class="w-full"> | |
| <div class="flex justify-between text-xs text-gray-500"> | |
| <span>Low</span> | |
| <span>High</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Instructions --> | |
| <div class="bg-white rounded-lg shadow-xl p-6"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-700 flex items-center gap-2"> | |
| <i class="fas fa-info-circle"></i> | |
| How to Use | |
| </h2> | |
| <ol class="list-decimal pl-5 space-y-2 text-gray-600"> | |
| <li>Click "Upload Image" to select an image from your device</li> | |
| <li>Use "Remove Watermark" to eliminate watermarks/logos</li> | |
| <li>Use "Remove Text" to delete any text elements</li> | |
| <li>Use "Remove Clothes" for fashion editing (experimental)</li> | |
| <li>Download the processed image when satisfied</li> | |
| <li>Start over with "New Image" at any time</li> | |
| </ol> | |
| <div class="mt-4 p-3 bg-yellow-50 border-l-4 border-yellow-400 text-yellow-700"> | |
| <p><strong>Note:</strong> For best results, use high-quality images with clear watermarks/text.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const uploadBtn = document.getElementById('upload-btn'); | |
| const removeWatermarkBtn = document.getElementById('remove-watermark-btn'); | |
| const removeTextBtn = document.getElementById('remove-text-btn'); | |
| const downloadBtn = document.getElementById('download-btn'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| const removeClothesBtn = document.getElementById('remove-clothes-btn'); | |
| const originalCanvas = document.getElementById('original-canvas'); | |
| const processedCanvas = document.getElementById('processed-canvas'); | |
| const originalPlaceholder = document.getElementById('original-image-placeholder'); | |
| const processedPlaceholder = document.getElementById('processed-image-placeholder'); | |
| const originalProcessing = document.getElementById('original-processing'); | |
| const processedProcessing = document.getElementById('processed-processing'); | |
| const watermarkSensitivity = document.getElementById('watermark-sensitivity'); | |
| const textSensitivity = document.getElementById('text-sensitivity'); | |
| // Variables | |
| let originalImage = null; | |
| let processedImage = null; | |
| // Event Listeners | |
| uploadBtn.addEventListener('click', handleImageUpload); | |
| removeWatermarkBtn.addEventListener('click', removeWatermark); | |
| removeTextBtn.addEventListener('click', removeText); | |
| downloadBtn.addEventListener('click', downloadProcessedImage); | |
| resetBtn.addEventListener('click', resetAll); | |
| removeClothesBtn.addEventListener('click', removeClothes); | |
| // Functions | |
| function handleImageUpload() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = 'image/*'; | |
| input.onchange = e => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| showProcessing(originalProcessing); | |
| const reader = new FileReader(); | |
| reader.onload = event => { | |
| originalImage = new Image(); | |
| originalImage.onload = () => { | |
| displayOriginalImage(); | |
| enableButtons(); | |
| hideProcessing(originalProcessing); | |
| }; | |
| originalImage.src = event.target.result; | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }; | |
| input.click(); | |
| } | |
| function displayOriginalImage() { | |
| originalCanvas.width = originalImage.width; | |
| originalCanvas.height = originalImage.height; | |
| const ctx = originalCanvas.getContext('2d'); | |
| ctx.drawImage(originalImage, 0, 0); | |
| originalCanvas.classList.remove('hidden'); | |
| originalPlaceholder.classList.add('hidden'); | |
| // Copy to processed canvas initially | |
| copyToProcessedCanvas(); | |
| } | |
| function copyToProcessedCanvas() { | |
| processedCanvas.width = originalCanvas.width; | |
| processedCanvas.height = originalCanvas.height; | |
| const ctx = processedCanvas.getContext('2d'); | |
| ctx.drawImage(originalImage, 0, 0); | |
| processedCanvas.classList.remove('hidden'); | |
| processedPlaceholder.classList.add('hidden'); | |
| } | |
| function removeWatermark() { | |
| showProcessing(processedProcessing); | |
| // Simulate processing delay | |
| setTimeout(() => { | |
| const ctx = processedCanvas.getContext('2d'); | |
| const imageData = ctx.getImageData(0, 0, processedCanvas.width, processedCanvas.height); | |
| const data = imageData.data; | |
| // Simple watermark removal algorithm (simulated) | |
| const sensitivity = parseInt(watermarkSensitivity.value) / 100; | |
| for (let i = 0; i < data.length; i += 4) { | |
| // Check for watermark patterns (simplified) | |
| if (Math.random() < sensitivity * 0.2) { | |
| // Blend with surrounding pixels | |
| data[i] = (data[i] + data[i - 4] || data[i] + data[i + 4] || data[i]) / 2; // R | |
| data[i + 1] = (data[i + 1] + data[i - 3] || data[i + 1] + data[i + 5] || data[i + 1]) / 2; // G | |
| data[i + 2] = (data[i + 2] + data[i - 2] || data[i + 2] + data[i + 6] || data[i + 2]) / 2; // B | |
| } | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| hideProcessing(processedProcessing); | |
| // Show success message | |
| showToast('Watermark removed successfully!', 'green'); | |
| }, 1500); | |
| } | |
| function removeText() { | |
| showProcessing(processedProcessing); | |
| // Simulate processing delay | |
| setTimeout(() => { | |
| const ctx = processedCanvas.getContext('2d'); | |
| const imageData = ctx.getImageData(0, 0, processedCanvas.width, processedCanvas.height); | |
| const data = imageData.data; | |
| // Simple text removal algorithm (simulated) | |
| const sensitivity = parseInt(textSensitivity.value) / 100; | |
| for (let i = 0; i < data.length; i += 4) { | |
| // Check for high contrast edges (simplified text detection) | |
| const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3; | |
| const nextBrightness = (data[i + 4] + data[i + 5] + data[i + 6]) / 3; | |
| if (Math.abs(brightness - nextBrightness) > 100 * sensitivity) { | |
| // Inpaint by copying nearby pixels | |
| data[i] = data[i - 4] || data[i + 4] || data[i]; // R | |
| data[i + 1] = data[i - 3] || data[i + 5] || data[i + 1]; // G | |
| data[i + 2] = data[i - 2] || data[i + 6] || data[i + 2]; // B | |
| } | |
| } | |
| ctx.putImageData(imageData, 0, 0); | |
| hideProcessing(processedProcessing); | |
| // Show success message | |
| showToast('Text removed successfully!', 'purple'); | |
| }, 2000); | |
| } | |
| function removeClothes() { | |
| showProcessing(processedProcessing); | |
| // Simulate processing delay with a warning | |
| setTimeout(() => { | |
| hideProcessing(processedProcessing); | |
| showToast('This feature is experimental and may not work perfectly', 'red'); | |
| // In a real app, this would use a more sophisticated algorithm | |
| const ctx = processedCanvas.getContext('2d'); | |
| ctx.drawImage(originalImage, 0, 0); | |
| // Just simulate by making the image slightly transparent | |
| ctx.globalAlpha = 0.7; | |
| ctx.drawImage(originalImage, 0, 0); | |
| ctx.globalAlpha = 1.0; | |
| }, 2500); | |
| } | |
| function downloadProcessedImage() { | |
| const link = document.createElement('a'); | |
| link.download = 'processed-image.png'; | |
| link.href = processedCanvas.toDataURL('image/png'); | |
| link.click(); | |
| showToast('Image downloaded!', 'blue'); | |
| } | |
| function resetAll() { | |
| originalImage = null; | |
| processedImage = null; | |
| originalCanvas.classList.add('hidden'); | |
| processedCanvas.classList.add('hidden'); | |
| originalPlaceholder.classList.remove('hidden'); | |
| processedPlaceholder.classList.remove('hidden'); | |
| disableButtons(); | |
| showToast('Reset complete. Ready for new image.', 'gray'); | |
| } | |
| function enableButtons() { | |
| removeWatermarkBtn.disabled = false; | |
| removeTextBtn.disabled = false; | |
| downloadBtn.disabled = false; | |
| resetBtn.disabled = false; | |
| removeClothesBtn.disabled = false; | |
| } | |
| function disableButtons() { | |
| removeWatermarkBtn.disabled = true; | |
| removeTextBtn.disabled = true; | |
| downloadBtn.disabled = true; | |
| resetBtn.disabled = true; | |
| removeClothesBtn.disabled = true; | |
| } | |
| function showProcessing(element) { | |
| element.style.display = 'flex'; | |
| } | |
| function hideProcessing(element) { | |
| element.style.display = 'none'; | |
| } | |
| function showToast(message, color) { | |
| const toast = document.createElement('div'); | |
| toast.className = `fixed bottom-4 right-4 bg-${color}-500 text-white px-4 py-2 rounded-lg shadow-lg transition-opacity duration-300 opacity-0`; | |
| toast.textContent = message; | |
| document.body.appendChild(toast); | |
| setTimeout(() => { | |
| toast.classList.add('opacity-100'); | |
| }, 10); | |
| setTimeout(() => { | |
| toast.classList.remove('opacity-100'); | |
| setTimeout(() => { | |
| document.body.removeChild(toast); | |
| }, 300); | |
| }, 3000); | |
| } | |
| </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-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=hader1890/enzostvs-deepsite" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |