Commit
·
730b3b2
1
Parent(s):
56ea5b1
Added watermark
Browse files- app.py +21 -17
- src/utils/watermark.py +51 -75
app.py
CHANGED
@@ -46,30 +46,24 @@ def base64_to_image(base64_str):
|
|
46 |
if not base64_str:
|
47 |
return None
|
48 |
|
49 |
-
# Remove data URI prefix if present (e.g., "data:image/png;base64,")
|
50 |
if isinstance(base64_str, str) and "base64," in base64_str:
|
51 |
base64_str = base64_str.split("base64,", 1)[1]
|
52 |
|
53 |
try:
|
54 |
-
# Strip any whitespace that might be in the base64 string
|
55 |
if isinstance(base64_str, str):
|
56 |
base64_str = base64_str.strip()
|
57 |
|
58 |
-
# Decode the base64 data
|
59 |
image_data = base64.b64decode(base64_str)
|
60 |
|
61 |
-
# Check if we have data
|
62 |
if not image_data:
|
63 |
print("Decoded base64 data is empty")
|
64 |
return None
|
65 |
|
66 |
-
# Attempt to open the image
|
67 |
image = Image.open(io.BytesIO(image_data))
|
68 |
|
69 |
-
# Convert the image to ensure it's valid
|
70 |
return image.copy()
|
71 |
|
72 |
-
except
|
73 |
print(f"Base64 decoding error: {str(e)}")
|
74 |
if isinstance(base64_str, str):
|
75 |
preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str
|
@@ -79,12 +73,10 @@ def base64_to_image(base64_str):
|
|
79 |
except Exception as e:
|
80 |
print(f"Error converting base64 to image: {str(e)}")
|
81 |
|
82 |
-
# Print preview of the base64 string for debugging
|
83 |
if isinstance(base64_str, str):
|
84 |
preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str
|
85 |
print(f"Base64 preview: {preview}")
|
86 |
|
87 |
-
# Additional debug information
|
88 |
if 'image_data' in locals() and image_data:
|
89 |
try:
|
90 |
magic_bytes = image_data[:12].hex()
|
@@ -99,21 +91,28 @@ def url_to_base64(url):
|
|
99 |
return base64.b64encode(response.content).decode()
|
100 |
|
101 |
def gradio_remove_background(image):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
if image is None:
|
103 |
return None
|
104 |
base64_img = image_to_base64(image)
|
105 |
result = remove_background(f"data:image/png;base64,{base64_img}")
|
106 |
|
107 |
-
# Check if the result is directly a base64 string or has an image_data key
|
108 |
if isinstance(result, str):
|
109 |
return base64_to_image(result)
|
110 |
elif isinstance(result, dict) and "image_data" in result:
|
111 |
-
# If image_data contains a data URI prefix
|
112 |
if isinstance(result["image_data"], str) and result["image_data"].startswith("data:"):
|
113 |
-
# The response already contains the full data URI
|
114 |
return base64_to_image(result["image_data"])
|
115 |
else:
|
116 |
-
# Try to process it as a regular base64 string
|
117 |
try:
|
118 |
return base64_to_image(result["image_data"])
|
119 |
except Exception as e:
|
@@ -175,14 +174,19 @@ def toggle_intensity_slider(filter_type):
|
|
175 |
return gr.Slider(interactive=filter_type in intensity_filters)
|
176 |
|
177 |
def gradio_add_watermark(image, watermark_text, opacity=0.5):
|
|
|
|
|
|
|
178 |
if image is None:
|
179 |
return None
|
|
|
|
|
|
|
180 |
try:
|
181 |
-
|
182 |
-
result
|
183 |
-
return base64_to_image(result)
|
184 |
except Exception as e:
|
185 |
-
print(f"Error
|
186 |
return image
|
187 |
|
188 |
def gradio_remove_watermark(image):
|
|
|
46 |
if not base64_str:
|
47 |
return None
|
48 |
|
|
|
49 |
if isinstance(base64_str, str) and "base64," in base64_str:
|
50 |
base64_str = base64_str.split("base64,", 1)[1]
|
51 |
|
52 |
try:
|
|
|
53 |
if isinstance(base64_str, str):
|
54 |
base64_str = base64_str.strip()
|
55 |
|
|
|
56 |
image_data = base64.b64decode(base64_str)
|
57 |
|
|
|
58 |
if not image_data:
|
59 |
print("Decoded base64 data is empty")
|
60 |
return None
|
61 |
|
|
|
62 |
image = Image.open(io.BytesIO(image_data))
|
63 |
|
|
|
64 |
return image.copy()
|
65 |
|
66 |
+
except binascii.Error as e:
|
67 |
print(f"Base64 decoding error: {str(e)}")
|
68 |
if isinstance(base64_str, str):
|
69 |
preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str
|
|
|
73 |
except Exception as e:
|
74 |
print(f"Error converting base64 to image: {str(e)}")
|
75 |
|
|
|
76 |
if isinstance(base64_str, str):
|
77 |
preview = base64_str[:30] + "..." if len(base64_str) > 30 else base64_str
|
78 |
print(f"Base64 preview: {preview}")
|
79 |
|
|
|
80 |
if 'image_data' in locals() and image_data:
|
81 |
try:
|
82 |
magic_bytes = image_data[:12].hex()
|
|
|
91 |
return base64.b64encode(response.content).decode()
|
92 |
|
93 |
def gradio_remove_background(image):
|
94 |
+
"""
|
95 |
+
Attempt to remove watermarks from an image using contrast and brightness adjustment.
|
96 |
+
|
97 |
+
Args:
|
98 |
+
image_input: PIL Image object or base64 string
|
99 |
+
alpha: Contrast control (1.0-3.0, default 2.0). Higher values increase contrast.
|
100 |
+
beta: Brightness control (-255 to 255, default -160). Negative values decrease brightness.
|
101 |
+
|
102 |
+
Returns:
|
103 |
+
PIL Image object with watermark removal attempted
|
104 |
+
"""
|
105 |
if image is None:
|
106 |
return None
|
107 |
base64_img = image_to_base64(image)
|
108 |
result = remove_background(f"data:image/png;base64,{base64_img}")
|
109 |
|
|
|
110 |
if isinstance(result, str):
|
111 |
return base64_to_image(result)
|
112 |
elif isinstance(result, dict) and "image_data" in result:
|
|
|
113 |
if isinstance(result["image_data"], str) and result["image_data"].startswith("data:"):
|
|
|
114 |
return base64_to_image(result["image_data"])
|
115 |
else:
|
|
|
116 |
try:
|
117 |
return base64_to_image(result["image_data"])
|
118 |
except Exception as e:
|
|
|
174 |
return gr.Slider(interactive=filter_type in intensity_filters)
|
175 |
|
176 |
def gradio_add_watermark(image, watermark_text, opacity=0.5):
|
177 |
+
"""
|
178 |
+
Gradio wrapper for add_watermark function
|
179 |
+
"""
|
180 |
if image is None:
|
181 |
return None
|
182 |
+
if not watermark_text or watermark_text.strip() == "":
|
183 |
+
return image
|
184 |
+
|
185 |
try:
|
186 |
+
result = add_watermark(image, watermark_text, opacity)
|
187 |
+
return result
|
|
|
188 |
except Exception as e:
|
189 |
+
print(f"Error in gradio_add_watermark: {e}")
|
190 |
return image
|
191 |
|
192 |
def gradio_remove_watermark(image):
|
src/utils/watermark.py
CHANGED
@@ -3,22 +3,36 @@ import os
|
|
3 |
from typing import Dict, Any
|
4 |
import cv2
|
5 |
import numpy as np
|
|
|
|
|
|
|
|
|
6 |
|
7 |
-
def add_watermark(
|
8 |
"""
|
9 |
Add a semi-transparent text watermark to an image.
|
10 |
|
11 |
Args:
|
12 |
-
|
13 |
-
watermark_text: The text to be used as watermark
|
|
|
14 |
|
15 |
Returns:
|
16 |
-
|
17 |
-
On success: success=True, input_path, output_path, output_size_bytes, watermark_text, message.
|
18 |
-
On failure: success=False, error message, input_path, output_path=None.
|
19 |
"""
|
20 |
try:
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
|
24 |
draw = ImageDraw.Draw(overlay)
|
@@ -27,7 +41,10 @@ def add_watermark(image_path: str, watermark_text: str) -> Dict[str, Any]:
|
|
27 |
font_size = min(image.width, image.height) // 20
|
28 |
font = ImageFont.truetype("arial.ttf", font_size)
|
29 |
except:
|
30 |
-
|
|
|
|
|
|
|
31 |
|
32 |
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
33 |
text_width = bbox[2] - bbox[0]
|
@@ -36,84 +53,43 @@ def add_watermark(image_path: str, watermark_text: str) -> Dict[str, Any]:
|
|
36 |
x = (image.width - text_width) // 2
|
37 |
y = (image.height - text_height) // 2
|
38 |
|
39 |
-
|
|
|
|
|
40 |
|
41 |
-
draw.text((x-2, y-2), watermark_text, fill=
|
42 |
draw.text((x, y), watermark_text, fill=text_color, font=font)
|
43 |
|
44 |
-
watermarked = Image.alpha_composite(image
|
45 |
final_image = watermarked.convert('RGB')
|
46 |
|
47 |
-
|
48 |
-
base_name, ext = os.path.splitext(os.path.basename(image_path))
|
49 |
-
new_filename = f"{base_name}_watermarked{ext}"
|
50 |
-
new_path = os.path.join(base_dir, new_filename)
|
51 |
-
|
52 |
-
final_image.save(new_path, quality=95)
|
53 |
-
output_size = os.path.getsize(new_path)
|
54 |
-
|
55 |
-
return {
|
56 |
-
"success": True,
|
57 |
-
"message": "Watermark added successfully",
|
58 |
-
"input_path": image_path,
|
59 |
-
"output_path": new_path,
|
60 |
-
"output_size_bytes": output_size,
|
61 |
-
"watermark_text": watermark_text
|
62 |
-
}
|
63 |
|
64 |
except Exception as e:
|
65 |
-
|
66 |
-
|
67 |
-
"error": str(e),
|
68 |
-
"input_path": image_path,
|
69 |
-
"output_path": None
|
70 |
-
}
|
71 |
|
72 |
-
def remove_watermark(
|
73 |
-
"""
|
74 |
-
Attempt to remove watermarks from an image using contrast and brightness adjustment.
|
75 |
-
|
76 |
-
Args:
|
77 |
-
image_path: The path to the input image file.
|
78 |
-
alpha: Contrast control (1.0-3.0, default 2.0). Higher values increase contrast.
|
79 |
-
beta: Brightness control (-255 to 255, default -160). Negative values decrease brightness.
|
80 |
-
|
81 |
-
Returns:
|
82 |
-
A dictionary containing success status, file paths, and operation details.
|
83 |
-
On success: success=True, input_path, output_path, output_size_bytes, alpha, beta, message.
|
84 |
-
On failure: success=False, error message, input_path, output_path=None.
|
85 |
-
"""
|
86 |
try:
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
new = np.clip(new, 0, 255).astype(np.uint8)
|
94 |
|
95 |
-
|
96 |
-
base_name, ext = os.path.splitext(os.path.basename(image_path))
|
97 |
-
new_filename = f"{base_name}_cleaned{ext}"
|
98 |
-
new_path = os.path.join(base_dir, new_filename)
|
99 |
-
|
100 |
-
cv2.imwrite(new_path, new)
|
101 |
-
output_size = os.path.getsize(new_path)
|
102 |
|
103 |
-
return
|
104 |
-
"success": True,
|
105 |
-
"message": "Watermark removal attempted successfully",
|
106 |
-
"input_path": image_path,
|
107 |
-
"output_path": new_path,
|
108 |
-
"output_size_bytes": output_size,
|
109 |
-
"alpha": alpha,
|
110 |
-
"beta": beta
|
111 |
-
}
|
112 |
|
113 |
except Exception as e:
|
114 |
-
|
115 |
-
|
116 |
-
"error": str(e),
|
117 |
-
"input_path": image_path,
|
118 |
-
"output_path": None
|
119 |
-
}
|
|
|
3 |
from typing import Dict, Any
|
4 |
import cv2
|
5 |
import numpy as np
|
6 |
+
import binascii
|
7 |
+
from typing import Union
|
8 |
+
import base64
|
9 |
+
import io
|
10 |
|
11 |
+
def add_watermark(image_input: Union[str, Image.Image], watermark_text: str, opacity: float = 0.5) -> Union[str, Image.Image]:
|
12 |
"""
|
13 |
Add a semi-transparent text watermark to an image.
|
14 |
|
15 |
Args:
|
16 |
+
image_input: PIL Image object or base64 string
|
17 |
+
watermark_text: The text to be used as watermark
|
18 |
+
opacity: Opacity of the watermark (0.1 to 1.0)
|
19 |
|
20 |
Returns:
|
21 |
+
PIL Image object with watermark applied
|
|
|
|
|
22 |
"""
|
23 |
try:
|
24 |
+
if isinstance(image_input, str):
|
25 |
+
if image_input.startswith('data:image'):
|
26 |
+
base64_data = image_input.split(',')[1]
|
27 |
+
else:
|
28 |
+
base64_data = image_input
|
29 |
+
image_data = base64.b64decode(base64_data)
|
30 |
+
image = Image.open(io.BytesIO(image_data))
|
31 |
+
else:
|
32 |
+
image = image_input
|
33 |
+
|
34 |
+
if image.mode != 'RGBA':
|
35 |
+
image = image.convert('RGBA')
|
36 |
|
37 |
overlay = Image.new('RGBA', image.size, (255, 255, 255, 0))
|
38 |
draw = ImageDraw.Draw(overlay)
|
|
|
41 |
font_size = min(image.width, image.height) // 20
|
42 |
font = ImageFont.truetype("arial.ttf", font_size)
|
43 |
except:
|
44 |
+
try:
|
45 |
+
font = ImageFont.load_default()
|
46 |
+
except:
|
47 |
+
font = ImageFont.load_default()
|
48 |
|
49 |
bbox = draw.textbbox((0, 0), watermark_text, font=font)
|
50 |
text_width = bbox[2] - bbox[0]
|
|
|
53 |
x = (image.width - text_width) // 2
|
54 |
y = (image.height - text_height) // 2
|
55 |
|
56 |
+
alpha_value = int(255 * opacity)
|
57 |
+
text_color = (255, 255, 255, alpha_value)
|
58 |
+
shadow_color = (0, 0, 0, int(alpha_value * 0.5))
|
59 |
|
60 |
+
draw.text((x-2, y-2), watermark_text, fill=shadow_color, font=font)
|
61 |
draw.text((x, y), watermark_text, fill=text_color, font=font)
|
62 |
|
63 |
+
watermarked = Image.alpha_composite(image, overlay)
|
64 |
final_image = watermarked.convert('RGB')
|
65 |
|
66 |
+
return final_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
except Exception as e:
|
69 |
+
print(f"Error adding watermark: {e}")
|
70 |
+
return image_input if isinstance(image_input, Image.Image) else None
|
|
|
|
|
|
|
|
|
71 |
|
72 |
+
def remove_watermark(image_input: Union[str, Image.Image], alpha: float = 2.0, beta: float = -160) -> Union[str, Image.Image]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
try:
|
74 |
+
if isinstance(image_input, str):
|
75 |
+
if image_input.startswith('data:image'):
|
76 |
+
base64_data = image_input.split(',')[1]
|
77 |
+
else:
|
78 |
+
base64_data = image_input
|
79 |
+
image_data = base64.b64decode(base64_data)
|
80 |
+
image = Image.open(io.BytesIO(image_data))
|
81 |
+
else:
|
82 |
+
image = image_input
|
83 |
+
|
84 |
+
img_array = np.array(image)
|
85 |
+
|
86 |
+
new = alpha * img_array + beta
|
87 |
new = np.clip(new, 0, 255).astype(np.uint8)
|
88 |
|
89 |
+
result_image = Image.fromarray(new)
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
|
91 |
+
return result_image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
|
93 |
except Exception as e:
|
94 |
+
print(f"Error removing watermark: {e}")
|
95 |
+
return image_input if isinstance(image_input, Image.Image) else None
|
|
|
|
|
|
|
|