# La galería de visuales Los clips que se ven en el Mezclador y en las capas del mapping viven en `data/videos`. Además del archivo suelto, hay un **catálogo** (`data/visuales.json`) con la ficha de cada uno: de qué película sale, quién aparece, cuánto dura y — lo importante — **si es una silueta de verdad y cómo de llena está**. Sin esa ficha el panel solo puede enseñar `p3-davy-jones-tormenta_silueta-negro.mp4`. Con ella enseña *Davy Jones (en la tormenta)*, agrupado bajo *Piratas del Caribe: En el fin del mundo*, y marca los que casi no tienen figura. --- ## Cómo se genera ```bash cd ~/COFRE/CODERS/MUSIKA/VISUALES/FOSFENO python3 scripts/catalogar-visuales.py # cataloga y escribe el JSON python3 scripts/catalogar-visuales.py --limpiar # + aparta los que no son siluetas ``` Necesita `ffmpeg`/`ffprobe` y `numpy`. Tarda unos segundos por clip: decodifica 2 fotogramas por segundo a 160x90 en gris y saca las cuentas de ahí. Hay que volver a pasarlo **cada vez que entran clips nuevos** en la galería. El panel lo recarga solo al pulsar el botón de recargar del Mezclador, o al subir un archivo; si el JSON no está, el panel sigue funcionando con los nombres de archivo pelados. ## Qué mide, y por qué así **¿Es una silueta o es un trozo de película tal cual?** En un clip siluetado por DETEKTION el fondo es negro **puro** (valor 0), cosa que no pasa nunca en una imagen de cine, por oscura que sea. Así que basta con medir qué porcentaje del cuadro está a cero: | | negro puro | |---|---| | Clips siluetados de la galería | 0,63 – 0,90 | | Cortes sin siluetear | 0,04 – 0,06 | No hay zona gris, y por eso el umbral está en 0,30. No es un número inventado: sale de medir la galería entera. **¿Tiene figura suficiente?** Un clip puede estar perfectamente siluetado y aun así no servir, porque la escena era muy oscura y el recorte salió casi vacío. Se mide el porcentaje del cuadro por encima de gris 32, y se mira la **mediana** (no un fotograma suelto: casi todos los clips tienen tramos vacíos y tramos llenos, y juzgar por el fotograma central engaña). | calidad | criterio | qué significa | |---|---|---| | `buena` | mediana ≥ 0,03 | se ve bien sobre el fondo | | `floja` | mediana < 0,03 | figura pequeña o muy oscura | | `vacia` | mediana < 0,005 o más de la mitad de fotogramas vacíos | apenas hay nada | | `descartable` | negro puro < 0,30 | no es una silueta: se ve el fondo | ## Qué hace `--limpiar` Los `descartable` (los que enseñan el fondo entero de la película) se **mueven** a `data/videos/_descartados/`. No se borran: si un día quieres volver a pasarlos por DETEKTION, siguen ahí. El servidor no los sirve, porque solo lista archivos sueltos de `data/videos`, no subcarpetas. Los `floja` y `vacia` **no se tocan**: son siluetas correctas, solo que oscuras. Se quedan en la galería marcadas, y ya decides tú si las usas — sobre un fondo de MilkDrop brillante, una silueta tenue puede funcionar. ## La ficha ```json { "archivo": "p3-davy-jones-tormenta_silueta-negro.mp4", "obra": "Piratas del Caribe: En el fin del mundo", "anio": 2007, "titulo": "Davy Jones (en la tormenta)", "tipo": "silueta", "modo": "silueta sobre negro", "origen": "detektion", "personajes": ["Davy Jones"], "etiquetas": ["en la tormenta"], "ancho": 1152, "alto": 480, "fps": 23.98, "duracion": 30.03, "metricas": { "negroPuro": 0.737, "figura": 0.0582, "figuraPico": 0.1974, "brillo": 8.49, "vacios": 0.03 }, "clase": "silueta", "calidad": "buena", "aviso": "", "miniatura": "miniaturas/p3-davy-jones-tormenta_silueta-negro.jpg" } ``` La miniatura no es un fotograma cualquiera: es **el fotograma con más figura** de todo el clip, que es el que dice de verdad qué vas a ver. ### De dónde salen los nombres El catálogo lee las dos convenciones de DETEKTION: - `siluetear.py` → `_silueta-.mp4` → `tipo: silueta` - `recortar.py` → `_13m26s.mp4` → `tipo: corte` (crudo, **sin** siluetear) y del resto del nombre saca la película (`p1`…`p5`, `hackers`, `tron`…), los personajes conocidos y las palabras de escena. Las tablas están arriba del todo en `scripts/catalogar-visuales.py`: **para añadir películas o personajes nuevos se tocan ahí y ya**. Un clip que no encaje en ninguna tabla no rompe nada: sale con el nombre prettificado y agrupado en "Sin catalogar". ## Transparencia de verdad (canal alfa) Hay **dos formas** de que un personaje se vea sobre las visuales, y conviene no confundirlas: **1. Recorte por luminancia (lo de siempre).** El clip `_silueta-negro.mp4` tiene fondo negro opaco, y es el Mezclador el que lo hace transparente al vuelo: `src(s0).layer(src(s1).luma(umbral, 0.15))`. Funciona y va en H.264, que es lo que mejor decodifica una Raspberry. Pega: hay un umbral que ajustar, y las zonas oscuras del propio personaje (pelo, ropa negra) se comen con él. **2. Canal alfa de verdad (`_silueta-alfa.webm`).** El clip lleva la transparencia dentro, en un WebM con VP9. No hay umbral, no hay bordes comidos: los píxeles del fondo simplemente **no existen**, y debajo se ve la capa que haya. Se genera con: ```bash .venv/bin/python3 siluetear.py clip.mp4 --modo alfa ``` FOSFENO ya lo compone bien sin tocar nada: el fragment shader del mapper saca `gl_FragColor = vec4(c.rgb, c.a * uAlpha)` y mezcla con `SRC_ALPHA / ONE_MINUS_SRC_ALPHA`, así que una capa con alfa deja ver la capa dibujada antes que ella. **Comprobado**, no supuesto: con una capa de fondo roja y encima un clip alfa, el 48,6 % de la pantalla sale roja por los huecos. > Para que se vea a través hace falta que **haya algo debajo**: otra capa del > mapping dibujada antes (MilkDrop, shader, cámara). Si el clip alfa es la > única capa, debajo solo está el negro del compositor. ### Dos trampas del alfa en WebM - **El decodificador VP9 nativo de ffmpeg se come el alfa en silencio**: no falla, devuelve el vídeo entero opaco. Para leerlo hay que pedir `-c:v libvpx-vp9` a mano. Es exactamente por esto que el catalogador mide estos clips con `ffmpeg -c:v libvpx-vp9 ... -vf format=rgba,alphaextract` (y el `format=rgba` no es adorno: sin él `alphaextract` no negocia formato y ffmpeg aborta). - **VP9 no tiene decodificación por hardware en la Raspberry Pi 5**, que sí la tiene para H.264/HEVC. En el portátil va sobrado; en la Pi, con clips de 480p cortos debería ir, pero **hay que probarlo antes de un bolo**. Si se atraganta, el camino 1 (negro + luma key) sigue ahí. ## En el panel `server.py` carga el catálogo en `state.meta.visuales` (`{archivo: ficha}`), filtrado contra lo que hay de verdad en disco para que un catálogo viejo no enseñe clips borrados. El panel lo usa en `fillClipSelect()`: las listas de clips salen agrupadas por película con ``, con el título legible, la duración y la marca de calidad (`· poca figura`, `· casi vacio`), y el aviso completo en el `title` de cada opción.