Mapping - Los clips salian del reves: mapper.js activaba UNPACK_FLIP_Y_WEBGL, pero el modelo va con origen arriba-izquierda y texImage2D ya sube la primera fila en t=0, asi que el flip la mandaba al final. Comprobado renderizando el mapper real en Chromium headless con una fuente mitad roja / mitad azul. - projectM se puede usar YA en una capa: los .milk de data/presets-projectm se traducen a Butterchurn en el navegador al elegirlos (milkdrop-preset- converter). La capa MilkDrop gana 'biblioteca' (butter | projectm) sin cambiar su firma, para no recrear el contexto WebGL al cambiar de una a otra. Medido: 100/100 presets de una muestra convierten, 84/84 de los que llevan shaders warp/comp; ~7 ms por preset. - Cada capa tiene su pestana arriba y su propia vista, y '+ CAPA' crea una y entra en ella: con varias capas, ir y volver a MAPPING no era viable. - Dos capas MilkDrop sin preset ya no salen identicas (cogian el indice 0): cada instancia elige uno al azar y lo escribe en el estado. - Una capa nueva ya no nace en la fuente "motor", que es el lienzo del motor activo y hacia que dos capas ensenaran lo mismo. Galeria de visuales - scripts/catalogar-visuales.py: ficha de cada clip (pelicula, personajes, duracion) y, sobre todo, si es una silueta de verdad y cuanta figura tiene. Distingue silueta de corte crudo por el negro puro del fondo: 0,63-0,90 frente a 0,04-0,06, sin zona gris. - El panel lista los clips agrupados por pelicula, con nombre legible y aviso de los que casi no tienen figura, en vez del nombre del archivo. - Soporte de siluetas con canal alfa (.webm VP9): transparencia de verdad, sin recorte por luminancia. El shader del mapper ya la respeta. Documentacion - docs/visuales.md nuevo; pendiente.md al dia con lo hecho y lo que queda. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
160 lines
6.9 KiB
Markdown
160 lines
6.9 KiB
Markdown
# 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` → `<algo>_silueta-<modo>.mp4` → `tipo: silueta`
|
||
- `recortar.py` → `<peli>_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 `<optgroup>`, 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.
|