Generación de clips de video (de imagen fija a clip, luego lip-sync)
Resumen
Una imagen fija terminada se convierte en un clip de 4-9 s con un modelo de imagen a video. Luego, si un personaje habla en pantalla, un modelo de lip-sync reajusta el movimiento de su boca para que coincida con tu propio diálogo. No puedes ver el resultado: juzga cada clip a partir de fotogramas extraídos (scripts/take_sheet.py y la skill de visualización de videos), y deja que el humano tenga la última palabra sobre el movimiento.
Requisitos: Python 3 (solo biblioteca estándar) y ffmpeg/ffprobe. Las claves van en variables de entorno: GEMINI_API_KEY (una clave de Google AI Studio; una clave con alcance de imagen funciona, y GEMINI_IMAGE_API_KEY se lee como respaldo) para Omni, y FAL_KEY (la clave completa exactamente como la muestra fal) para lip-sync. Nunca las imprimas.
Elegir el modelo
| Modelo | Se accede a través de | Úsalo para | Notas |
|---|---|---|---|
Gemini Omni Flash 1.1 (gemini-omni-1.1-flash) | API de Google Interactions, clave AI Studio | Predeterminado para ilustraciones, arte anime y caricaturas; también funcionó en una imagen fija fotorrealista (una prueba) | mantiene el estilo artístico y los rostros; cualquier relación de aspecto de entrada (una imagen fija 2:3 regresó como 9:16 limpio, por lo que no es necesario ampliar ni recortar primero); 720p 9:16 o 16:9; síncrono, ~25-35 s por clip; añade su propio audio (enmudécelo) |
| Veo 3.1 / Veo 3.1 Fast | API de Gemini, imagen como fotograma inicial | imágenes fijas fotorrealistas | no se usó en nuestras ejecuciones; consulta la documentación actual primero |
| Seedance 2.5 | ElevenLabs Flows, o ByteDance directamente | aspecto alternativo | necesitaba aprobación de cuenta cuando se probó |
| Cualquier modelo dentro de ElevenLabs Flows | API de ElevenLabs | una clave para todo | el acceso a la API necesita el plan Pro (402 paid_plan_required) y el permiso image_video_generation de la clave (401 sin él); Omni allí es solo para aplicaciones |
Para lip-sync, Sync 3 en fal (fal-ai/sync-lipsync/v3) es el predeterminado. Funciona video a video, por lo que conserva el movimiento del clip y reajusta solo la boca. Comparación y precios: references/providers.md.
Animar una imagen fija
python3 <directorio-de-esta-skill>/scripts/omni_generate.py trabajos.json clips/ [--takes 3] [--only S05]
{"base": "Dale vida a esta imagen fija tal como es: mismo estilo artístico, paleta, iluminación y composición, y cada personaje conserva la cara, el cabello, la complexión y la ropa que tiene en ella. Movimiento natural y fluido. Nada escrito en pantalla, sin bocadillos de diálogo, nadie nuevo, un único plano continuo. ",
"trabajos": {"S05": {"imagen": "paneles/p05.png",
"indicación": "[0-3s] La mujer de la chaqueta roja levanta el teléfono y habla, emocionada. [3-6s] La amiga de la sudadera verde se reclina en el sofá y se ríe. Cámara lenta en zoom interior."}}}
Cómo escribir la indicación (guía completa con ejemplos en references/gemini-omni.md):
- Una línea de tiempo por segundo, un compás por corchete. El clip dura aproximadamente lo mismo que la línea de tiempo.
- Nombra a las personas por lo que visten, nunca por su nombre: el modelo no sabe quién es "Dana".
- Di "habla" para cualquier persona que vaya a tener lip-sync, para que la boca ya se mueva.
- Fija lo que no debe cambiar: "la cámara se queda quieta", "una sola ventana en la pared izquierda", "exactamente UNA figura encapuchada", "sus manos permanecen planas sobre la mesa". Cada fijación en
references/failure-modes.mdprovino de un fallo real.
Revisar cada toma antes de usarla
python3 <directorio-de-esta-skill>/scripts/take_sheet.py hoja revision/S09.jpg clips/S09_t1.mp4 clips/S09_t2.mp4 clips/S09_t3.mp4 --times 0.3,1,2,3,4,5,6
Lee la hoja: las filas son tomas, las columnas son tiempos. Busca objetos que se dupliquen, puertas o personas que aparezcan, manos que crucen cuerpos, rostros que se desvíen y un gesto realizado dos veces. Nombra el segundo exacto de cada problema, porque la solución depende de dónde esté.
Un corte brusco es fácil de pasar por alto entre fotogramas muestreados. Enuméllalos directamente; sin salida significa un plano continuo:
ffmpeg -i clips/S09_t1.mp4 -vf "select='gt(scene,0.3)',showinfo" -an -f null - 2>&1 | grep pts_time
Ajustar un clip a su toma
Una edición necesita un clip que llene su toma exactamente, a la velocidad de fotogramas de la edición:
| Problema | Solución |
|---|---|
| Mal inicio (un gesto hecho antes de su línea, un objeto duplicado en el primer segundo) | Empezar más tarde: --off 1.5. |
| Demasiado corto después del recorte | Ralentizarlo con --speed 1.22 (hasta ~1.25 se ve bien); el último fotograma se mantiene. |
| Mal medio | Dividir la toma: reproducir la primera línea en un recorte que mantenga el problema fuera del encuadre, luego cortar de nuevo después. |
| Irreparable (aparece una puerta, entra una segunda persona) | Regenerar con una indicación fijada, 2-3 tomas, y seleccionar en una hoja. |
lipsync_parts.py normalize clip.mp4 toma.mp4 --frames N --off S --speed K realiza el desplazamiento, la velocidad, los fps, la escala (cubrir y centrar-recortar, nunca estirar) y la retención del último fotograma en un solo paso, y siempre devuelve exactamente N fotogramas.
Lip-sync de diálogo
- Normalizar el clip a la toma:
python3 <directorio-de-esta-skill>/scripts/lipsync_parts.py normalize clip.mp4 toma.mp4 --frames 132 --off 1.5 --speed 1.22. - Dividir en los límites de las líneas para que cada parte tenga un solo hablante.
- Escribir
toma.json: cada línea con su inicio/fin en la toma (segundos) y su hablante;frameses igual al--framesal que normalizaste. Añadir"skip": truepara un hablante visto de espaldas, fuera de pantalla, o un grito de multitud:{"id": "S05", "video": "toma.mp4", "fps": 24, "frames": 132, "lines": [{"id": "05-1", "audio": "lines/05-1.mp3", "inicio": 0.72, "fin": 2.31, "hablante": "maya"}, {"id": "05-2", "audio": "lines/05-2.mp3", "inicio": 2.43, "fin": 4.10, "hablante": "ido"}]} - Ejecutar
lipsync_parts.py split toma.json partes/. Además de las partes, escribepartes/S05_lipsync_trabajos.json: una entrada por parte no saltada, con rutas absolutas,outya nombrado como espera join (synced/S05_<k>.mp4), yface: null.
- Escribir
- Señalar al hablante en cada parte, y escribirlo en ese archivo de trabajos como
"face": "x,y@<mid>"(midestá en cada entrada).- Elegir un píxel en su rostro en el fotograma medio de la parte.
- Confirmarlo:
take_sheet.py point partes/S05_1.mp4 check.jpg --frame 30 --xy 410,560, luego leer la imagen. - Los detectores de rostros no son fiables en rostros dibujados, así que elige y confirma a ojo. Deja
nullsolo cuando sea visible un solo rostro.
- Sincronizar:
lipsync_fal.py --trabajos partes/S05_lipsync_trabajos.json. Espera ~$0.13 por segundo y ~70-150 s por parte; 6 se ejecutan en paralelo. Las salidas existentes se saltan. Para rehacer una parte después de corregir su rostro, ejecuta esa parte sola:lipsync_fal.py partes/S05_1.mp4 partes/S05_1.wav synced/S05_1.mp4 --face 410,560@30 --force(--forcecon--trabajosreharía cada parte). - Unir:
lipsync_parts.py join partes/S05_plan.json synced/ final/S05.mp4. Cada parte se fuerza a su recuento exacto de fotogramas, las partes saltadas conservan sus fotogramas originales, una parte sin archivo sincronizado se informa (--strictlo convierte en un error), y se verifica el recuento total de fotogramas.
Detalles, elección del modelo y casos de fallo: references/lipsync.md.
Costos y tiempos (medidos en 2026-10)
| Paso | Cantidad | Costo | Tiempo |
|---|---|---|---|
| Clip Omni | por segundo de video de salida (un clip de 6 s es ~$0.60; planifica ~1.5 llamadas por toma para repeticiones) | ~$0.10 | ~30 s por llamada, 4 en paralelo |
| Lip-sync Sync 3 | por segundo de salida (una parte suele ser de 3-6 s) | ~$0.13 | ~70-150 s por parte, 6 en paralelo |
Errores comunes
| Error | Resultado | Solución |
|---|---|---|
| Nombrar personajes en la indicación de video | la persona equivocada se mueve | describirlos por su ropa |
| Conservar el audio de Omni | voces inventadas y música debajo de tu diálogo | enmudecerlo (-an) |
| Usar una toma sin una hoja | objetos duplicados y nuevas puertas enviadas | hacer hoja de cada toma |
| Regenerar todo el clip por un primer segundo malo | nuevos problemas en otro lugar | desplazamiento más ralentización |
| Hacer lip-sync a toda una toma de dos hablantes | una boca se mueve para ambas líneas | dividir en los límites de las líneas, un hablante por parte |
| Dejar que el modelo elija al hablante | la cara equivocada habla | --face X,Y@FOTRAMA |
| Acortar la clave de fal | 401 | pasar la clave exactamente como la muestra fal |
| Unir partes sin arreglar los recuentos de fotogramas | la toma se desincroniza con la edición | lipsync_parts.py join |
Skills relacionadas
- generación-de-imágenes: las imágenes fijas (tarjetas de personaje, paneles).
- elevenlabs-tts: el diálogo al que se sincronizan las bocas.
- visualización-de-videos: extracción de fotogramas para revisar clips.
- producción-de-episodios-animados: el flujo de trabajo completo del episodio que utiliza estos clips.
Referencias
references/gemini-omni.md-- guía detallada de indicaciones para el generador de video Omni.references/failure-modes.md-- catálogo de artefactos conocidos y sus causas raíz.references/lipsync.md-- guía de lip-sync, elección de modelos y modos de fallo.references/providers.md-- comparación de modelos de video y proveedores con precios.