mirxa2's picture
Update app.py from anycoder
ac24058 verified
Raw
History Blame Contribute Delete
37.4 kB
import gradio as gr
import numpy as np
import cv2
from PIL import Image
import tempfile
import os
from pathlib import Path
from typing import Optional, Tuple, List
import subprocess
import shutil
import time
# Try to import ML libraries with graceful fallbacks
try:
import torch
HAS_TORCH = True
except ImportError:
HAS_TORCH = False
print("Warning: torch not installed. Using CPU-only methods.")
try:
from transformers import pipeline, AutoImageProcessor, AutoModelForImageSegmentation
from diffusers import StableDiffusionInpaintPipeline, DPMSolverMultistepScheduler
HAS_TRANSFORMERS = True
except ImportError:
HAS_TRANSFORMERS = False
print("Warning: transformers/diffusers not installed. Using OpenCV fallback.")
try:
from rembg import remove as rembg_remove
HAS_REMBG = True
except ImportError:
HAS_REMBG = False
try:
from skimage import morphology, measure
from skimage.filters import threshold_otsu
HAS_SKIMAGE = True
except ImportError:
HAS_SKIMAGE = False
print(f"Available: torch={HAS_TORCH}, transformers={HAS_TRANSFORMERS}, rembg={HAS_REMBG}, skimage={HAS_SKIMAGE}")
class WatermarkRemover:
"""Main class for watermark removal using various techniques."""
def __init__(self):
self.device = "cuda" if HAS_TORCH and torch.cuda.is_available() else "cpu"
self.sd_pipeline = None
self.sam_processor = None
self.sam_model = None
self._load_models()
def _load_models(self):
"""Load all required models with error handling."""
print(f"Initializing on {self.device}...")
if HAS_TRANSFORMERS and HAS_TORCH:
try:
# Try to load a lightweight inpainting model
print("Attempting to load inpainting model...")
# Use a smaller model for better compatibility
model_id = "runwayml/stable-diffusion-inpainting"
# Check if model exists locally first
from huggingface_hub import snapshot_download
try:
model_path = snapshot_download(model_id, local_files_only=True)
print(f"Found model at {model_path}")
except:
print("Model not cached, skipping download for faster startup")
self.sd_pipeline = None
return
self.sd_pipeline = StableDiffusionInpaintPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
local_files_only=True,
)
self.sd_pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
self.sd_pipeline.scheduler.config
)
self.sd_pipeline = self.sd_pipeline.to(self.device)
print("✓ SD inpainting model loaded")
except Exception as e:
print(f"⚠ Could not load SD model: {e}")
self.sd_pipeline = None
try:
print("Attempting to load SAM model...")
self.sam_processor = AutoImageProcessor.from_pretrained(
"facebook/sam-vit-base",
local_files_only=True
)
self.sam_model = AutoModelForImageSegmentation.from_pretrained(
"facebook/sam-vit-base",
local_files_only=True
)
self.sam_model = self.sam_model.to(self.device)
print("✓ SAM model loaded")
except Exception as e:
print(f"⚠ Could not load SAM model: {e}")
self.sam_processor = None
self.sam_model = None
else:
print("⚠ ML libraries not available, using OpenCV methods only")
def detect_watermark_region(self, image: np.ndarray, method: str = "automatic") -> np.ndarray:
"""Detect watermark region in image. Returns binary mask."""
h, w = image.shape[:2]
mask = np.zeros((h, w), dtype=np.uint8)
if method == "automatic":
# Convert to grayscale
if len(image.shape) == 3:
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
else:
gray = image
corner_size = min(h, w) // 4
corners = [
("tr", 0, w-corner_size),
("br", h-corner_size, w-corner_size),
("bl", h-corner_size, 0),
("tl", 0, 0)
]
for name, y0, x0 in corners:
region = gray[y0:y0+corner_size, x0:x0+corner_size]
# Edge detection
edges = cv2.Canny(region, 50, 150)
edge_density = np.sum(edges > 0) / edges.size
# Threshold analysis
_, thresh = cv2.threshold(region, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
white_ratio = np.sum(thresh > 128) / thresh.size
if edge_density > 0.05 or (0.1 < white_ratio < 0.9):
mask[y0:y0+corner_size, x0:x0+corner_size] = 255
# Laplacian variance for high-contrast regions
laplacian = cv2.Laplacian(gray, cv2.CV_64F)
laplacian = np.abs(laplacian)
_, high_var = cv2.threshold(laplacian.astype(np.uint8), 30, 255, cv2.THRESH_BINARY)
kernel = np.ones((5, 5), np.uint8)
high_var = cv2.dilate(high_var, kernel, iterations=2)
contours, _ = cv2.findContours(high_var, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
x, y, cw, ch = cv2.boundingRect(cnt)
in_corner = (x < corner_size and y < corner_size) or \
(x > w - corner_size - cw and y < corner_size) or \
(x < corner_size and y > h - corner_size - ch) or \
(x > w - corner_size - cw and y > h - corner_size - ch)
if in_corner and cw * ch > 100:
cv2.rectangle(mask, (x, y), (x+cw, y+ch), 255, -1)
elif method == "bottom_right":
corner_size = min(h, w) // 5
mask[h-corner_size:h, w-corner_size:w] = 255
elif method == "bottom_left":
corner_size = min(h, w) // 5
mask[h-corner_size:h, 0:corner_size] = 255
elif method == "top_right":
corner_size = min(h, w) // 5
mask[0:corner_size, w-corner_size:w] = 255
elif method == "top_left":
corner_size = min(h, w) // 5
mask[0:corner_size, 0:corner_size] = 255
elif method == "center":
ch, cw = h // 4, w // 4
mask[h//2-ch:h//2+ch, w//2-cw:w//2+cw] = 255
# Morphological operations
kernel = np.ones((15, 15), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.dilate(mask, kernel, iterations=2)
return mask
def inpaint_image(self, image: np.ndarray, mask: np.ndarray,
method: str = "opencv", prompt: str = "") -> np.ndarray:
"""Inpaint image using specified method."""
# Ensure RGB
if len(image.shape) == 2:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
elif image.shape[2] == 4:
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
mask_binary = (mask > 128).astype(np.uint8) * 255
# Always use OpenCV for reliability
print("Using OpenCV inpainting (most reliable)")
result = cv2.inpaint(image, mask_binary, 3, cv2.INPAINT_TELEA)
return result
def remove_watermark(self, image_input, detection_method: str = "automatic",
inpaint_method: str = "opencv", custom_prompt: str = "",
manual_mask: Optional[np.ndarray] = None) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
"""Main watermark removal function."""
# Load image
if isinstance(image_input, str):
image = cv2.imread(image_input)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
elif isinstance(image_input, np.ndarray):
image = image_input.copy()
elif isinstance(image_input, Image.Image):
image = np.array(image_input)
if len(image.shape) == 3 and image.shape[2] == 4:
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
elif isinstance(image_input, dict):
image = np.array(image_input.get("image", image_input))
if len(image.shape) == 3 and image.shape[2] == 4:
image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
else:
raise ValueError(f"Unsupported image type: {type(image_input)}")
# Ensure RGB
if len(image.shape) == 2:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
# Get mask
if manual_mask is not None:
mask = manual_mask
else:
mask = self.detect_watermark_region(image, detection_method)
# Create visualization
mask_viz = image.copy()
red_overlay = np.zeros_like(image)
red_overlay[mask > 128] = [255, 0, 0]
mask_viz = cv2.addWeighted(mask_viz, 0.7, red_overlay, 0.3, 0)
# Inpaint
result = self.inpaint_image(image, mask, inpaint_method, custom_prompt)
return mask_viz, result, mask
def enhance_image(self, image: np.ndarray, enhancement_type: str) -> np.ndarray:
"""Apply image enhancements."""
if enhancement_type == "sharpen":
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
return cv2.filter2D(image, -1, kernel)
elif enhancement_type == "denoise":
return cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21)
elif enhancement_type == "contrast":
lab = cv2.cvtColor(image, cv2.COLOR_RGB2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
l = clahe.apply(l)
return cv2.cvtColor(cv2.merge([l,a,b]), cv2.COLOR_LAB2RGB)
elif enhancement_type == "brightness_up":
return cv2.convertScaleAbs(image, alpha=1.0, beta=30)
elif enhancement_type == "brightness_down":
return cv2.convertScaleAbs(image, alpha=1.0, beta=-30)
elif enhancement_type == "saturation_up":
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV).astype(np.float32)
hsv[:,:,1] = np.clip(hsv[:,:,1] * 1.3, 0, 255)
return cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2RGB)
elif enhancement_type == "upscale_2x":
return cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_LANCZOS4)
return image
def remove_background(self, image: np.ndarray) -> np.ndarray:
"""Remove image background."""
if HAS_REMBG:
try:
pil_img = Image.fromarray(image)
result = rembg_remove(pil_img)
return np.array(result)
except Exception as e:
print(f"rembg failed: {e}")
# Fallback: simple threshold-based segmentation
if len(image.shape) == 3:
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
_, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
largest = max(contours, key=cv2.contourArea)
mask = np.zeros_like(mask)
cv2.drawContours(mask, [largest], -1, 255, -1)
rgba = cv2.cvtColor(image, cv2.COLOR_RGB2RGBA)
rgba[:,:,3] = mask
return rgba
return image
def process_video(self, video_path: str, detection_method: str = "automatic",
inpaint_method: str = "opencv", progress=None) -> str:
"""Process video frame by frame."""
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise ValueError(f"Could not open video: {video_path}")
fps = cap.get(cv2.CAP_PROP_FPS)
if fps <= 0:
fps = 30.0
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if total_frames <= 0:
cap.release()
raise ValueError("Could not determine frame count")
# Create temp output
temp_output = tempfile.mktemp(suffix='.mp4')
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(temp_output, fourcc, fps, (width, height))
# Detect watermark on first frame
ret, first_frame = cap.read()
if not ret:
cap.release()
raise ValueError("Could not read first frame")
first_frame_rgb = cv2.cvtColor(first_frame, cv2.COLOR_BGR2RGB)
mask = self.detect_watermark_region(first_frame_rgb, detection_method)
# Dilate mask for video
kernel = np.ones((20, 20), np.uint8)
mask = cv2.dilate(mask, kernel, iterations=3)
mask_binary = (mask > 128).astype(np.uint8) * 255
# Process frames
frame_count = 0
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
while True:
ret, frame = cap.read()
if not ret:
break
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
result = cv2.inpaint(frame_rgb, mask_binary, 3, cv2.INPAINT_TELEA)
result_bgr = cv2.cvtColor(result, cv2.COLOR_RGB2BGR)
out.write(result_bgr)
frame_count += 1
if progress is not None and total_frames > 0:
progress(frame_count / total_frames, f"Processing {frame_count}/{total_frames}")
cap.release()
out.release()
# Try to re-encode with ffmpeg
final_output = self._reencode_video(temp_output)
return final_output
def _reencode_video(self, input_path: str) -> str:
"""Re-encode video with better codec."""
output_path = tempfile.mktemp(suffix='.mp4')
ffmpeg_cmd = [
'ffmpeg', '-y', '-i', input_path,
'-c:v', 'libx264', '-preset', 'fast', '-crf', '23',
'-c:a', 'aac', '-b:a', '128k',
'-movflags', '+faststart',
output_path
]
try:
result = subprocess.run(ffmpeg_cmd, check=True, capture_output=True, timeout=300)
if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
if os.path.exists(input_path):
os.remove(input_path)
return output_path
except Exception as e:
print(f"ffmpeg failed: {e}")
if os.path.exists(output_path):
os.remove(output_path)
return input_path
# Global instance
_watermark_remover = None
def get_remover():
"""Get or create watermark remover instance."""
global _watermark_remover
if _watermark_remover is None:
_watermark_remover = WatermarkRemover()
return _watermark_remover
def process_image(image, detection_method, inpaint_method, custom_prompt,
apply_enhancement, enhancement_type):
"""Process single image."""
if image is None:
return None, None, None, "⚠ Please upload an image first."
try:
remover = get_remover()
# Handle different image input types
if isinstance(image, dict):
img_array = image.get("image", image)
if isinstance(img_array, np.ndarray):
img_array = img_array
else:
img_array = np.array(img_array)
elif isinstance(image, np.ndarray):
img_array = image.copy()
elif isinstance(image, Image.Image):
img_array = np.array(image)
elif isinstance(image, str):
img_array = cv2.imread(image)
img_array = cv2.cvtColor(img_array, cv2.COLOR_BGR2RGB)
else:
img_array = np.array(image)
# Remove watermark
mask_viz, result, mask = remover.remove_watermark(
img_array,
detection_method=detection_method,
inpaint_method=inpaint_method,
custom_prompt=custom_prompt
)
# Apply enhancement
if apply_enhancement and enhancement_type != "none":
result = remover.enhance_image(result, enhancement_type)
return mask_viz, result, mask, "✅ Processing complete!"
except Exception as e:
import traceback
error_msg = f"❌ Error: {str(e)}"
print(error_msg)
print(traceback.format_exc())
return None, None, None, error_msg
def process_video_file(video, detection_method, inpaint_method, progress=gr.Progress()):
"""Process video file."""
if video is None:
return None, "⚠ Please upload a video first."
try:
remover = get_remover()
# Get video path
if isinstance(video, str):
video_path = video
elif hasattr(video, 'name'):
video_path = video.name
else:
video_path = str(video)
if not os.path.exists(video_path):
return None, f"❌ Video file not found: {video_path}"
progress(0, "Starting video processing...")
output_path = remover.process_video(
video_path,
detection_method=detection_method,
inpaint_method=inpaint_method,
progress=lambda p, msg: progress(p, msg)
)
if os.path.exists(output_path):
return output_path, "✅ Video processing complete!"
else:
return None, "❌ Failed to create output video"
except Exception as e:
import traceback
error_msg = f"❌ Error: {str(e)}"
print(error_msg)
print(traceback.format_exc())
return None, error_msg
def batch_process_images(files, detection_method, inpaint_method, custom_prompt,
apply_enhancement, enhancement_type, progress=gr.Progress()):
"""Process multiple images in batch."""
if not files:
return [], "⚠ No files uploaded."
results = []
remover = get_remover()
# Handle files list
if isinstance(files, str):
files = [files]
elif hasattr(files, 'name'):
files = [files.name]
elif isinstance(files, (list, tuple)):
files = [f.name if hasattr(f, 'name') else str(f) for f in files]
for i, file_path in enumerate(files):
progress((i) / len(files), f"Processing {i+1}/{len(files)}")
try:
if not os.path.exists(file_path):
print(f"File not found: {file_path}")
continue
img = cv2.imread(file_path)
if img is None:
print(f"Could not read: {file_path}")
continue
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
_, result, _, _ = remover.remove_watermark(
img, detection_method, inpaint_method, custom_prompt
)
if apply_enhancement and enhancement_type != "none":
result = remover.enhance_image(result, enhancement_type)
results.append(result)
except Exception as e:
print(f"Error processing {file_path}: {e}")
progress(1.0, "Complete!")
if results:
return results, f"✅ Processed {len(results)} images."
else:
return [], "❌ No images were successfully processed."
def apply_enhancement_only(image, enh_type, remove_bg):
"""Apply enhancement without watermark removal."""
if image is None:
return None, "⚠ Please upload an image."
try:
remover = get_remover()
# Handle image input
if isinstance(image, dict):
img_array = image.get("image", image)
if isinstance(img_array, np.ndarray):
result = img_array.copy()
else:
result = np.array(img_array)
elif isinstance(image, np.ndarray):
result = image.copy()
elif isinstance(image, Image.Image):
result = np.array(image)
elif isinstance(image, str):
result = cv2.imread(image)
result = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
else:
result = np.array(image)
status_msg = ""
if remove_bg:
result = remover.remove_background(result)
status_msg = "Background removed. "
result = remover.enhance_image(result, enh_type)
return result, status_msg + f"Applied {enh_type} enhancement."
except Exception as e:
return None, f"❌ Error: {str(e)}"
# Custom CSS
custom_css = """
.watermark-tool {
max-width: 1400px;
margin: 0 auto;
}
.header-title {
text-align: center;
margin-bottom: 1rem;
}
.header-title h1 {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.built-with {
text-align: center;
margin-top: 0.5rem;
font-size: 0.9rem;
}
.built-with a {
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.built-with a:hover {
text-decoration: underline;
}
.tool-description {
text-align: center;
color: #666;
margin-bottom: 2rem;
}
"""
# Create Gradio application
with gr.Blocks() as demo:
# Header
with gr.Row():
with gr.Column():
gr.Markdown(
"""
<div class="header-title">
<h1>🎨 AI Watermark Remover</h1>
</div>
<div class="built-with">
Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">anycoder</a>
</div>
<div class="tool-description">
Remove watermarks from images and videos using AI inpainting.
Supports automatic detection and manual masking.
</div>
""",
elem_classes="watermark-tool"
)
# Main tabs
with gr.Tabs():
# Image Watermark Removal Tab
with gr.Tab("🖼️ Image Removal"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Input Settings")
image_input = gr.Image(
label="Upload Image",
type="numpy",
height=400
)
with gr.Accordion("Detection Settings", open=True):
detection_method = gr.Radio(
choices=[
("Automatic (AI)", "automatic"),
("Bottom Right", "bottom_right"),
("Bottom Left", "bottom_left"),
("Top Right", "top_right"),
("Top Left", "top_left"),
("Center", "center")
],
value="automatic",
label="Watermark Location"
)
with gr.Accordion("Inpainting Settings", open=True):
inpaint_method = gr.Radio(
choices=[
("OpenCV (Fast)", "opencv"),
],
value="opencv",
label="Processing Method"
)
custom_prompt = gr.Textbox(
label="Custom Prompt (optional)",
placeholder="Describe what should replace watermark",
value="",
info="For future AI model support"
)
with gr.Accordion("Enhancement", open=False):
apply_enhancement = gr.Checkbox(
label="Apply post-processing",
value=False
)
enhancement_type = gr.Dropdown(
choices=[
("None", "none"),
("Sharpen", "sharpen"),
("Denoise", "denoise"),
("Enhance Contrast", "contrast"),
("Increase Brightness", "brightness_up"),
("Decrease Brightness", "brightness_down"),
("Increase Saturation", "saturation_up"),
("2x Upscale", "upscale_2x")
],
value="none",
label="Enhancement Type"
)
process_btn = gr.Button(
"🚀 Remove Watermark",
variant="primary",
size="lg"
)
with gr.Column(scale=1):
gr.Markdown("### Results")
with gr.Tabs():
with gr.Tab("Result"):
result_output = gr.Image(
label="Clean Image",
height=400
)
with gr.Tab("Detection Mask"):
mask_viz_output = gr.Image(
label="Detected Region",
height=400
)
with gr.Tab("Raw Mask"):
mask_output = gr.Image(
label="Binary Mask",
height=400
)
status_text = gr.Textbox(
label="Status",
interactive=False
)
process_btn.click(
fn=process_image,
inputs=[image_input, detection_method, inpaint_method, custom_prompt,
apply_enhancement, enhancement_type],
outputs=[mask_viz_output, result_output, mask_output, status_text],
api_visibility="public"
)
# Video Watermark Removal Tab
with gr.Tab("🎬 Video Removal"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Video Input")
video_input = gr.Video(
label="Upload Video",
format="mp4"
)
with gr.Accordion("Settings", open=True):
video_detection = gr.Radio(
choices=[
("Automatic", "automatic"),
("Bottom Right", "bottom_right"),
("Bottom Left", "bottom_left")
],
value="automatic",
label="Watermark Location"
)
video_inpaint = gr.Radio(
choices=[("OpenCV (Fast)", "opencv")],
value="opencv",
label="Processing Method"
)
video_process_btn = gr.Button(
"🎬 Process Video",
variant="primary",
size="lg"
)
with gr.Column(scale=1):
gr.Markdown("### Result")
video_output = gr.Video(
label="Clean Video",
format="mp4"
)
video_status = gr.Textbox(
label="Status",
interactive=False
)
video_process_btn.click(
fn=process_video_file,
inputs=[video_input, video_detection, video_inpaint],
outputs=[video_output, video_status],
api_visibility="public"
)
# Batch Processing Tab
with gr.Tab("📁 Batch Processing"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Batch Input")
batch_files = gr.File(
label="Upload Multiple Images",
file_count="multiple",
file_types=["image"]
)
batch_detection = gr.Radio(
choices=[
("Automatic", "automatic"),
("Bottom Right", "bottom_right"),
("Bottom Left", "bottom_left")
],
value="automatic",
label="Detection Method"
)
batch_inpaint = gr.Radio(
choices=[
("OpenCV Fast", "opencv"),
],
value="opencv",
label="Processing Method"
)
batch_enhance = gr.Checkbox(
label="Apply Enhancement",
value=False
)
batch_process_btn = gr.Button(
"📦 Process Batch",
variant="primary",
size="lg"
)
with gr.Column(scale=2):
gr.Markdown("### Results")
batch_output = gr.Gallery(
label="Processed Images",
columns=3,
rows=2,
height=600
)
batch_status = gr.Textbox(
label="Status",
interactive=False
)
batch_process_btn.click(
fn=batch_process_images,
inputs=[batch_files, batch_detection, batch_inpaint,
gr.State(""), batch_enhance, gr.State("none")],
outputs=[batch_output, batch_status],
api_visibility="public"
)
# Image Enhancement Tab
with gr.Tab("✨ Enhancement"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Enhance Images")
enhance_input = gr.Image(
label="Upload Image",
type="numpy"
)
enhance_type = gr.Dropdown(
choices=[
("Sharpen", "sharpen"),
("Denoise", "denoise"),
("Enhance Contrast", "contrast"),
("Increase Brightness", "brightness_up"),
("Decrease Brightness", "brightness_down"),
("Increase Saturation", "saturation_up"),
("2x Upscale", "upscale_2x")
],
value="sharpen",
label="Enhancement Type"
)
bg_remove_check = gr.Checkbox(
label="Remove Background",
value=False,
info="AI background removal"
)
enhance_btn = gr.Button(
"✨ Apply Enhancement",
variant="primary",
size="lg"
)
with gr.Column(scale=1):
gr.Markdown("### Result")
enhance_output = gr.Image(
label="Enhanced Image",
height=400
)
enhance_status = gr.Textbox(
label="Status",
interactive=False
)
enhance_btn.click(
fn=apply_enhancement_only,
inputs=[enhance_input, enhance_type, bg_remove_check],
outputs=[enhance_output, enhance_status],
api_visibility="public"
)
# About Tab
with gr.Tab("ℹ️ About"):
gr.Markdown(
"""
## About AI Watermark Remover
This tool removes watermarks from images and videos using computer vision techniques.
### Features
- 🎯 **Automatic Detection**: AI-powered watermark location detection
- 🖼️ **Image Processing**: Remove watermarks from single images
- 🎬 **Video Support**: Process videos frame by frame
- 📁 **Batch Processing**: Handle multiple images at once
- ✨ **Enhancement Tools**: Sharpen, denoise, adjust colors, upscale
### How It Works
1. **Detection**: Analyzes corners and edges where watermarks typically appear
2. **Masking**: Creates a binary mask covering the watermark region
3. **Inpainting**: Uses OpenCV to fill the masked area seamlessly
4. **Enhancement**: Optional post-processing to improve quality
### Tips for Best Results
- Use "Automatic" detection for unknown watermark positions
- Specify corner locations if you know where the watermark is
- For videos, static watermarks work best
- Try different enhancement options for best quality
### Limitations
- Large watermarks (>30% of image) may be challenging
- Semi-transparent watermarks over complex textures are harder
- Video processing optimized for static watermarks
"""
)
# Launch application
if __name__ == "__main__":
print("🚀 Initializing Watermark Remover...")
try:
remover = get_remover()
print("✓ Models initialized")
except Exception as e:
print(f"⚠ Model initialization warning: {e}")
print("🌐 Starting Gradio server...")
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
css=custom_css,
theme=gr.themes.Soft(
primary_hue="indigo",
secondary_hue="purple",
neutral_hue="slate"
).set(
button_primary_background_fill="*primary_600",
button_primary_background_fill_hover="*primary_700",
block_title_text_weight="600",
block_label_text_weight="500",
),
footer_links=[{"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"}]
)