Update app.py
Browse files
app.py
CHANGED
@@ -1,232 +1,245 @@
|
|
1 |
-
import gradio as gr
|
2 |
-
import numpy as np
|
3 |
-
import imageio
|
4 |
-
import cv2
|
5 |
-
import numpy as np
|
6 |
-
from inpaint import InpaintingTester
|
7 |
-
import os
|
8 |
-
import shutil
|
9 |
-
import re
|
10 |
-
import uuid
|
11 |
-
|
12 |
-
def create_mask(watermark, mask_type="white"):
|
13 |
-
"""
|
14 |
-
Create a mask for the watermark region.
|
15 |
-
mask_type: 'white' for white mask and 'black' for black mask
|
16 |
-
"""
|
17 |
-
h, w, _ = watermark.shape
|
18 |
-
if mask_type == "white":
|
19 |
-
return np.ones((h, w), dtype=np.uint8) * 255 # White mask
|
20 |
-
elif mask_type == "black":
|
21 |
-
return np.zeros((h, w), dtype=np.uint8) # Black mask
|
22 |
-
return None
|
23 |
-
|
24 |
-
|
25 |
-
def inpaint_watermark(watermark, mask):
|
26 |
-
"""Inpaint the watermark region using the mask."""
|
27 |
-
return cv2.inpaint(watermark, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)
|
28 |
-
|
29 |
-
|
30 |
-
def place_inpainted_back(image, inpainted_region, location):
|
31 |
-
"""Place the inpainted region back into the original image."""
|
32 |
-
x_start, y_start, x_end, y_end = location
|
33 |
-
image[y_start:y_end, x_start:x_end] = inpainted_region
|
34 |
-
return image
|
35 |
-
|
36 |
-
|
37 |
-
def extract_watermark(image, height_ratio=0.15, width_ratio=0.15, margin=0):
|
38 |
-
"""Extract watermark from the image using given ratios and margin."""
|
39 |
-
h, w, _ = image.shape
|
40 |
-
crop_h, crop_w = int(h * height_ratio), int(w * width_ratio)
|
41 |
-
x_start, y_start = w - crop_w, h - crop_h
|
42 |
-
watermark = image[y_start:h-margin, x_start:w-margin]
|
43 |
-
location = (x_start, y_start, w-margin, h-margin)
|
44 |
-
return watermark, location
|
45 |
-
|
46 |
-
|
47 |
-
def load_inpainting_model():
|
48 |
-
"""Load the inpainting model."""
|
49 |
-
save_path = "./output"
|
50 |
-
# resize_to = None # Default size from config
|
51 |
-
resize_to = (480,480)
|
52 |
-
return InpaintingTester(save_path, resize_to)
|
53 |
-
|
54 |
-
|
55 |
-
def process_image_with_model(image_path, mask_path, tester):
|
56 |
-
"""Process the image using the inpainting model and return the cleaned image path."""
|
57 |
-
image_mask_pairs = [(image_path, mask_path)]
|
58 |
-
return tester.process_multiple_images(image_mask_pairs)[0]
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
def img_file_name(image_path):
|
65 |
-
global image_folder
|
66 |
-
text=os.path.basename(image_path)
|
67 |
-
text=text.split(".")[0]
|
68 |
-
# Remove all non-alphabetic characters and convert to lowercase
|
69 |
-
text = re.sub(r'[^a-zA-Z\s]', '', text) # Retain only alphabets and spaces
|
70 |
-
text = text.lower().strip() # Convert to lowercase and strip leading/trailing spaces
|
71 |
-
text = text.replace(" ", "_") # Replace spaces with underscores
|
72 |
-
|
73 |
-
# Truncate or handle empty text
|
74 |
-
truncated_text = text[:25] if len(text) > 25 else text if len(text) > 0 else "empty"
|
75 |
-
|
76 |
-
# Generate a random string for uniqueness
|
77 |
-
random_string = uuid.uuid4().hex[:8].upper()
|
78 |
-
|
79 |
-
# Construct the file name
|
80 |
-
file_name = f"{image_folder}/{truncated_text}_{random_string}.png"
|
81 |
-
return file_name
|
82 |
-
|
83 |
-
def logo_remover(image_path):
|
84 |
-
image = cv2.imread(image_path)
|
85 |
-
image = cv2.resize(image, (1280, 1280)) # Resize image if needed
|
86 |
-
|
87 |
-
# Extract watermark and location
|
88 |
-
first_crop, first_location = extract_watermark(image, 0.50, 0.50, 0)
|
89 |
-
watermark, location = extract_watermark(first_crop, 0.12, 0.26, 27) #height, side, margin
|
90 |
-
|
91 |
-
|
92 |
-
# Create black and white masks
|
93 |
-
mask1 = create_mask(first_crop, "black")
|
94 |
-
mask2 = create_mask(watermark, "white")
|
95 |
-
combined_mask = place_inpainted_back(mask1, mask2, location)
|
96 |
-
|
97 |
-
# Save temporary files
|
98 |
-
input_image = "./input/temp.png"
|
99 |
-
input_mask = "./input/temp_mask.png"
|
100 |
-
# temp_image = cv2.resize(first_crop, (512, 512))
|
101 |
-
temp_image=first_crop
|
102 |
-
cv2.imwrite(input_image, temp_image)
|
103 |
-
# temp_mask = cv2.resize(combined_mask, (512, 512))
|
104 |
-
temp_mask=combined_mask
|
105 |
-
cv2.imwrite(input_mask, temp_mask)
|
106 |
-
|
107 |
-
|
108 |
-
clean_image_path = process_image_with_model(input_image, input_mask, tester)
|
109 |
-
|
110 |
-
# Check if the image was loaded correctly
|
111 |
-
if clean_image_path is None:
|
112 |
-
print(f"Failed to load image: {clean_image_path}")
|
113 |
-
return # Or handle the error accordingly
|
114 |
-
clean_image = cv2.imread(clean_image_path)
|
115 |
-
clean_image = cv2.resize(clean_image, (combined_mask.shape[1], combined_mask.shape[0]))
|
116 |
-
result_image = place_inpainted_back(image, clean_image, first_location)
|
117 |
-
save_path=img_file_name(image_path)
|
118 |
-
cv2.imwrite(save_path, result_image)
|
119 |
-
return save_path
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
# Define a function to handle the image editing and return the final result
|
126 |
-
def process_and_return(im):
|
127 |
-
global tester
|
128 |
-
# Save the composite image (base image) and mask to files
|
129 |
-
base_image_path = "base_image.png"
|
130 |
-
mask_image_path = "mask_image.png"
|
131 |
-
|
132 |
-
# Save the composite image (base image)
|
133 |
-
imageio.imwrite(base_image_path, im["composite"])
|
134 |
-
|
135 |
-
# Extract the alpha channel (mask)
|
136 |
-
alpha_channel = im["layers"][0][:, :, 3]
|
137 |
-
|
138 |
-
# Create the mask: white (255) where drawn, black (0) elsewhere
|
139 |
-
mask = np.zeros_like(alpha_channel, dtype=np.uint8)
|
140 |
-
mask[alpha_channel > 0] = 255 # Set drawn areas to white (255)
|
141 |
-
|
142 |
-
# Save the mask image
|
143 |
-
imageio.imwrite(mask_image_path, mask)
|
144 |
-
# Process the images using the inpainting model
|
145 |
-
final_result = process_image_with_model(base_image_path, mask_image_path,tester)
|
146 |
-
|
147 |
-
# Return the processed image
|
148 |
-
return final_result
|
149 |
-
|
150 |
-
def ui_3():
|
151 |
-
# Create a Gradio app
|
152 |
-
with gr.Blocks() as demo:
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
import
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
[gr.File(label='
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import numpy as np
|
3 |
+
import imageio
|
4 |
+
import cv2
|
5 |
+
import numpy as np
|
6 |
+
from inpaint import InpaintingTester
|
7 |
+
import os
|
8 |
+
import shutil
|
9 |
+
import re
|
10 |
+
import uuid
|
11 |
+
|
12 |
+
def create_mask(watermark, mask_type="white"):
|
13 |
+
"""
|
14 |
+
Create a mask for the watermark region.
|
15 |
+
mask_type: 'white' for white mask and 'black' for black mask
|
16 |
+
"""
|
17 |
+
h, w, _ = watermark.shape
|
18 |
+
if mask_type == "white":
|
19 |
+
return np.ones((h, w), dtype=np.uint8) * 255 # White mask
|
20 |
+
elif mask_type == "black":
|
21 |
+
return np.zeros((h, w), dtype=np.uint8) # Black mask
|
22 |
+
return None
|
23 |
+
|
24 |
+
|
25 |
+
def inpaint_watermark(watermark, mask):
|
26 |
+
"""Inpaint the watermark region using the mask."""
|
27 |
+
return cv2.inpaint(watermark, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)
|
28 |
+
|
29 |
+
|
30 |
+
def place_inpainted_back(image, inpainted_region, location):
|
31 |
+
"""Place the inpainted region back into the original image."""
|
32 |
+
x_start, y_start, x_end, y_end = location
|
33 |
+
image[y_start:y_end, x_start:x_end] = inpainted_region
|
34 |
+
return image
|
35 |
+
|
36 |
+
|
37 |
+
def extract_watermark(image, height_ratio=0.15, width_ratio=0.15, margin=0):
|
38 |
+
"""Extract watermark from the image using given ratios and margin."""
|
39 |
+
h, w, _ = image.shape
|
40 |
+
crop_h, crop_w = int(h * height_ratio), int(w * width_ratio)
|
41 |
+
x_start, y_start = w - crop_w, h - crop_h
|
42 |
+
watermark = image[y_start:h-margin, x_start:w-margin]
|
43 |
+
location = (x_start, y_start, w-margin, h-margin)
|
44 |
+
return watermark, location
|
45 |
+
|
46 |
+
|
47 |
+
def load_inpainting_model():
|
48 |
+
"""Load the inpainting model."""
|
49 |
+
save_path = "./output"
|
50 |
+
# resize_to = None # Default size from config
|
51 |
+
resize_to = (480,480)
|
52 |
+
return InpaintingTester(save_path, resize_to)
|
53 |
+
|
54 |
+
|
55 |
+
def process_image_with_model(image_path, mask_path, tester):
|
56 |
+
"""Process the image using the inpainting model and return the cleaned image path."""
|
57 |
+
image_mask_pairs = [(image_path, mask_path)]
|
58 |
+
return tester.process_multiple_images(image_mask_pairs)[0]
|
59 |
+
|
60 |
+
|
61 |
+
|
62 |
+
|
63 |
+
|
64 |
+
def img_file_name(image_path):
|
65 |
+
global image_folder
|
66 |
+
text=os.path.basename(image_path)
|
67 |
+
text=text.split(".")[0]
|
68 |
+
# Remove all non-alphabetic characters and convert to lowercase
|
69 |
+
text = re.sub(r'[^a-zA-Z\s]', '', text) # Retain only alphabets and spaces
|
70 |
+
text = text.lower().strip() # Convert to lowercase and strip leading/trailing spaces
|
71 |
+
text = text.replace(" ", "_") # Replace spaces with underscores
|
72 |
+
|
73 |
+
# Truncate or handle empty text
|
74 |
+
truncated_text = text[:25] if len(text) > 25 else text if len(text) > 0 else "empty"
|
75 |
+
|
76 |
+
# Generate a random string for uniqueness
|
77 |
+
random_string = uuid.uuid4().hex[:8].upper()
|
78 |
+
|
79 |
+
# Construct the file name
|
80 |
+
file_name = f"{image_folder}/{truncated_text}_{random_string}.png"
|
81 |
+
return file_name
|
82 |
+
|
83 |
+
def logo_remover(image_path):
|
84 |
+
image = cv2.imread(image_path)
|
85 |
+
image = cv2.resize(image, (1280, 1280)) # Resize image if needed
|
86 |
+
|
87 |
+
# Extract watermark and location
|
88 |
+
first_crop, first_location = extract_watermark(image, 0.50, 0.50, 0)
|
89 |
+
watermark, location = extract_watermark(first_crop, 0.12, 0.26, 27) #height, side, margin
|
90 |
+
|
91 |
+
|
92 |
+
# Create black and white masks
|
93 |
+
mask1 = create_mask(first_crop, "black")
|
94 |
+
mask2 = create_mask(watermark, "white")
|
95 |
+
combined_mask = place_inpainted_back(mask1, mask2, location)
|
96 |
+
|
97 |
+
# Save temporary files
|
98 |
+
input_image = "./input/temp.png"
|
99 |
+
input_mask = "./input/temp_mask.png"
|
100 |
+
# temp_image = cv2.resize(first_crop, (512, 512))
|
101 |
+
temp_image=first_crop
|
102 |
+
cv2.imwrite(input_image, temp_image)
|
103 |
+
# temp_mask = cv2.resize(combined_mask, (512, 512))
|
104 |
+
temp_mask=combined_mask
|
105 |
+
cv2.imwrite(input_mask, temp_mask)
|
106 |
+
|
107 |
+
|
108 |
+
clean_image_path = process_image_with_model(input_image, input_mask, tester)
|
109 |
+
|
110 |
+
# Check if the image was loaded correctly
|
111 |
+
if clean_image_path is None:
|
112 |
+
print(f"Failed to load image: {clean_image_path}")
|
113 |
+
return # Or handle the error accordingly
|
114 |
+
clean_image = cv2.imread(clean_image_path)
|
115 |
+
clean_image = cv2.resize(clean_image, (combined_mask.shape[1], combined_mask.shape[0]))
|
116 |
+
result_image = place_inpainted_back(image, clean_image, first_location)
|
117 |
+
save_path=img_file_name(image_path)
|
118 |
+
cv2.imwrite(save_path, result_image)
|
119 |
+
return save_path
|
120 |
+
|
121 |
+
|
122 |
+
|
123 |
+
|
124 |
+
|
125 |
+
# Define a function to handle the image editing and return the final result
|
126 |
+
def process_and_return(im):
|
127 |
+
global tester
|
128 |
+
# Save the composite image (base image) and mask to files
|
129 |
+
base_image_path = "base_image.png"
|
130 |
+
mask_image_path = "mask_image.png"
|
131 |
+
|
132 |
+
# Save the composite image (base image)
|
133 |
+
imageio.imwrite(base_image_path, im["composite"])
|
134 |
+
|
135 |
+
# Extract the alpha channel (mask)
|
136 |
+
alpha_channel = im["layers"][0][:, :, 3]
|
137 |
+
|
138 |
+
# Create the mask: white (255) where drawn, black (0) elsewhere
|
139 |
+
mask = np.zeros_like(alpha_channel, dtype=np.uint8)
|
140 |
+
mask[alpha_channel > 0] = 255 # Set drawn areas to white (255)
|
141 |
+
|
142 |
+
# Save the mask image
|
143 |
+
imageio.imwrite(mask_image_path, mask)
|
144 |
+
# Process the images using the inpainting model
|
145 |
+
final_result = process_image_with_model(base_image_path, mask_image_path,tester)
|
146 |
+
|
147 |
+
# Return the processed image
|
148 |
+
return final_result
|
149 |
+
|
150 |
+
def ui_3():
|
151 |
+
# Create a Gradio app
|
152 |
+
with gr.Blocks() as demo:
|
153 |
+
gr.Markdown("Manually Select the area.")
|
154 |
+
with gr.Row():
|
155 |
+
# Create an ImageEditor component for uploading and editing the image
|
156 |
+
im = gr.ImageEditor(
|
157 |
+
type="numpy",
|
158 |
+
canvas_size=(1, 1), # Use canvas_size instead of crop_size
|
159 |
+
layers=True, # Allow layers in the editor
|
160 |
+
transforms=["crop"], # Allow cropping
|
161 |
+
format="png", # Save images in PNG format
|
162 |
+
label="Base Image",
|
163 |
+
show_label=True
|
164 |
+
)
|
165 |
+
# Create an Image component to display the processed result
|
166 |
+
im2 = gr.Image(label="Processed Image", show_label=True)
|
167 |
+
|
168 |
+
# Create a Button to trigger the image processing
|
169 |
+
btn = gr.Button("Process Image")
|
170 |
+
|
171 |
+
# Define an event listener to trigger the image processing when the button is clicked
|
172 |
+
btn.click(process_and_return, inputs=im, outputs=im2) # Output processed image
|
173 |
+
return demo
|
174 |
+
# def handle_pil_image(image):
|
175 |
+
|
176 |
+
# logo_remover(image)
|
177 |
+
|
178 |
+
|
179 |
+
def ui_1():
|
180 |
+
test_examples=[["./input/image.jpg"]]
|
181 |
+
gradio_input=[gr.Image(label='Upload an Image',type="filepath")]
|
182 |
+
gradio_Output=[gr.Image(label='Display Image')]
|
183 |
+
gradio_interface = gr.Interface(fn=logo_remover, inputs=gradio_input,outputs=gradio_Output ,
|
184 |
+
title="Meta Watermark Remover For Single image",
|
185 |
+
examples=test_examples)
|
186 |
+
return gradio_interface
|
187 |
+
from PIL import Image
|
188 |
+
import zipfile
|
189 |
+
|
190 |
+
def make_zip(image_list):
|
191 |
+
zip_path = f"./temp/images/{uuid.uuid4().hex[:6]}.zip"
|
192 |
+
with zipfile.ZipFile(zip_path, 'w') as zipf:
|
193 |
+
for image in image_list:
|
194 |
+
zipf.write(image, os.path.basename(image))
|
195 |
+
return zip_path
|
196 |
+
|
197 |
+
def handle_multiple_files(image_files):
|
198 |
+
image_list = []
|
199 |
+
if len(image_files) == 1:
|
200 |
+
saved_path=logo_remover(image_files[0])
|
201 |
+
return saved_path
|
202 |
+
else:
|
203 |
+
for image_path in image_files:
|
204 |
+
saved_path=logo_remover(image_path)
|
205 |
+
image_list.append(saved_path)
|
206 |
+
zip_path = make_zip(image_list)
|
207 |
+
return zip_path
|
208 |
+
|
209 |
+
|
210 |
+
|
211 |
+
def ui_2():
|
212 |
+
gradio_multiple_images = gr.Interface(
|
213 |
+
handle_multiple_files,
|
214 |
+
[gr.File(type='filepath', file_count='multiple',label='Upload Images')],
|
215 |
+
[gr.File(label='Download File')],
|
216 |
+
title='Meta Watermark Remover For Bulk Images',
|
217 |
+
cache_examples=True
|
218 |
+
)
|
219 |
+
return gradio_multiple_images
|
220 |
+
|
221 |
+
# Load and process the inpainting model
|
222 |
+
tester = load_inpainting_model()
|
223 |
+
image_folder="./temp/images"
|
224 |
+
if not os.path.exists(image_folder):
|
225 |
+
os.makedirs(image_folder)
|
226 |
+
|
227 |
+
import click
|
228 |
+
@click.command()
|
229 |
+
@click.option("--debug", is_flag=True, default=False, help="Enable debug mode.")
|
230 |
+
@click.option("--share", is_flag=True, default=False, help="Enable sharing of the interface.")
|
231 |
+
|
232 |
+
def main(debug, share):
|
233 |
+
demo1 = ui_1()
|
234 |
+
demo2 = ui_2()
|
235 |
+
demo3=ui_3()
|
236 |
+
demo=gr.TabbedInterface([demo1,demo2,demo3], title="Meta Watermark Remover",tab_names=["Meta Single Image","Meta Bulk Images","Manual Remove"])
|
237 |
+
demo.queue().launch(debug=debug, share=share)#,server_port=9000)
|
238 |
+
#Run on local network
|
239 |
+
# laptop_ip="192.168.0.30"
|
240 |
+
# port=8080
|
241 |
+
# demo.queue().launch(debug=debug, share=share,server_name=laptop_ip,server_port=port)
|
242 |
+
|
243 |
+
if __name__ == "__main__":
|
244 |
+
main()
|
245 |
+
|