face2 / index.html
6ee5ali's picture
import cv2 import numpy as np def remove_sticker(image_path, mask_path, output_path): # قراءة الصورة والقناع (اللاصقة) img = cv2.imread(image_path) mask = cv2.imread(mask_path, 0) # قراءة كصورة رمادية # تطبيق خوارزمية الاستعادة result = cv2.inpaint(img, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA) # حفظ النتيجة cv2.imwrite(output_path, result) - Initial Deployment
ba6a049 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sticker Remover 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>
.dropzone {
border: 2px dashed #ccc;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #4f46e5;
background-color: #eef2ff;
}
.canvas-container {
position: relative;
margin: 0 auto;
}
.processing-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.7);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
z-index: 10;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(255,255,255,0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.brush-size-control {
-webkit-appearance: none;
width: 100%;
height: 8px;
border-radius: 4px;
background: #d1d5db;
}
.brush-size-control::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #4f46e5;
cursor: pointer;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="text-4xl font-bold text-indigo-600 mb-2">Sticker Remover</h1>
<p class="text-gray-600 max-w-2xl mx-auto">
Remove unwanted stickers, watermarks, or objects from your images with our AI-powered tool.
</p>
</header>
<div class="bg-white rounded-xl shadow-lg overflow-hidden max-w-4xl mx-auto">
<div class="md:flex">
<!-- Left Panel - Controls -->
<div class="md:w-1/3 p-6 bg-gray-50 border-r border-gray-200">
<div class="space-y-6">
<div>
<h2 class="text-lg font-medium text-gray-900 mb-3">Upload Image</h2>
<div id="image-dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-cloud-upload-alt text-4xl text-indigo-400 mb-3"></i>
<p class="text-gray-500 mb-1">Drag & drop your image here</p>
<p class="text-sm text-gray-400">or click to browse</p>
<input type="file" id="image-input" accept="image/*" class="hidden">
</div>
</div>
</div>
<div id="mask-controls" class="hidden">
<h2 class="text-lg font-medium text-gray-900 mb-3">Mark the Sticker</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">Brush Size</label>
<input type="range" min="1" max="50" value="15" class="brush-size-control" id="brush-size">
<div class="flex justify-between text-xs text-gray-500 mt-1">
<span>Small</span>
<span>Large</span>
</div>
</div>
<div class="flex space-x-3">
<button id="draw-btn" class="flex-1 bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition flex items-center justify-center">
<i class="fas fa-paint-brush mr-2"></i> Draw
</button>
<button id="erase-btn" class="flex-1 bg-gray-200 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-300 transition flex items-center justify-center">
<i class="fas fa-eraser mr-2"></i> Erase
</button>
</div>
<div class="flex space-x-3">
<button id="clear-btn" class="flex-1 bg-gray-200 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-300 transition">
Clear Mask
</button>
<button id="process-btn" class="flex-1 bg-indigo-600 text-white py-2 px-4 rounded-md hover:bg-indigo-700 transition">
Remove Sticker
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Right Panel - Canvas -->
<div class="md:w-2/3 p-6">
<div class="canvas-container">
<div id="canvas-wrapper" class="relative">
<canvas id="image-canvas" class="max-w-full border border-gray-200 rounded-lg hidden"></canvas>
<canvas id="mask-canvas" class="absolute top-0 left-0 max-w-full border border-gray-200 rounded-lg hidden" style="opacity: 0.5;"></canvas>
<div id="dropzone-placeholder" class="flex items-center justify-center h-64 bg-gray-100 rounded-lg">
<div class="text-center p-6">
<i class="fas fa-image text-4xl text-gray-300 mb-3"></i>
<h3 class="text-lg font-medium text-gray-500">No image selected</h3>
<p class="text-gray-400 mt-1">Upload an image to get started</p>
</div>
</div>
<div id="processing-overlay" class="processing-overlay hidden">
<div class="spinner mb-4"></div>
<p class="text-xl font-medium mb-2">Processing Image</p>
<p class="text-sm text-gray-300">Removing sticker and reconstructing image...</p>
</div>
</div>
<div id="result-controls" class="mt-4 hidden">
<div class="flex justify-between">
<button id="download-btn" class="bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 transition flex items-center">
<i class="fas fa-download mr-2"></i> Download Result
</button>
<button id="reset-btn" class="bg-gray-200 text-gray-700 py-2 px-4 rounded-md hover:bg-gray-300 transition">
Start Over
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-12 bg-white rounded-xl shadow-lg overflow-hidden max-w-4xl mx-auto p-6">
<h2 class="text-xl font-bold text-gray-800 mb-4">How It Works</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="text-center">
<div class="bg-indigo-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-upload text-indigo-600 text-2xl"></i>
</div>
<h3 class="font-medium text-gray-800 mb-2">1. Upload Image</h3>
<p class="text-gray-600 text-sm">Select an image containing the sticker or object you want to remove.</p>
</div>
<div class="text-center">
<div class="bg-indigo-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-paint-brush text-indigo-600 text-2xl"></i>
</div>
<h3 class="font-medium text-gray-800 mb-2">2. Mark the Area</h3>
<p class="text-gray-600 text-sm">Use the brush tool to mark the exact area you want to remove.</p>
</div>
<div class="text-center">
<div class="bg-indigo-100 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4">
<i class="fas fa-magic text-indigo-600 text-2xl"></i>
</div>
<h3 class="font-medium text-gray-800 mb-2">3. Download Result</h3>
<p class="text-gray-600 text-sm">Our AI will seamlessly remove the marked area and reconstruct the image.</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const imageDropzone = document.getElementById('image-dropzone');
const imageInput = document.getElementById('image-input');
const dropzonePlaceholder = document.getElementById('dropzone-placeholder');
const imageCanvas = document.getElementById('image-canvas');
const maskCanvas = document.getElementById('mask-canvas');
const maskControls = document.getElementById('mask-controls');
const resultControls = document.getElementById('result-controls');
const processingOverlay = document.getElementById('processing-overlay');
const drawBtn = document.getElementById('draw-btn');
const eraseBtn = document.getElementById('erase-btn');
const clearBtn = document.getElementById('clear-btn');
const processBtn = document.getElementById('process-btn');
const downloadBtn = document.getElementById('download-btn');
const resetBtn = document.getElementById('reset-btn');
const brushSizeControl = document.getElementById('brush-size');
// Canvas contexts
const imageCtx = imageCanvas.getContext('2d');
const maskCtx = maskCanvas.getContext('2d');
// State variables
let isDrawing = false;
let isErasing = false;
let brushSize = 15;
let originalImage = null;
// Event Listeners
imageDropzone.addEventListener('click', () => imageInput.click());
imageInput.addEventListener('change', handleImageUpload);
// Drag and drop events
imageDropzone.addEventListener('dragover', (e) => {
e.preventDefault();
imageDropzone.classList.add('active');
});
imageDropzone.addEventListener('dragleave', () => {
imageDropzone.classList.remove('active');
});
imageDropzone.addEventListener('drop', (e) => {
e.preventDefault();
imageDropzone.classList.remove('active');
if (e.dataTransfer.files.length) {
imageInput.files = e.dataTransfer.files;
handleImageUpload({ target: imageInput });
}
});
// Brush size control
brushSizeControl.addEventListener('input', () => {
brushSize = parseInt(brushSizeControl.value);
});
// Tool buttons
drawBtn.addEventListener('click', () => {
isErasing = false;
drawBtn.classList.remove('bg-gray-200', 'text-gray-700');
drawBtn.classList.add('bg-indigo-600', 'text-white');
eraseBtn.classList.remove('bg-indigo-600', 'text-white');
eraseBtn.classList.add('bg-gray-200', 'text-gray-700');
});
eraseBtn.addEventListener('click', () => {
isErasing = true;
eraseBtn.classList.remove('bg-gray-200', 'text-gray-700');
eraseBtn.classList.add('bg-indigo-600', 'text-white');
drawBtn.classList.remove('bg-indigo-600', 'text-white');
drawBtn.classList.add('bg-gray-200', 'text-gray-700');
});
clearBtn.addEventListener('click', () => {
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
});
processBtn.addEventListener('click', processImage);
downloadBtn.addEventListener('click', downloadResult);
resetBtn.addEventListener('click', resetTool);
// Canvas drawing events
maskCanvas.addEventListener('mousedown', startDrawing);
maskCanvas.addEventListener('mousemove', draw);
maskCanvas.addEventListener('mouseup', stopDrawing);
maskCanvas.addEventListener('mouseout', stopDrawing);
// Touch events for mobile
maskCanvas.addEventListener('touchstart', handleTouchStart);
maskCanvas.addEventListener('touchmove', handleTouchMove);
maskCanvas.addEventListener('touchend', stopDrawing);
// Functions
function handleImageUpload(e) {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = function(event) {
originalImage = new Image();
originalImage.onload = function() {
setupCanvases(originalImage);
dropzonePlaceholder.classList.add('hidden');
imageCanvas.classList.remove('hidden');
maskCanvas.classList.remove('hidden');
maskControls.classList.remove('hidden');
};
originalImage.src = event.target.result;
};
reader.readAsDataURL(file);
}
function setupCanvases(image) {
const maxWidth = 800;
const maxHeight = 600;
let width = image.width;
let height = image.height;
// Scale down if necessary
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width = width * ratio;
height = height * ratio;
}
// Set canvas dimensions
imageCanvas.width = width;
imageCanvas.height = height;
maskCanvas.width = width;
maskCanvas.height = height;
// Draw the image
imageCtx.drawImage(image, 0, 0, width, height);
// Initialize mask canvas with transparent
maskCtx.clearRect(0, 0, width, height);
}
function startDrawing(e) {
isDrawing = true;
draw(e);
}
function draw(e) {
if (!isDrawing) return;
const rect = maskCanvas.getBoundingClientRect();
const x = (e.clientX || e.touches[0].clientX) - rect.left;
const y = (e.clientY || e.touches[0].clientY) - rect.top;
maskCtx.globalCompositeOperation = 'source-over';
maskCtx.fillStyle = isErasing ? 'rgba(0, 0, 0, 0)' : 'rgba(255, 0, 0, 0.5)';
maskCtx.beginPath();
maskCtx.arc(x, y, brushSize, 0, Math.PI * 2);
maskCtx.fill();
}
function handleTouchStart(e) {
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousedown', {
clientX: touch.clientX,
clientY: touch.clientY
});
startDrawing(mouseEvent);
}
}
function handleTouchMove(e) {
e.preventDefault();
if (e.touches.length === 1) {
const touch = e.touches[0];
const mouseEvent = new MouseEvent('mousemove', {
clientX: touch.clientX,
clientY: touch.clientY
});
draw(mouseEvent);
}
}
function stopDrawing() {
isDrawing = false;
}
function processImage() {
processingOverlay.classList.remove('hidden');
// Simulate processing delay
setTimeout(() => {
processingOverlay.classList.add('hidden');
resultControls.classList.remove('hidden');
// In a real app, this would send the image data to a backend service
// that runs the actual OpenCV code
alert("In a real application, this would send the image to a backend service that uses OpenCV to remove the sticker. The demo simulates this process.");
}, 2000);
}
function downloadResult() {
// Create a temporary canvas with the result
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imageCanvas.width;
tempCanvas.height = imageCanvas.height;
const tempCtx = tempCanvas.getContext('2d');
// Draw the original image
tempCtx.drawImage(imageCanvas, 0, 0);
// In a real app, this would apply the actual inpainting algorithm
// Here we just simulate it by blurring the masked area
const maskData = maskCtx.getImageData(0, 0, maskCanvas.width, maskCanvas.height).data;
tempCtx.save();
tempCtx.beginPath();
// Find all red pixels in the mask (our marked area)
for (let y = 0; y < maskCanvas.height; y++) {
for (let x = 0; x < maskCanvas.width; x++) {
const i = (y * maskCanvas.width + x) * 4;
if (maskData[i] > 200 && maskData[i+1] < 50 && maskData[i+2] < 50) {
tempCtx.rect(x, y, 1, 1);
}
}
}
tempCtx.clip();
// Apply a blur effect to simulate inpainting
tempCtx.filter = 'blur(8px)';
tempCtx.drawImage(imageCanvas, 0, 0);
tempCtx.filter = 'none';
tempCtx.restore();
// Download the result
const link = document.createElement('a');
link.download = 'sticker-removed.png';
link.href = tempCanvas.toDataURL('image/png');
link.click();
}
function resetTool() {
// Reset all elements to initial state
imageCanvas.classList.add('hidden');
maskCanvas.classList.add('hidden');
dropzonePlaceholder.classList.remove('hidden');
maskControls.classList.add('hidden');
resultControls.classList.add('hidden');
// Clear canvases
imageCtx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);
maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
// Reset input
imageInput.value = '';
originalImage = null;
// Reset tools
isErasing = false;
drawBtn.classList.remove('bg-gray-200', 'text-gray-700');
drawBtn.classList.add('bg-indigo-600', 'text-white');
eraseBtn.classList.remove('bg-indigo-600', 'text-white');
eraseBtn.classList.add('bg-gray-200', 'text-gray-700');
}
});
</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=6ee5ali/face2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>