b2bomber commited on
Commit
0c538b6
Β·
verified Β·
1 Parent(s): 02bf1d1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -0
app.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ import torch
5
+ import os
6
+ import tempfile
7
+ import subprocess
8
+
9
+ from ultralytics import YOLO
10
+
11
+ model = YOLO("watermark-detection-73.pt")
12
+ model.conf = 0.25
13
+ model.iou = 0.45
14
+ model.max_det = 1
15
+
16
+
17
+ # ------------------------------
18
+ # Helper Functions
19
+ # ------------------------------
20
+ def extract_first_frame(video_path):
21
+ cap = cv2.VideoCapture(video_path)
22
+ ret, frame = cap.read()
23
+ cap.release()
24
+ if ret:
25
+ return frame
26
+ return None
27
+
28
+ def detect_watermark_coordinates(frame):
29
+ results = model(frame)
30
+ detections = results.xyxy[0].cpu().numpy()
31
+ if len(detections) == 0:
32
+ return None
33
+ x1, y1, x2, y2, _, _ = detections[0]
34
+ return int(x1), int(y1), int(x2 - x1), int(y2 - y1)
35
+
36
+ def generate_mask_from_coords(frame_shape, x, y, w, h):
37
+ mask = np.zeros(frame_shape[:2], dtype=np.uint8)
38
+ mask[int(y):int(y+h), int(x):int(x+w)] = 255
39
+ return mask
40
+
41
+ def apply_inpaint_to_video(video_path, x, y, w, h):
42
+ temp_dir = tempfile.mkdtemp()
43
+ cap = cv2.VideoCapture(video_path)
44
+ fps = cap.get(cv2.CAP_PROP_FPS)
45
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
46
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
47
+ frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
48
+ output_video_path = os.path.join(temp_dir, "output.mp4")
49
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
50
+ writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
51
+ for _ in range(frame_count):
52
+ ret, frame = cap.read()
53
+ if not ret:
54
+ break
55
+ mask = generate_mask_from_coords(frame.shape, x, y, w, h)
56
+ inpainted = cv2.inpaint(frame, mask, 3, cv2.INPAINT_TELEA)
57
+ writer.write(inpainted)
58
+ cap.release()
59
+ writer.release()
60
+
61
+ # Combine processed video with original audio
62
+ temp_with_audio = os.path.splitext(video_path)[0] + "_no_watermark.mp4"
63
+ cmd_audio = f'ffmpeg -y -i "{output_video_path}" -i "{video_path}" -c:v copy -c:a aac -map 0:v:0 -map 1:a:0 -shortest "{temp_with_audio}"'
64
+ subprocess.call(cmd_audio, shell=True)
65
+
66
+ # Re-encode to ensure browser compatibility
67
+ final_output_path = os.path.splitext(video_path)[0] + "_no_watermark_fixed.mp4"
68
+ cmd_fix = f'ffmpeg -y -i "{temp_with_audio}" -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k -movflags +faststart "{final_output_path}"'
69
+ subprocess.call(cmd_fix, shell=True)
70
+ return final_output_path
71
+
72
+ def apply_inpaint_to_image(image, x, y, w, h):
73
+ mask = generate_mask_from_coords(image.shape, x, y, w, h)
74
+ inpainted = cv2.inpaint(image, mask, 3, cv2.INPAINT_TELEA)
75
+ return inpainted
76
+
77
+ def overlay_box_on_image(image, x, y, w, h):
78
+ image_with_box = image.copy()
79
+ cv2.rectangle(image_with_box, (x, y), (x + w, y + h), (0, 255, 0), 2)
80
+ return image_with_box
81
+
82
+ def get_coords_for_image(image):
83
+ coords = detect_watermark_coordinates(image)
84
+ h, w, _ = image.shape
85
+ if coords:
86
+ x, y, w_box, h_box = coords
87
+ return overlay_box_on_image(image, x, y, w_box, h_box), x, y, w_box, h_box, "βœ… Auto watermark detected."
88
+ else:
89
+ w_box, h_box = int(w * 0.25), int(h * 0.1)
90
+ x, y = (w - w_box) // 2, (h - h_box) // 2
91
+ return overlay_box_on_image(image, x, y, w_box, h_box), x, y, w_box, h_box, "⚠️ No watermark detected. Default box placed."
92
+
93
+ def update_image_live(image, x, y, w, h):
94
+ return overlay_box_on_image(image, int(x), int(y), int(w), int(h))
95
+
96
+ def process_uploaded_video(video, x, y, w, h):
97
+ try:
98
+ output_path = apply_inpaint_to_video(video, int(x), int(y), int(w), int(h))
99
+ return output_path, "βœ… Watermark removed from video."
100
+ except Exception as e:
101
+ return None, f"❌ Error: {str(e)}"
102
+
103
+ def process_uploaded_image(image, x, y, w, h):
104
+ try:
105
+ result = apply_inpaint_to_image(image, int(x), int(y), int(w), int(h))
106
+ return result, "βœ… Watermark removed from image."
107
+ except Exception as e:
108
+ return None, f"❌ Error: {str(e)}"
109
+
110
+ # ------------------------------
111
+ # Gradio UI (Merged with Theme)
112
+ # ------------------------------
113
+ with gr.Blocks(theme=gr.themes.Soft(), title="Watermark Remover") as demo:
114
+ gr.Markdown("<p style='text-align: center;'>Remove watermarks from both videos and images using AI detection or manual box selection.</p>")
115
+
116
+ with gr.Tab("πŸ“Ή Video Watermark Remover"):
117
+ with gr.Row():
118
+ with gr.Column(scale=1):
119
+ video_input = gr.Video(label="🎞️ Upload Video")
120
+ auto_btn_v = gr.Button("πŸ” Auto Detect Watermark", variant="primary")
121
+ run_btn_v = gr.Button("🧹 Remove Watermark", variant="secondary")
122
+ status_v = gr.Textbox(label="Status", interactive=False)
123
+ output_file_v = gr.File(label="⬇️ Download Cleaned Video")
124
+ with gr.Column(scale=1):
125
+ video_frame = gr.Image(label="πŸ“ Watermark Preview", interactive=False)
126
+ frame_original = gr.State()
127
+ with gr.Accordion("πŸ”§ Manual Box Adjustment", open=False):
128
+ x_v = gr.Slider(minimum=0, maximum=2000, label="X Coordinate", step=1)
129
+ y_v = gr.Slider(minimum=0, maximum=2000, label="Y Coordinate", step=1)
130
+ w_v = gr.Slider(minimum=10, maximum=2000, label="Width", step=1)
131
+ h_v = gr.Slider(minimum=10, maximum=2000, label="Height", step=1)
132
+
133
+ auto_btn_v.click(
134
+ fn=lambda video: get_coords_for_image(extract_first_frame(video)),
135
+ inputs=video_input,
136
+ outputs=[video_frame, x_v, y_v, w_v, h_v, status_v],
137
+ ).then(
138
+ fn=lambda video: extract_first_frame(video),
139
+ inputs=video_input,
140
+ outputs=frame_original,
141
+ )
142
+
143
+ for slider in [x_v, y_v, w_v, h_v]:
144
+ slider.change(
145
+ fn=update_image_live,
146
+ inputs=[frame_original, x_v, y_v, w_v, h_v],
147
+ outputs=video_frame
148
+ )
149
+
150
+ run_btn_v.click(
151
+ fn=process_uploaded_video,
152
+ inputs=[video_input, x_v, y_v, w_v, h_v],
153
+ outputs=[output_file_v, status_v]
154
+ )
155
+
156
+ with gr.Tab("πŸ–ΌοΈ Image Watermark Remover"):
157
+ with gr.Row():
158
+ with gr.Column(scale=1):
159
+ image_input = gr.Image(label="πŸ–ΌοΈ Upload Image")
160
+ auto_btn_i = gr.Button("πŸ” Auto Detect Watermark", variant="primary")
161
+ run_btn_i = gr.Button("🧹 Remove Watermark", variant="secondary")
162
+ status_i = gr.Textbox(label="Status", interactive=False)
163
+ output_image = gr.Image(label="🧼 Cleaned Image")
164
+ with gr.Column(scale=1):
165
+ image_display = gr.Image(label="πŸ“ Watermark Preview", interactive=False)
166
+ image_original = gr.State()
167
+ with gr.Accordion("πŸ”§ Manual Box Adjustment", open=False):
168
+ x_i = gr.Slider(minimum=0, maximum=2000, label="X Coordinate", step=1)
169
+ y_i = gr.Slider(minimum=0, maximum=2000, label="Y Coordinate", step=1)
170
+ w_i = gr.Slider(minimum=10, maximum=2000, label="Width", step=1)
171
+ h_i = gr.Slider(minimum=10, maximum=2000, label="Height", step=1)
172
+
173
+ auto_btn_i.click(
174
+ fn=get_coords_for_image,
175
+ inputs=image_input,
176
+ outputs=[image_display, x_i, y_i, w_i, h_i, status_i],
177
+ ).then(
178
+ fn=lambda img: img,
179
+ inputs=image_input,
180
+ outputs=image_original,
181
+ )
182
+
183
+ for slider in [x_i, y_i, w_i, h_i]:
184
+ slider.change(
185
+ fn=update_image_live,
186
+ inputs=[image_original, x_i, y_i, w_i, h_i],
187
+ outputs=image_display
188
+ )
189
+
190
+ run_btn_i.click(
191
+ fn=process_uploaded_image,
192
+ inputs=[image_input, x_i, y_i, w_i, h_i],
193
+ outputs=[output_image, status_i]
194
+ )
195
+
196
+ if __name__ == '__main__':
197
+ demo.launch()