叠帧 · Imágenes fijas numeradas → Vídeo de portada
Resumen
Escanea una carpeta, conserva solo los archivos cuyo nombre base es puramente un número (01.jpg, 200.jpg), reprodúcelos en orden numérico y codifica un mp4 H.264. Entre imágenes se aplica un corte directo (--transition none) o un encadenamiento suave (--transition fade, el valor predeterminado). Las imágenes no numeradas (logo, discord, …) y los vídeos existentes se ignoran.
Procedimiento
- Inspecciona la carpeta primero. Enumera los archivos y obtén las dimensiones de una imagen con ffprobe. Anota cuántas imágenes fijas numeradas existen y si las relaciones de aspecto son mixtas.
- Realiza una copia de seguridad de cualquier salida existente. Si
<carpeta>/cover.mp4ya existe, renómbralo acover.prev-YYYYMMDD.mp4antes de renderizar. Nunca sobrescribas silenciosamente una renderización anterior del usuario. - Renderiza con el script incluido:
python "<directorio-del-skill>/scripts/imgseq2mp4.py" --dir "<carpeta>" --out cover.mp4
Valores predeterminados: 0.8 s por imagen con un encadenamiento suave fade de 0.2 s, 30 fps, 1920x1080, relleno con letterbox, CRF 18, sin audio, +faststart.
- Verifica, no asumas. Obtén la duración/cuadros, confirma que no haya cuadros negros y extrae el último cuadro para compararlo con la última imagen fuente:
ffprobe -v error -show_entries format=duration -show_entries stream=nb_frames -of default=nw=1 cover.mp4
ffmpeg -i cover.mp4 -vf blackdetect=d=0.05:pix_th=0.10 -an -f null -
ffmpeg -y -sseof -0.05 -i cover.mp4 -frames:v 1 frame_last.png
Cuando las transiciones están activadas, también toma un cuadro dentro de la primera ventana de transición y verifica que sea una mezcla (más cercana a la imagen 2 que un corte directo, pero aún mediblemente diferente de ambas fuentes).
- Informa el recuento de imágenes, las imágenes ignoradas, la duración total, la ruta de salida y el tamaño.
Ritmo
Longitud total = n × segundos − (n−1) × segundos_de_transición. El paso de cada imagen en pantalla es segundos − segundos_de_transición; ese paso, no --seconds, es lo que percibe el espectador.
| Paso por imagen | Comando | 20 imgs | 44 imgs | 200 imgs |
|---|---|---|---|---|
| 0.35 s | --seconds 0.5 --transition-seconds 0.15 | ~8 s | ~18 s | ~75 s |
| 0.45 s | --seconds 0.65 --transition-seconds 0.2 | ~9 s | ~22 s | ~95 s |
| 0.60 s (predeterminado) | --seconds 0.8 --transition-seconds 0.2 | ~12 s | ~27 s | ~120 s |
| 0.80 s | --seconds 1.0 --transition-seconds 0.2 | ~16 s | ~34 s | ~150 s |
Los cortes directos usan --transition none --seconds <paso>.
Muévete una fila a la vez cuando el usuario pida más rápido o más lento. Mantén --transition-seconds entre 0.15 y 0.2; una duración más larga comienza a leerse como una presentación en lugar de un encadenamiento suave.
Reglas
- Solo encadenamiento suave. No uses transiciones de diapositiva, cortinilla ni otras transiciones push; fueron rechazadas como dirección.
- Selecciona con la expresión regular
^\d+\.(jpe?g|png|webp)$— el nombre del archivo debe ser solo dígitos. Esto es lo que excluyelogo.jpg,discord.jpg,cover.jpg. - Ordena por valor entero, no lexicográficamente (
2.jpgantes que10.jpg,99.jpgantes que100.jpg). Los saltos en la numeración no son un problema; los números faltantes no son errores. - Usa
--fit pad(predeterminado) para que todo el cuadro permanezca visible. Solo usa--fit fillcuando el usuario quiera explícitamente recortar de borde a borde. - La salida es silenciosa. No agregues música a menos que el usuario proporcione una pista.
- Mantén los colores intactos. Los JPEG son de rango completo; si los codificas como H.264 de rango completo sin etiquetar, la mayoría de los reproductores decodifican como rango limitado y el resultado se ve más brillante y deslavado (+10 niveles). El script convierte a bt709 de rango limitado y lo etiqueta — no "simplifiques" esa cadena.
- El script se niega a sobrescribir
--outsin--force; resuelve haciendo una copia de seguridad, no forzando a ciegas.
Notas sobre la máquina
- ffmpeg no está en el PATH. El script prefiere un
ffmpeg.exereal (~/.dsh/tools/ffmpeg/**/ffmpeg.exe, luego copias bajoC:/oD:/ffmpeg) antes que cualquier envoltorio.cmd, porque el envoltorio enruta a través de cmd.exe y limita la línea de comandos a ~8k caracteres. Sobrescribe conFFMPEG=/ruta/a/ffmpeg. - Esta compilación carece de
-filter_complex_script; pasa el grafo xfade en línea mediante-filter_complex(el script ya lo hace). - Las transiciones se renderizan en grupos de
--chunk(predeterminado 6) y luego se fusionan, por lo que hay como máximo 6 decodificadores abiertos a la vez. Una sola pasada xfade de 44 entradas agotó la RAM en una máquina de 16 GB (~2.4 GB libres) y falló conCannot allocate memory; no eleves--chunkpor encima de ~8 aquí. El agrupamiento es recursivo, por lo que 200 imágenes se fusionan en tres etapas sin OOM. - Los archivos de etapa intermedia se codifican con CRF 14 / veryfast y se descartan; solo la pasada final usa
--crf/ medium. - Los cuadros intermedios necesitan un directorio con permiso de escritura. Los resultados de
tempfile.mkdtemp()pueden ser inaccesibles bajo entornos restringidos (observado en Windows + DSH: ffmpeg obtuvoPermission deniedyos.listdirlanzóWinError 5). Por lo tanto, el script crea un directorio simple, verifica que sea escribible y recurre a$IMGSEQ_WORKDIR→ temp del sistema →<directorio-de-salida>/.imgseq-work.
Recursos
scripts/imgseq2mp4.py— selección, ordenamiento numérico, renderizado con corte directo o encadenamiento suave, resumen amigable para verificación.