Sistema completo de detección de highlights con VLM y análisis de gameplay
- Implementación de detector híbrido (Whisper + Chat + Audio + VLM) - Sistema de detección de gameplay real vs hablando - Scene detection con FFmpeg - Soporte para RTX 3050 y RX 6800 XT - Guía completa en 6800xt.md para próxima IA - Scripts de filtrado visual y análisis de contexto - Pipeline automatizado de generación de videos
This commit is contained in:
171
scene_detector.py
Normal file
171
scene_detector.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/opt/vlm_env/bin/python3
|
||||
"""
|
||||
SCENE DETECTION + CLASSIFICATION (GPU Accelerated)
|
||||
Detecta cambios de escena con FFmpeg (rápido) y clasifica cada una
|
||||
Compatible con RX 6800 XT (16GB VRAM)
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
print("=" * 70)
|
||||
print("🎬 SCENE DETECTOR + CLASSIFIER")
|
||||
print("=" * 70)
|
||||
print("Paso 1: Detectar cambios de escena con FFmpeg (rápido)")
|
||||
print("Paso 2: Clasificar cada escena (gameplay vs hablando)")
|
||||
print()
|
||||
|
||||
video_path = (
|
||||
"/home/ren/proyectos/editor/twitch-highlight-detector/nuevo_stream_360p.mp4"
|
||||
)
|
||||
|
||||
# PASO 1: Detectar cambios de escena (threshold 0.3 = cambios significativos)
|
||||
print("🔍 Detectando cambios de escena...")
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ffmpeg",
|
||||
"-i",
|
||||
video_path,
|
||||
"-vf",
|
||||
"select=gt(scene\,0.3),showinfo",
|
||||
"-f",
|
||||
"null",
|
||||
"-",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
# Extraer timestamps de cambios de escena
|
||||
scene_changes = []
|
||||
for line in result.stderr.split("\n"):
|
||||
if "pts_time:" in line:
|
||||
match = re.search(r"pts_time:(\d+\.\d+)", line)
|
||||
if match:
|
||||
ts = float(match.group(1))
|
||||
if ts > 455: # Saltar intro
|
||||
scene_changes.append(ts)
|
||||
|
||||
print(f"✅ {len(scene_changes)} cambios de escena detectados")
|
||||
|
||||
# PASO 2: Analizar transcripción en cada escena
|
||||
print("\n📊 Analizando contenido de cada escena...")
|
||||
|
||||
with open(
|
||||
"/home/ren/proyectos/editor/twitch-highlight-detector/transcripcion_rage.json", "r"
|
||||
) as f:
|
||||
trans = json.load(f)
|
||||
|
||||
# Crear segmentos entre cambios de escena
|
||||
segments = []
|
||||
prev_ts = 455
|
||||
|
||||
for ts in sorted(scene_changes):
|
||||
if ts - prev_ts > 30: # Mínimo 30 segundos
|
||||
segments.append({"start": prev_ts, "end": ts, "duration": ts - prev_ts})
|
||||
prev_ts = ts
|
||||
|
||||
# Agregar último segmento
|
||||
result = subprocess.run(
|
||||
[
|
||||
"ffprobe",
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"format=duration",
|
||||
"-of",
|
||||
"default=noprint_wrappers=1:nokey=1",
|
||||
video_path,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
duration = float(result.stdout.strip())
|
||||
|
||||
if duration - prev_ts > 30:
|
||||
segments.append({"start": prev_ts, "end": duration, "duration": duration - prev_ts})
|
||||
|
||||
print(f"✅ {len(segments)} segmentos para analizar")
|
||||
|
||||
# PASO 3: Clasificar cada segmento usando transcripción
|
||||
print("\n🎯 Clasificando segmentos (gameplay vs hablando)...")
|
||||
|
||||
for seg in segments:
|
||||
# Buscar transcripción en este rango
|
||||
seg_text = []
|
||||
rage_score = 0
|
||||
|
||||
for t in trans["segments"]:
|
||||
if seg["start"] <= t["start"] <= seg["end"]:
|
||||
seg_text.append(t["text"].lower())
|
||||
|
||||
# Calcular score de rage
|
||||
if any(word in t["text"].lower() for word in ["puta", "mierda", "joder"]):
|
||||
rage_score += 10
|
||||
elif any(
|
||||
word in t["text"].lower() for word in ["me mataron", "kill", "muere"]
|
||||
):
|
||||
rage_score += 8
|
||||
elif any(word in t["text"].lower() for word in ["ulti", "flash", "gank"]):
|
||||
rage_score += 5
|
||||
|
||||
full_text = " ".join(seg_text)
|
||||
|
||||
# Clasificar
|
||||
if any(
|
||||
word in full_text for word in ["seleccion", "champions", "ban", "pick", "elij"]
|
||||
):
|
||||
seg["type"] = "SELECCION"
|
||||
seg["keep"] = False
|
||||
elif any(
|
||||
word in full_text for word in ["cuento", "historia", "ayer", "comida", "vida"]
|
||||
):
|
||||
seg["type"] = "HABLANDO"
|
||||
seg["keep"] = False
|
||||
elif rage_score >= 5 or any(
|
||||
word in full_text for word in ["kill", "matan", "pelea", "fight"]
|
||||
):
|
||||
seg["type"] = "GAMEPLAY"
|
||||
seg["keep"] = True
|
||||
seg["rage_score"] = rage_score
|
||||
else:
|
||||
seg["type"] = "GAMEPLAY_NEUTRO"
|
||||
seg["keep"] = True
|
||||
seg["rage_score"] = rage_score
|
||||
|
||||
# Mostrar resultados
|
||||
print("\n" + "=" * 70)
|
||||
print("SEGMENTOS CLASIFICADOS")
|
||||
print("=" * 70)
|
||||
|
||||
gameplay_segments = [s for s in segments if s["keep"]]
|
||||
|
||||
for i, seg in enumerate(segments, 1):
|
||||
mins_s, secs_s = divmod(int(seg["start"]), 60)
|
||||
mins_e, secs_e = divmod(int(seg["end"]), 60)
|
||||
icon = "✅" if seg["keep"] else "❌"
|
||||
print(
|
||||
f"{icon} {i}. {mins_s:02d}:{secs_s:02d} - {mins_e:02d}:{secs_e:02d} "
|
||||
f"({seg['duration'] // 60:.0f}m) [{seg['type']}]"
|
||||
)
|
||||
if seg.get("rage_score"):
|
||||
print(f" Rage score: {seg['rage_score']}")
|
||||
|
||||
print(f"\n{'=' * 70}")
|
||||
print(f"RESUMEN")
|
||||
print(f"{'=' * 70}")
|
||||
print(f"Total segmentos: {len(segments)}")
|
||||
print(f"Gameplay útil: {len(gameplay_segments)}")
|
||||
total_gameplay = sum(s["duration"] for s in gameplay_segments)
|
||||
print(f"Tiempo gameplay: {total_gameplay // 60:.0f}m {total_gameplay % 60:.0f}s")
|
||||
|
||||
# Guardar gameplay útil
|
||||
with open(
|
||||
"/home/ren/proyectos/editor/twitch-highlight-detector/gameplay_scenes.json", "w"
|
||||
) as f:
|
||||
json.dump(gameplay_segments, f, indent=2)
|
||||
|
||||
print(f"\n💾 Guardado: gameplay_scenes.json")
|
||||
print("\nAhora extrae highlights SOLO de estos rangos confirmados.")
|
||||
Reference in New Issue
Block a user