Darveht commited on
Commit
0946d77
·
verified ·
1 Parent(s): 55bc9be

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +501 -0
app.py ADDED
@@ -0,0 +1,501 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ import torchaudio
4
+ import whisper
5
+ import cv2
6
+ import numpy as np
7
+ from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip
8
+ from transformers import pipeline, AutoTokenizer, AutoModel
9
+ import tempfile
10
+ import os
11
+ import json
12
+ from datetime import timedelta
13
+ import librosa
14
+ from scipy.signal import find_peaks
15
+ import tensorflow as tf
16
+ from sklearn.feature_extraction.text import TfidfVectorizer
17
+ from sklearn.metrics.pairwise import cosine_similarity
18
+ import spacy
19
+ import nltk
20
+ from googletrans import Translator
21
+ import warnings
22
+ warnings.filterwarnings("ignore")
23
+
24
+ class ZenVisionModel:
25
+ """
26
+ ZenVision - Advanced AI Subtitle Generation Model
27
+ Desarrollado por el equipo ZenVision
28
+ Modelo de 3GB+ con múltiples tecnologías de IA
29
+ """
30
+
31
+ def __init__(self):
32
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
33
+ print(f"🚀 Inicializando ZenVision en {self.device}")
34
+
35
+ # Cargar modelos de IA
36
+ self.load_models()
37
+
38
+ def load_models(self):
39
+ """Carga todos los modelos de IA necesarios"""
40
+ print("📦 Cargando modelos de IA...")
41
+
42
+ # 1. Whisper para transcripción de audio (1.5GB)
43
+ self.whisper_model = whisper.load_model("large-v2")
44
+
45
+ # 2. Modelo de traducción multiidioma (500MB)
46
+ self.translator = pipeline("translation",
47
+ model="Helsinki-NLP/opus-mt-en-mul",
48
+ device=0 if self.device == "cuda" else -1)
49
+
50
+ # 3. Modelo de análisis de sentimientos (200MB)
51
+ self.sentiment_analyzer = pipeline("sentiment-analysis",
52
+ model="cardiffnlp/twitter-roberta-base-sentiment-latest",
53
+ device=0 if self.device == "cuda" else -1)
54
+
55
+ # 4. Modelo de detección de emociones (300MB)
56
+ self.emotion_detector = pipeline("text-classification",
57
+ model="j-hartmann/emotion-english-distilroberta-base",
58
+ device=0 if self.device == "cuda" else -1)
59
+
60
+ # 5. Modelo BERT para embeddings (400MB)
61
+ self.bert_tokenizer = AutoTokenizer.from_pretrained("bert-base-multilingual-cased")
62
+ self.bert_model = AutoModel.from_pretrained("bert-base-multilingual-cased")
63
+
64
+ # 6. Traductor de Google
65
+ self.google_translator = Translator()
66
+
67
+ # 7. Procesador de lenguaje natural
68
+ try:
69
+ self.nlp = spacy.load("en_core_web_sm")
70
+ except:
71
+ print("⚠️ Modelo spacy no encontrado, usando funcionalidad básica")
72
+ self.nlp = None
73
+
74
+ print("✅ Todos los modelos cargados exitosamente")
75
+
76
+ def extract_audio_features(self, video_path):
77
+ """Extrae características avanzadas del audio"""
78
+ print("🎵 Extrayendo características de audio...")
79
+
80
+ # Extraer audio del video
81
+ video = VideoFileClip(video_path)
82
+ audio_path = tempfile.mktemp(suffix=".wav")
83
+ video.audio.write_audiofile(audio_path, verbose=False, logger=None)
84
+
85
+ # Cargar audio con librosa para análisis avanzado
86
+ y, sr = librosa.load(audio_path, sr=16000)
87
+
88
+ # Características espectrales
89
+ mfccs = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
90
+ spectral_centroids = librosa.feature.spectral_centroid(y=y, sr=sr)
91
+ chroma = librosa.feature.chroma_stft(y=y, sr=sr)
92
+
93
+ # Detección de pausas y segmentos
94
+ intervals = librosa.effects.split(y, top_db=20)
95
+
96
+ video.close()
97
+ os.remove(audio_path)
98
+
99
+ return {
100
+ 'audio_data': y,
101
+ 'sample_rate': sr,
102
+ 'mfccs': mfccs,
103
+ 'spectral_centroids': spectral_centroids,
104
+ 'chroma': chroma,
105
+ 'intervals': intervals,
106
+ 'duration': len(y) / sr
107
+ }
108
+
109
+ def advanced_transcription(self, audio_features):
110
+ """Transcripción avanzada con Whisper y análisis contextual"""
111
+ print("🎤 Realizando transcripción avanzada...")
112
+
113
+ # Transcripción con Whisper
114
+ result = self.whisper_model.transcribe(
115
+ audio_features['audio_data'],
116
+ language="auto",
117
+ word_timestamps=True,
118
+ verbose=False
119
+ )
120
+
121
+ # Procesar segmentos con timestamps precisos
122
+ segments = []
123
+ for segment in result['segments']:
124
+ # Análisis de sentimientos del texto
125
+ sentiment = self.sentiment_analyzer(segment['text'])[0]
126
+
127
+ # Análisis de emociones
128
+ emotion = self.emotion_detector(segment['text'])[0]
129
+
130
+ # Procesamiento con spaCy si está disponible
131
+ entities = []
132
+ if self.nlp:
133
+ doc = self.nlp(segment['text'])
134
+ entities = [(ent.text, ent.label_) for ent in doc.ents]
135
+
136
+ segments.append({
137
+ 'start': segment['start'],
138
+ 'end': segment['end'],
139
+ 'text': segment['text'],
140
+ 'confidence': segment.get('avg_logprob', 0),
141
+ 'sentiment': sentiment,
142
+ 'emotion': emotion,
143
+ 'entities': entities,
144
+ 'words': segment.get('words', [])
145
+ })
146
+
147
+ return {
148
+ 'language': result['language'],
149
+ 'segments': segments,
150
+ 'full_text': result['text']
151
+ }
152
+
153
+ def intelligent_translation(self, transcription, target_language):
154
+ """Traducción inteligente con múltiples modelos"""
155
+ print(f"🌍 Traduciendo a {target_language}...")
156
+
157
+ translated_segments = []
158
+
159
+ for segment in transcription['segments']:
160
+ original_text = segment['text']
161
+
162
+ # Traducción con Google Translate (más precisa)
163
+ try:
164
+ google_translation = self.google_translator.translate(
165
+ original_text,
166
+ dest=target_language
167
+ ).text
168
+ except:
169
+ google_translation = original_text
170
+
171
+ # Preservar entidades nombradas
172
+ final_translation = google_translation
173
+ if segment['entities']:
174
+ for entity_text, entity_type in segment['entities']:
175
+ if entity_type in ['PERSON', 'ORG', 'GPE']:
176
+ final_translation = final_translation.replace(
177
+ entity_text.lower(), entity_text
178
+ )
179
+
180
+ translated_segments.append({
181
+ **segment,
182
+ 'translated_text': final_translation,
183
+ 'original_text': original_text
184
+ })
185
+
186
+ return translated_segments
187
+
188
+ def generate_smart_subtitles(self, segments, video_duration):
189
+ """Genera subtítulos inteligentes con formato optimizado"""
190
+ print("📝 Generando subtítulos inteligentes...")
191
+
192
+ subtitles = []
193
+
194
+ for i, segment in enumerate(segments):
195
+ # Calcular duración óptima del subtítulo
196
+ duration = segment['end'] - segment['start']
197
+ text = segment.get('translated_text', segment['text'])
198
+
199
+ # Dividir texto largo en múltiples subtítulos
200
+ max_chars = 42 # Máximo caracteres por línea
201
+ max_lines = 2 # Máximo líneas por subtítulo
202
+
203
+ words = text.split()
204
+ lines = []
205
+ current_line = ""
206
+
207
+ for word in words:
208
+ if len(current_line + " " + word) <= max_chars:
209
+ current_line += (" " + word) if current_line else word
210
+ else:
211
+ if current_line:
212
+ lines.append(current_line)
213
+ current_line = word
214
+
215
+ if len(lines) >= max_lines:
216
+ break
217
+
218
+ if current_line:
219
+ lines.append(current_line)
220
+
221
+ # Crear subtítulo con formato
222
+ subtitle_text = "\n".join(lines[:max_lines])
223
+
224
+ # Aplicar estilo basado en emoción
225
+ emotion_label = segment['emotion']['label']
226
+ color = self.get_emotion_color(emotion_label)
227
+
228
+ subtitles.append({
229
+ 'start': segment['start'],
230
+ 'end': segment['end'],
231
+ 'text': subtitle_text,
232
+ 'emotion': emotion_label,
233
+ 'color': color,
234
+ 'confidence': segment['confidence']
235
+ })
236
+
237
+ return subtitles
238
+
239
+ def get_emotion_color(self, emotion):
240
+ """Asigna colores basados en emociones"""
241
+ emotion_colors = {
242
+ 'joy': 'yellow',
243
+ 'sadness': 'blue',
244
+ 'anger': 'red',
245
+ 'fear': 'purple',
246
+ 'surprise': 'orange',
247
+ 'disgust': 'green',
248
+ 'neutral': 'white'
249
+ }
250
+ return emotion_colors.get(emotion.lower(), 'white')
251
+
252
+ def create_subtitle_video(self, video_path, subtitles, output_path):
253
+ """Crea video con subtítulos integrados"""
254
+ print("🎬 Creando video con subtítulos...")
255
+
256
+ video = VideoFileClip(video_path)
257
+ subtitle_clips = []
258
+
259
+ for subtitle in subtitles:
260
+ # Crear clip de texto con estilo
261
+ txt_clip = TextClip(
262
+ subtitle['text'],
263
+ fontsize=24,
264
+ font='Arial-Bold',
265
+ color=subtitle['color'],
266
+ stroke_color='black',
267
+ stroke_width=2
268
+ ).set_position(('center', 'bottom')).set_duration(
269
+ subtitle['end'] - subtitle['start']
270
+ ).set_start(subtitle['start'])
271
+
272
+ subtitle_clips.append(txt_clip)
273
+
274
+ # Componer video final
275
+ final_video = CompositeVideoClip([video] + subtitle_clips)
276
+ final_video.write_videofile(
277
+ output_path,
278
+ codec='libx264',
279
+ audio_codec='aac',
280
+ verbose=False,
281
+ logger=None
282
+ )
283
+
284
+ video.close()
285
+ final_video.close()
286
+
287
+ return output_path
288
+
289
+ def export_subtitle_formats(self, subtitles, base_path):
290
+ """Exporta subtítulos en múltiples formatos"""
291
+ formats = {}
292
+
293
+ # Formato SRT
294
+ srt_path = f"{base_path}.srt"
295
+ with open(srt_path, 'w', encoding='utf-8') as f:
296
+ for i, sub in enumerate(subtitles, 1):
297
+ start_time = self.seconds_to_srt_time(sub['start'])
298
+ end_time = self.seconds_to_srt_time(sub['end'])
299
+ f.write(f"{i}\n{start_time} --> {end_time}\n{sub['text']}\n\n")
300
+ formats['srt'] = srt_path
301
+
302
+ # Formato VTT
303
+ vtt_path = f"{base_path}.vtt"
304
+ with open(vtt_path, 'w', encoding='utf-8') as f:
305
+ f.write("WEBVTT\n\n")
306
+ for sub in subtitles:
307
+ start_time = self.seconds_to_vtt_time(sub['start'])
308
+ end_time = self.seconds_to_vtt_time(sub['end'])
309
+ f.write(f"{start_time} --> {end_time}\n{sub['text']}\n\n")
310
+ formats['vtt'] = vtt_path
311
+
312
+ # Formato JSON con metadatos
313
+ json_path = f"{base_path}.json"
314
+ with open(json_path, 'w', encoding='utf-8') as f:
315
+ json.dump(subtitles, f, indent=2, ensure_ascii=False)
316
+ formats['json'] = json_path
317
+
318
+ return formats
319
+
320
+ def seconds_to_srt_time(self, seconds):
321
+ """Convierte segundos a formato SRT"""
322
+ td = timedelta(seconds=seconds)
323
+ hours, remainder = divmod(td.total_seconds(), 3600)
324
+ minutes, seconds = divmod(remainder, 60)
325
+ milliseconds = int((seconds % 1) * 1000)
326
+ return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d},{milliseconds:03d}"
327
+
328
+ def seconds_to_vtt_time(self, seconds):
329
+ """Convierte segundos a formato VTT"""
330
+ td = timedelta(seconds=seconds)
331
+ hours, remainder = divmod(td.total_seconds(), 3600)
332
+ minutes, seconds = divmod(remainder, 60)
333
+ milliseconds = int((seconds % 1) * 1000)
334
+ return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d}.{milliseconds:03d}"
335
+
336
+ def process_video(self, video_file, target_language="es", include_emotions=True):
337
+ """Procesa video completo para generar subtítulos"""
338
+ if video_file is None:
339
+ return None, None, "Por favor sube un video"
340
+
341
+ try:
342
+ print("🎯 Iniciando procesamiento con ZenVision...")
343
+
344
+ # 1. Extraer características de audio
345
+ audio_features = self.extract_audio_features(video_file.name)
346
+
347
+ # 2. Transcripción avanzada
348
+ transcription = self.advanced_transcription(audio_features)
349
+
350
+ # 3. Traducción inteligente
351
+ if target_language != transcription['language']:
352
+ segments = self.intelligent_translation(transcription, target_language)
353
+ else:
354
+ segments = transcription['segments']
355
+
356
+ # 4. Generar subtítulos inteligentes
357
+ subtitles = self.generate_smart_subtitles(segments, audio_features['duration'])
358
+
359
+ # 5. Crear video con subtítulos
360
+ output_video_path = tempfile.mktemp(suffix=".mp4")
361
+ self.create_subtitle_video(video_file.name, subtitles, output_video_path)
362
+
363
+ # 6. Exportar formatos de subtítulos
364
+ subtitle_base_path = tempfile.mktemp()
365
+ subtitle_formats = self.export_subtitle_formats(subtitles, subtitle_base_path)
366
+
367
+ # Estadísticas del procesamiento
368
+ stats = {
369
+ 'language_detected': transcription['language'],
370
+ 'total_segments': len(subtitles),
371
+ 'duration': audio_features['duration'],
372
+ 'avg_confidence': np.mean([s['confidence'] for s in segments]),
373
+ 'emotions_detected': len(set([s['emotion']['label'] for s in segments]))
374
+ }
375
+
376
+ status_msg = f"""✅ Procesamiento completado con ZenVision!
377
+
378
+ 📊 Estadísticas:
379
+ • Idioma detectado: {stats['language_detected']}
380
+ • Segmentos generados: {stats['total_segments']}
381
+ • Duración: {stats['duration']:.1f}s
382
+ • Confianza promedio: {stats['avg_confidence']:.2f}
383
+ • Emociones detectadas: {stats['emotions_detected']}
384
+
385
+ 🎯 Tecnologías utilizadas:
386
+ • Whisper Large-v2 (Transcripción)
387
+ • BERT Multilingual (Embeddings)
388
+ • RoBERTa (Análisis de sentimientos)
389
+ • DistilRoBERTa (Detección de emociones)
390
+ • Google Translate (Traducción)
391
+ • OpenCV + MoviePy (Procesamiento de video)
392
+ • Librosa (Análisis de audio)
393
+ • spaCy (NLP avanzado)
394
+ """
395
+
396
+ return output_video_path, subtitle_formats['srt'], status_msg
397
+
398
+ except Exception as e:
399
+ return None, None, f"❌ Error en ZenVision: {str(e)}"
400
+
401
+ # Inicializar ZenVision
402
+ print("🚀 Inicializando ZenVision Model...")
403
+ zenvision = ZenVisionModel()
404
+
405
+ # Interfaz Gradio
406
+ with gr.Blocks(title="ZenVision - AI Subtitle Generator", theme=gr.themes.Soft()) as demo:
407
+ gr.HTML("""
408
+ <div style="text-align: center; padding: 20px;">
409
+ <h1>🎬 ZenVision AI Subtitle Generator</h1>
410
+ <p style="font-size: 18px; color: #666;">
411
+ Modelo avanzado de subtitulado automático con IA<br>
412
+ <strong>Desarrollado por el equipo ZenVision</strong>
413
+ </p>
414
+ <p style="font-size: 14px; color: #888;">
415
+ Modelo de 3GB+ • Whisper • BERT • RoBERTa • OpenCV • Librosa • spaCy
416
+ </p>
417
+ </div>
418
+ """)
419
+
420
+ with gr.Row():
421
+ with gr.Column(scale=1):
422
+ gr.Markdown("### 📤 Entrada")
423
+ video_input = gr.Video(label="Subir Video", height=300)
424
+
425
+ with gr.Row():
426
+ language_dropdown = gr.Dropdown(
427
+ choices=[
428
+ ("Español", "es"),
429
+ ("English", "en"),
430
+ ("Français", "fr"),
431
+ ("Deutsch", "de"),
432
+ ("Italiano", "it"),
433
+ ("Português", "pt"),
434
+ ("中文", "zh"),
435
+ ("日本語", "ja"),
436
+ ("한국어", "ko"),
437
+ ("Русский", "ru")
438
+ ],
439
+ value="es",
440
+ label="Idioma de destino"
441
+ )
442
+
443
+ emotions_checkbox = gr.Checkbox(
444
+ label="Incluir análisis de emociones",
445
+ value=True
446
+ )
447
+
448
+ process_btn = gr.Button(
449
+ "🚀 Procesar con ZenVision",
450
+ variant="primary",
451
+ size="lg"
452
+ )
453
+
454
+ with gr.Column(scale=1):
455
+ gr.Markdown("### 📥 Resultados")
456
+ video_output = gr.Video(label="Video con Subtítulos", height=300)
457
+ subtitle_file = gr.File(label="Archivo de Subtítulos (.srt)")
458
+
459
+ with gr.Row():
460
+ status_output = gr.Textbox(
461
+ label="Estado del Procesamiento",
462
+ lines=15,
463
+ interactive=False
464
+ )
465
+
466
+ # Ejemplos
467
+ gr.Markdown("### 🎯 Características de ZenVision")
468
+ gr.HTML("""
469
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin: 20px 0;">
470
+ <div style="padding: 15px; border: 1px solid #ddd; border-radius: 8px;">
471
+ <h4>🎤 Transcripción Avanzada</h4>
472
+ <p>Whisper Large-v2 con timestamps precisos y detección automática de idioma</p>
473
+ </div>
474
+ <div style="padding: 15px; border: 1px solid #ddd; border-radius: 8px;">
475
+ <h4>🌍 Traducción Inteligente</h4>
476
+ <p>Google Translate + preservación de entidades nombradas</p>
477
+ </div>
478
+ <div style="padding: 15px; border: 1px solid #ddd; border-radius: 8px;">
479
+ <h4>😊 Análisis Emocional</h4>
480
+ <p>Detección de emociones y sentimientos con colores adaptativos</p>
481
+ </div>
482
+ <div style="padding: 15px; border: 1px solid #ddd; border-radius: 8px;">
483
+ <h4>📝 Múltiples Formatos</h4>
484
+ <p>Exportación en SRT, VTT y JSON con metadatos completos</p>
485
+ </div>
486
+ </div>
487
+ """)
488
+
489
+ # Conectar funciones
490
+ process_btn.click(
491
+ fn=zenvision.process_video,
492
+ inputs=[video_input, language_dropdown, emotions_checkbox],
493
+ outputs=[video_output, subtitle_file, status_output]
494
+ )
495
+
496
+ if __name__ == "__main__":
497
+ demo.launch(
498
+ server_name="0.0.0.0",
499
+ server_port=7860,
500
+ share=True
501
+ )