FOSFENO/docs/visuales.md
hacklab f017246f0e Capas independientes, projectM en el mapping y catalogo de la galeria
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>
2026-08-02 14:56:01 +02:00

6.9 KiB
Raw Blame History

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

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

{
  "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>.mp4tipo: silueta
  • recortar.py<peli>_13m26s.mp4tipo: corte (crudo, sin siluetear)

y del resto del nombre saca la película (p1p5, 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:

.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.