Sesión 19 Feb: OCR intentos, MCP op.gg, timestamps manuales, video final muertes
- Agregado intentos.md con registro de todos los fallos - Actualizado contexto.md con sesión de noche - MCP op.gg instalado (no funcionó - 0 matches) - OCR con Tesseract y EasyOCR (falló - texto muy pequeño) - Video final generado: HIGHLIGHTS_MUERTES_COMPLETO.mp4 - Juegos separados: JUEGO_1/2/3_COMPLETO.mp4 - 10 muertes secuenciales: 0/1→0/10 - Scripts de extracción automática con timestamps
This commit is contained in:
201
detector_ocr_puro.py
Normal file
201
detector_ocr_puro.py
Normal file
@@ -0,0 +1,201 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DETECTOR DE MUERTES - SOLO OCR EN KDA
|
||||
=====================================
|
||||
|
||||
Metodología pura:
|
||||
1. Escanear el video cada 2 segundos
|
||||
2. Extraer SOLO la zona del KDA (esquina superior izquierda)
|
||||
3. Usar Tesseract OCR para leer el número de deaths
|
||||
4. Detectar CUANDO cambia (0→1, 1→2, 2→3, etc.)
|
||||
5. Generar highlights de esos momentos exactos
|
||||
|
||||
Zona KDA: x=0, y=0, w=300, h=130 (1080p)
|
||||
"""
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import pytesseract
|
||||
import subprocess
|
||||
import os
|
||||
from datetime import timedelta
|
||||
import re
|
||||
|
||||
VIDEO_PATH = "stream_2699641307_1080p60.mp4"
|
||||
OUTPUT_DIR = "highlights_muertes"
|
||||
|
||||
|
||||
def format_time(seconds):
|
||||
return str(timedelta(seconds=int(seconds)))
|
||||
|
||||
|
||||
def extract_kda_frame(timestamp):
|
||||
"""Extrae SOLO la zona del KDA"""
|
||||
temp = f"/tmp/kda_{int(timestamp)}.png"
|
||||
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-ss",
|
||||
str(timestamp),
|
||||
"-i",
|
||||
VIDEO_PATH,
|
||||
"-vframes",
|
||||
"1",
|
||||
"-vf",
|
||||
"crop=300:130:0:0,scale=600:260,eq=contrast=1.5:brightness=0.2",
|
||||
temp,
|
||||
]
|
||||
|
||||
subprocess.run(cmd, capture_output=True, timeout=15)
|
||||
return temp if os.path.exists(temp) else None
|
||||
|
||||
|
||||
def read_deaths_ocr(image_path):
|
||||
"""Lee el número de deaths con OCR optimizado"""
|
||||
if not os.path.exists(image_path):
|
||||
return None
|
||||
|
||||
img = cv2.imread(image_path)
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
# Preprocesamiento agresivo para OCR
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
# Aumentar mucho contraste
|
||||
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
|
||||
enhanced = clahe.apply(gray)
|
||||
|
||||
# Threshold
|
||||
_, thresh = cv2.threshold(enhanced, 180, 255, cv2.THRESH_BINARY)
|
||||
|
||||
# OCR - buscar solo números y /
|
||||
config = r"--oem 3 --psm 6 -c tessedit_char_whitelist=0123456789/"
|
||||
text = pytesseract.image_to_string(thresh, config=config)
|
||||
|
||||
# Buscar formato X/Y/Z
|
||||
matches = re.findall(r"(\d+)/(\d+)/(\d+)", text)
|
||||
if matches:
|
||||
return int(matches[0][1]) # Return deaths (middle number)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def scan_for_deaths():
|
||||
"""Escanea el video buscando cambios en el KDA"""
|
||||
print("=" * 60)
|
||||
print("ESCANEANDO VIDEO CON OCR")
|
||||
print("=" * 60)
|
||||
print("Buscando: 0→1, 1→2, 2→3, etc.")
|
||||
print("")
|
||||
|
||||
# Rango del juego 1 (después de 17:29 = 1049s)
|
||||
# Primera muerte confirmada en 41:06 = 2466s
|
||||
start_time = 2460 # Un poco antes
|
||||
end_time = 2800 # Hasta donde sabemos que hay más muertes
|
||||
step = 3 # Cada 3 segundos
|
||||
|
||||
deaths_found = []
|
||||
last_deaths = 0
|
||||
|
||||
print(f"Escaneando desde {format_time(start_time)} hasta {format_time(end_time)}")
|
||||
print("-" * 60)
|
||||
|
||||
for ts in range(start_time, end_time, step):
|
||||
frame = extract_kda_frame(ts)
|
||||
if not frame:
|
||||
continue
|
||||
|
||||
deaths = read_deaths_ocr(frame)
|
||||
|
||||
# Mostrar progreso cada 30s
|
||||
if ts % 30 == 0:
|
||||
print(f" [{format_time(ts)}] Deaths: {deaths if deaths else '?'}")
|
||||
|
||||
if deaths and deaths > last_deaths:
|
||||
print(f" 💀 MUERTE DETECTADA: {format_time(ts)} - KDA: 0/{deaths}")
|
||||
deaths_found.append(
|
||||
{"numero": len(deaths_found) + 1, "timestamp": ts, "deaths": deaths}
|
||||
)
|
||||
last_deaths = deaths
|
||||
|
||||
# Limpiar
|
||||
if os.path.exists(frame):
|
||||
os.remove(frame)
|
||||
|
||||
return deaths_found
|
||||
|
||||
|
||||
def extract_clip(timestamp, numero, deaths_count):
|
||||
"""Extrae clip de una muerte"""
|
||||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||||
|
||||
start = max(0, timestamp - 8)
|
||||
duration = 18 # 8s antes + 10s después
|
||||
|
||||
output = f"{OUTPUT_DIR}/muerte_{numero:02d}_KDA_0_{deaths_count}_{timestamp}s.mp4"
|
||||
|
||||
cmd = [
|
||||
"ffmpeg",
|
||||
"-y",
|
||||
"-ss",
|
||||
str(start),
|
||||
"-t",
|
||||
str(duration),
|
||||
"-i",
|
||||
VIDEO_PATH,
|
||||
"-c:v",
|
||||
"h264_nvenc",
|
||||
"-preset",
|
||||
"fast",
|
||||
"-cq",
|
||||
"23",
|
||||
"-r",
|
||||
"60",
|
||||
"-c:a",
|
||||
"copy",
|
||||
output,
|
||||
]
|
||||
|
||||
subprocess.run(cmd, capture_output=True, timeout=120)
|
||||
return output if os.path.exists(output) else None
|
||||
|
||||
|
||||
def main():
|
||||
print("\nDETECTOR OCR - SOLO MUERTES REALES\n")
|
||||
|
||||
# Escanear
|
||||
deaths = scan_for_deaths()
|
||||
|
||||
if not deaths:
|
||||
print("No se encontraron muertes")
|
||||
return
|
||||
|
||||
print("")
|
||||
print(f"✓ Total muertes encontradas: {len(deaths)}")
|
||||
print("")
|
||||
|
||||
# Extraer clips
|
||||
print("=" * 60)
|
||||
print("EXTRAYENDO CLIPS")
|
||||
print("=" * 60)
|
||||
|
||||
clips = []
|
||||
for d in deaths:
|
||||
print(
|
||||
f"Muerte #{d['numero']} - KDA 0/{d['deaths']} - {format_time(d['timestamp'])}"
|
||||
)
|
||||
|
||||
clip = extract_clip(d["timestamp"], d["numero"], d["deaths"])
|
||||
if clip:
|
||||
size = os.path.getsize(clip) / (1024 * 1024)
|
||||
print(f" ✓ {size:.1f}MB")
|
||||
clips.append(clip)
|
||||
|
||||
print("")
|
||||
print(f"✓ {len(clips)} clips generados en {OUTPUT_DIR}/")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user