Delete watermark_remover.py
Browse files- watermark_remover.py +0 -92
watermark_remover.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
from PIL import Image
|
| 3 |
-
import io
|
| 4 |
-
import os
|
| 5 |
-
|
| 6 |
-
ALPHA_THRESHOLD = 0.002
|
| 7 |
-
MAX_ALPHA = 0.99
|
| 8 |
-
LOGO_VALUE = 255
|
| 9 |
-
|
| 10 |
-
def get_alpha_map(size: int) -> np.ndarray:
|
| 11 |
-
"""
|
| 12 |
-
Load the background image and calculate the alpha map.
|
| 13 |
-
"""
|
| 14 |
-
bg_path = f"assets/bg_{size}.png"
|
| 15 |
-
if not os.path.exists(bg_path):
|
| 16 |
-
# Retry with absolute path relative to this file
|
| 17 |
-
base_dir = os.path.dirname(os.path.abspath(__file__))
|
| 18 |
-
bg_path = os.path.join(base_dir, "assets", f"bg_{size}.png")
|
| 19 |
-
if not os.path.exists(bg_path):
|
| 20 |
-
raise FileNotFoundError(f"Background image {bg_path} not found.")
|
| 21 |
-
|
| 22 |
-
with Image.open(bg_path) as img:
|
| 23 |
-
img = img.convert("RGBA")
|
| 24 |
-
data = np.array(img, dtype=np.float32)
|
| 25 |
-
|
| 26 |
-
# Max of RGB channels normalized to [0, 1]
|
| 27 |
-
r = data[:, :, 0]
|
| 28 |
-
g = data[:, :, 1]
|
| 29 |
-
b = data[:, :, 2]
|
| 30 |
-
|
| 31 |
-
max_channel = np.maximum(np.maximum(r, g), b)
|
| 32 |
-
alpha_map = max_channel / 255.0
|
| 33 |
-
return alpha_map
|
| 34 |
-
|
| 35 |
-
def remove_watermark(image_bytes: bytes) -> tuple[bytes, str]:
|
| 36 |
-
"""
|
| 37 |
-
Remove the Gemini watermark from an image given as bytes.
|
| 38 |
-
Returns (processed_image_bytes, format).
|
| 39 |
-
"""
|
| 40 |
-
with Image.open(io.BytesIO(image_bytes)) as img:
|
| 41 |
-
img_format = img.format or 'PNG'
|
| 42 |
-
|
| 43 |
-
if img.mode != 'RGBA' and img.mode != 'RGB':
|
| 44 |
-
img = img.convert('RGB')
|
| 45 |
-
|
| 46 |
-
data = np.array(img, dtype=np.float32)
|
| 47 |
-
height, width = data.shape[:2]
|
| 48 |
-
|
| 49 |
-
if width > 1024 and height > 1024:
|
| 50 |
-
logo_size = 96
|
| 51 |
-
margin_right = 64
|
| 52 |
-
margin_bottom = 64
|
| 53 |
-
else:
|
| 54 |
-
logo_size = 48
|
| 55 |
-
margin_right = 32
|
| 56 |
-
margin_bottom = 32
|
| 57 |
-
|
| 58 |
-
x = width - margin_right - logo_size
|
| 59 |
-
y = height - margin_bottom - logo_size
|
| 60 |
-
|
| 61 |
-
# If the image is smaller than the required margins + logo, return as is
|
| 62 |
-
if x < 0 or y < 0:
|
| 63 |
-
return image_bytes, img_format
|
| 64 |
-
|
| 65 |
-
alpha_map = get_alpha_map(logo_size)
|
| 66 |
-
|
| 67 |
-
# Crop the region to process
|
| 68 |
-
region = data[y:y+logo_size, x:x+logo_size, :3]
|
| 69 |
-
|
| 70 |
-
# Apply reverse blending
|
| 71 |
-
for c in range(3):
|
| 72 |
-
# Mask applied only where alpha >= ALPHA_THRESHOLD
|
| 73 |
-
mask = alpha_map >= ALPHA_THRESHOLD
|
| 74 |
-
|
| 75 |
-
alpha_limited = np.minimum(alpha_map[mask], MAX_ALPHA)
|
| 76 |
-
one_minus_alpha = 1.0 - alpha_limited
|
| 77 |
-
|
| 78 |
-
watermarked = region[mask, c]
|
| 79 |
-
original = (watermarked - alpha_limited * LOGO_VALUE) / one_minus_alpha
|
| 80 |
-
|
| 81 |
-
# Clip to [0, 255] and round
|
| 82 |
-
region[mask, c] = np.clip(np.round(original), 0, 255)
|
| 83 |
-
|
| 84 |
-
# Update data with region
|
| 85 |
-
data[y:y+logo_size, x:x+logo_size, :3] = region
|
| 86 |
-
|
| 87 |
-
# Create output image
|
| 88 |
-
out_img = Image.fromarray(data.astype(np.uint8), mode=img.mode)
|
| 89 |
-
out_buffer = io.BytesIO()
|
| 90 |
-
out_img.save(out_buffer, format="PNG")
|
| 91 |
-
|
| 92 |
-
return out_buffer.getvalue(), "PNG"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|