gnosticdev commited on
Commit
58a2951
·
verified ·
1 Parent(s): e6bec2f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -0
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import cv2
3
+ import numpy as np
4
+ from pathlib import Path
5
+ import tempfile
6
+ import shutil
7
+ import threading
8
+ import time
9
+ from datetime import datetime, timedelta
10
+ import os
11
+
12
+ class WatermarkRemover:
13
+ def __init__(self):
14
+ self.temp_dir = Path(tempfile.gettempdir()) / "watermark_removal"
15
+ self.temp_dir.mkdir(exist_ok=True)
16
+ self.start_cleanup_thread()
17
+
18
+ def start_cleanup_thread(self):
19
+ """Inicia thread para limpiar archivos antiguos cada hora"""
20
+ def cleanup_worker():
21
+ while True:
22
+ self.cleanup_old_files()
23
+ time.sleep(3600) # Revisar cada hora
24
+
25
+ thread = threading.Thread(target=cleanup_worker, daemon=True)
26
+ thread.start()
27
+
28
+ def cleanup_old_files(self):
29
+ """Elimina archivos temporales mayores a 2 horas"""
30
+ try:
31
+ cutoff_time = datetime.now() - timedelta(hours=2)
32
+ for file_path in self.temp_dir.glob("*"):
33
+ if file_path.is_file():
34
+ file_time = datetime.fromtimestamp(file_path.stat().st_mtime)
35
+ if file_time < cutoff_time:
36
+ file_path.unlink()
37
+ print(f"Archivo eliminado: {file_path.name}")
38
+ except Exception as e:
39
+ print(f"Error en limpieza: {e}")
40
+
41
+ def detect_watermark_region(self, frame):
42
+ """
43
+ Detecta regiones candidatas de marca de agua usando análisis de opacidad
44
+ y detección de bordes
45
+ """
46
+ # Convertir a escala de grises
47
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
48
+
49
+ # Detectar bordes para encontrar contornos de marca de agua
50
+ edges = cv2.Canny(gray, 50, 150)
51
+
52
+ # Encontrar regiones con alta densidad de bordes (posibles watermarks)
53
+ kernel = np.ones((15, 15), np.uint8)
54
+ dilated = cv2.dilate(edges, kernel, iterations=2)
55
+
56
+ # Encontrar contornos
57
+ contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
58
+
59
+ # Filtrar contornos por tamaño (marcas de agua suelen ser pequeñas/medianas)
60
+ watermark_regions = []
61
+ h, w = frame.shape[:2]
62
+
63
+ for contour in contours:
64
+ area = cv2.contourArea(contour)
65
+ # Marca de agua típicamente entre 0.1% y 10% del área total
66
+ if 0.001 * (h * w) < area < 0.1 * (h * w):
67
+ x, y, w_box, h_box = cv2.boundingRect(contour)
68
+ watermark_regions.append((x, y, w_box, h_box))
69
+
70
+ return watermark_regions
71
+
72
+ def inpaint_watermark(self, frame, regions):
73
+ """
74
+ Elimina marcas de agua usando inpainting
75
+ """
76
+ # Crear máscara para las regiones detectadas
77
+ mask = np.zeros(frame.shape[:2], dtype=np.uint8)
78
+
79
+ for (x, y, w, h) in regions:
80
+ # Expandir ligeramente la región para asegurar eliminación completa
81
+ padding = 5
82
+ x1 = max(0, x - padding)
83
+ y1 = max(0, y - padding)
84
+ x2 = min(frame.shape[1], x + w + padding)
85
+ y2 = min(frame.shape[0], y + h + padding)
86
+
87
+ mask[y1:y2, x1:x2] = 255
88
+
89
+ # Aplicar inpainting si hay regiones detectadas
90
+ if np.any(mask):
91
+ result = cv2.inpaint(frame, mask, 3, cv2.INPAINT_TELEA)
92
+ return result, True
93
+
94
+ return frame, False
95
+
96
+ def remove_static_patterns(self, frame, reference_frames):
97
+ """
98
+ Elimina patrones estáticos comparando con frames de referencia
99
+ """
100
+ if len(reference_frames) < 3:
101
+ return frame
102
+
103
+ # Calcular mediana de frames de referencia
104
+ median_frame = np.median(reference_frames, axis=0).astype(np.uint8)
105
+
106
+ # Diferencia entre frame actual y mediana
107
+ diff = cv2.absdiff(frame, median_frame)
108
+ gray_diff = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
109
+
110
+ # Umbralizar para encontrar píxeles estáticos diferentes
111
+ _, mask = cv2.threshold(gray_diff, 30, 255, cv2.THRESH_BINARY_INV)
112
+
113
+ # Dilatar máscara para incluir bordes
114
+ kernel = np.ones((5, 5), np.uint8)
115
+ mask = cv2.dilate(mask, kernel, iterations=1)
116
+
117
+ # Aplicar inpainting en áreas estáticas diferentes
118
+ result = cv2.inpaint(frame, mask, 3, cv2.INPAINT_TELEA)
119
+
120
+ return result
121
+
122
+ def process_video(self, video_path, progress=gr.Progress()):
123
+ """
124
+ Procesa el video completo para eliminar marcas de agua móviles
125
+ """
126
+ try:
127
+ progress(0, desc="Iniciando procesamiento...")
128
+
129
+ # Abrir video
130
+ cap = cv2.VideoCapture(video_path)
131
+
132
+ if not cap.isOpened():
133
+ return None, "Error: No se pudo abrir el video"
134
+
135
+ # Obtener propiedades del video
136
+ fps = int(cap.get(cv2.CAP_PROP_FPS))
137
+ width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
138
+ height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
139
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
140
+
141
+ # Crear archivo de salida
142
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
143
+ output_path = self.temp_dir / f"cleaned_{timestamp}.mp4"
144
+
145
+ # Configurar writer
146
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
147
+ out = cv2.VideoWriter(str(output_path), fourcc, fps, (width, height))
148
+
149
+ reference_frames = []
150
+ frame_count = 0
151
+
152
+ progress(0.1, desc="Procesando frames...")
153
+
154
+ while True:
155
+ ret, frame = cap.read()
156
+ if not ret:
157
+ break
158
+
159
+ # Mantener buffer de frames de referencia (últimos 10 frames)
160
+ if len(reference_frames) >= 10:
161
+ reference_frames.pop(0)
162
+ reference_frames.append(frame.copy())
163
+
164
+ # Detectar y eliminar marcas de agua
165
+ processed_frame = frame.copy()
166
+
167
+ # Método 1: Detección por contornos
168
+ regions = self.detect_watermark_region(processed_frame)
169
+ if regions:
170
+ processed_frame, _ = self.inpaint_watermark(processed_frame, regions)
171
+
172
+ # Método 2: Eliminación de patrones estáticos
173
+ if len(reference_frames) >= 5:
174
+ processed_frame = self.remove_static_patterns(processed_frame, reference_frames)
175
+
176
+ # Escribir frame procesado
177
+ out.write(processed_frame)
178
+
179
+ frame_count += 1
180
+ if frame_count % 10 == 0:
181
+ progress_value = 0.1 + (0.8 * frame_count / total_frames)
182
+ progress(progress_value, desc=f"Procesando: {frame_count}/{total_frames} frames")
183
+
184
+ # Liberar recursos
185
+ cap.release()
186
+ out.release()
187
+
188
+ progress(1.0, desc="¡Completado!")
189
+
190
+ return str(output_path), f"Video procesado exitosamente: {frame_count} frames"
191
+
192
+ except Exception as e:
193
+ return None, f"Error durante el procesamiento: {str(e)}"
194
+
195
+ # Crear instancia del removedor
196
+ remover = WatermarkRemover()
197
+
198
+ def process_video_interface(video_file):
199
+ """Interfaz para Gradio"""
200
+ if video_file is None:
201
+ return None, "Por favor, carga un video"
202
+
203
+ output_path, message = remover.process_video(video_file)
204
+
205
+ return output_path, message
206
+
207
+ # Crear interfaz de Gradio
208
+ with gr.Blocks(title="Eliminador de Marcas de Agua") as demo:
209
+ gr.Markdown("""
210
+ # 🎬 Eliminador de Marcas de Agua de Videos
211
+
212
+ Sube tu video y esta herramienta eliminará automáticamente las marcas de agua móviles.
213
+
214
+ **Características:**
215
+ - Detecta y elimina marcas de agua móviles
216
+ - Procesa videos de cualquier tamaño
217
+ - Limpieza automática de archivos temporales (cada 2 horas)
218
+ - Soporta múltiples formatos de video
219
+ """)
220
+
221
+ with gr.Row():
222
+ with gr.Column():
223
+ video_input = gr.Video(label="Video Original")
224
+ process_btn = gr.Button("🚀 Eliminar Marcas de Agua", variant="primary")
225
+
226
+ with gr.Column():
227
+ video_output = gr.Video(label="Video Limpio")
228
+ status_output = gr.Textbox(label="Estado", lines=3)
229
+
230
+ process_btn.click(
231
+ fn=process_video_interface,
232
+ inputs=[video_input],
233
+ outputs=[video_output, status_output]
234
+ )
235
+
236
+ gr.Markdown("""
237
+ ---
238
+ ### 📝 Notas:
239
+ - Los archivos temporales se eliminan automáticamente después de 2 horas
240
+ - El procesamiento puede tardar según el tamaño del video
241
+ - Para mejores resultados, usa videos con buena calidad
242
+ """)
243
+
244
+ if __name__ == "__main__":
245
+ demo.launch()