Videoclips erstellen (Standbild zu Clip, dann Lippen-Synchronisation)
Übersicht
Ein fertiges Standbild wird mit einem Bild-zu-Video-Modell zu einem 4–9 s langen Clip. Wenn dann ein Charakter im Bild spricht, passt ein Lip-Sync-Modell die Mundbewegungen an den eigenen Dialog an. Das Ergebnis kann nicht direkt angesehen werden: Jeder Clip wird anhand extrahierter Frames beurteilt (scripts/take_sheet.py und die Fähigkeit zum Ansehen von Videos), und der Mensch hat das letzte Wort über die Bewegung.
Voraussetzungen: Python 3 (nur Standardbibliothek) und ffmpeg/ffprobe. API-Schlüssel kommen in Umgebungsvariablen: GEMINI_API_KEY (ein Google AI Studio-Schlüssel; ein bildbezogener funktioniert, und GEMINI_IMAGE_API_KEY wird als Fallback gelesen) für Omni, und FAL_KEY (der vollständige Schlüssel genau so, wie fal ihn anzeigt) für Lip-Sync. Niemals ausgeben.
Modell auswählen
| Modell | Erreichbar über | Verwenden für | Hinweise |
|---|---|---|---|
Gemini Omni Flash 1.1 (gemini-omni-1.1-flash) | Google Interactions API, AI Studio-Schlüssel | Standard für illustrierte, Anime- und Cartoon-Standbilder; funktionierte auch bei einem fotorealistischen Standbild (ein Test) | Behält Kunststil und Gesichter bei; jedes Eingabeformat (ein 2:3-Standbild kam sauber als 9:16 zurück, daher kein Outpainting oder Zuschneiden nötig); 720p 9:16 oder 16:9; synchron, ~25–35 s pro Clip; fügt eigenes Audio hinzu (stumm schalten) |
| Veo 3.1 / Veo 3.1 Fast | Gemini API, Bild als Startframe | fotorealistische Standbilder | In unseren Durchläufen nicht verwendet; aktuell Docs prüfen |
| Seedance 2.5 | ElevenLabs Flows, oder direkt von ByteDance | alternative Optik | Benötigte Konto-Freischaltung, als getestet |
| Jedes Modell in ElevenLabs Flows | ElevenLabs API | ein Schlüssel für alles | API-Zugriff benötigt den Pro-Plan (402 paid_plan_required) und die Berechtigung image_video_generation für den Schlüssel (401 ohne); Omni dort ist nur App-basiert |
Für Lip-Sync ist Sync 3 auf fal (fal-ai/sync-lipsync/v3) die Voreinstellung. Es arbeitet Video-zu-Video, behält also die Bewegung des Clips und passt nur den Mund an. Vergleich und Preise: references/providers.md.
Ein Standbild animieren
python3 <this-skill-dir>/scripts/omni_generate.py jobs.json clips/ [--takes 3] [--only S05]
{"base": "Erwecke dieses Standbild genau so zum Leben: gleicher Kunststil, gleiche Palette, gleiches Licht und Layout, und jeder Charakter behält das Gesicht, die Haare, den Körperbau und die Kleidung, die er darin hat. Natürliche, fließende Bewegung. Nichts auf dem Bildschirm geschrieben, keine Sprechblasen, niemand Neues, eine durchgehende Einstellung. ",
"jobs": {"S05": {"image": "panels/p05.png",
"prompt": "[0-3s] Die Frau in der roten Jacke hebt das Telefon und spricht, aufgeregt. [3-6s] Die Freundin im grünen Hoodie lehnt sich auf dem Sofa zurück und lacht. Langsames Kamera-Heranzoomen."}}}
Wie man den Prompt schreibt (vollständige Anleitung mit Beispielen in references/gemini-omni.md):
- Eine Zeitachse pro Sekunde, ein Beat pro Klammer. Der Clip läuft ungefähr so lange wie die Zeitachse.
- Personen nach ihrer Kleidung benennen, niemals mit Namen: Das Modell weiß nicht, wer „Dana“ ist.
- „Spricht“ sagen für jeden, der später lippensynchronisiert wird, damit sich der Mund bereits bewegt.
- Festlegen, was sich nicht ändern darf: „die Kamera bleibt still“, „ein einziges Fenster an der linken Wand“, „genau EINE vermummte Figur“, „ihre Hände bleiben flach auf dem Tisch“. Jede Festlegung in
references/failure-modes.mdstammt von einem echten Fehler.
Jeden Take vor der Verwendung überprüfen
python3 <this-skill-dir>/scripts/take_sheet.py sheet review/S09.jpg clips/S09_t1.mp4 clips/S09_t2.mp4 clips/S09_t3.mp4 --times 0.3,1,2,3,4,5,6
Das Blatt lesen: Zeilen sind Takes, Spalten sind Zeitpunkte. Achten auf Requisiten, die sich verdoppeln, Türen oder Personen, die erscheinen, Hände, die durch Körper gehen, Gesichter, die sich verändern, und eine Geste, die zweimal ausgeführt wird. Die genaue Sekunde jedes Problems benennen, da die Lösung davon abhängt, wo es auftritt.
Ein harter Schnitt ist zwischen abgetasteten Frames leicht zu übersehen. Direkt auflisten; keine Ausgabe bedeutet eine durchgehende Einstellung:
ffmpeg -i clips/S09_t1.mp4 -vf "select='gt(scene,0.3)',showinfo" -an -f null - 2>&1 | grep pts_time
Einen Clip an seine Einstellung anpassen
Ein Schnitt benötigt einen Clip, der seine Einstellung exakt ausfüllt, bei der Bildrate des Schnitts:
| Problem | Lösung |
|---|---|
| Schlechter Start (eine Geste vor ihrer Zeile, eine doppelte Requisite in der ersten Sekunde) | Später starten: --off 1.5. |
| Nach dem Trimmen zu kurz | Verlangsamen mit --speed 1.22 (bis ~1.25 liest sich gut); das letzte Bild wird gehalten. |
| Schlechte Mitte | Die Einstellung teilen: die erste Zeile auf einem Ausschnitt spielen, der das Problem aus dem Bild hält, dann danach zurückschneiden. |
| Nicht behebbar (eine Tür erscheint, eine zweite Person kommt herein) | Neu generieren mit einem festgelegten Prompt, 2–3 Takes, und auf einem Blatt auswählen. |
lipsync_parts.py normalize clip.mp4 shot.mp4 --frames N --off S --speed K führt den Versatz, die Geschwindigkeit, die Bildrate, die Skalierung (Abdecken und mittig zuschneiden, niemals strecken) und das Halten des letzten Frames in einem Schritt durch und gibt immer genau N Frames zurück.
Dialog lippensynchronisieren
- Normalisieren des Clips auf die Einstellung:
python3 <this-skill-dir>/scripts/lipsync_parts.py normalize clip.mp4 shot.mp4 --frames 132 --off 1.5 --speed 1.22. - Aufteilen an Zeilengrenzen, sodass jeder Teil einen Sprecher hat.
shot.jsonschreiben: jede Zeile mit ihrem Start/Ende in der Einstellung (Sekunden) und ihrem Sprecher;framesentspricht dem--frames, auf das normalisiert wurde."skip": truehinzufügen für einen Sprecher, der von hinten zu sehen, außerhalb des Bildschirms ist oder für einen Massenruf:{"id": "S05", "video": "shot.mp4", "fps": 24, "frames": 132, "lines": [{"id": "05-1", "audio": "lines/05-1.mp3", "start": 0.72, "end": 2.31, "speaker": "maya"}, {"id": "05-2", "audio": "lines/05-2.mp3", "start": 2.43, "end": 4.10, "speaker": "ido"}]}lipsync_parts.py split shot.json parts/ausführen. Neben den Teilen schreibt esparts/S05_lipsync_jobs.json: ein Eintrag pro nicht-übersprungenem Teil, mit absoluten Pfaden,outbereits so benannt, wiejoines erwartet (synced/S05_<k>.mp4), undface: null.
- Auf den Sprecher zeigen in jedem Teil, und dies in die Job-Datei als
"face": "x,y@<mid>"schreiben (midsteht in jedem Eintrag).- Ein Pixel auf ihrem Gesicht im mittleren Frame des Teils auswählen.
- Bestätigen:
take_sheet.py point parts/S05_1.mp4 check.jpg --frame 30 --xy 410,560, dann das Bild lesen. - Gesichtserkenner sind bei gezeichneten Gesichtern unzuverlässig, daher per Augenmaß auswählen und bestätigen.
nullnur lassen, wenn ein Gesicht sichtbar ist.
- Sync:
lipsync_fal.py --jobs parts/S05_lipsync_jobs.json. Erwarte ~$0.13 pro Sekunde und ~70–150 s pro Teil; 6 laufen parallel. Vorhandene Ausgaben werden übersprungen. Um einen Teil nach Korrektur seines Gesichts zu wiederholen, diesen Teil allein ausführen:lipsync_fal.py parts/S05_1.mp4 parts/S05_1.wav synced/S05_1.mp4 --face 410,560@30 --force(--forcemit--jobswürde jeden Teil wiederholen). - Zusammenfügen:
lipsync_parts.py join parts/S05_plan.json synced/ final/S05.mp4. Jeder Teil wird auf seine genaue Frame-Anzahl gezwungen, übersprungene Teile behalten ihre ursprünglichen Frames, ein Teil ohne synchronisierte Datei wird gemeldet (--strictmacht das zu einem Fehler), und die gesamte Frame-Anzahl wird überprüft.
Details, Modellauswahl und Fehlerfälle: references/lipsync.md.
Kosten und Zeiten (gemessen 2026-10)
| Schritt | Anzahl | Kosten | Zeit |
|---|---|---|---|
| Omni-Clip | pro Sekunde Ausgabevideo (ein 6-s-Clip kostet ~0,60 $; plane ~1,5 Aufrufe pro Einstellung für Nachdrehs) | ~0,10 $ | ~30 s pro Aufruf, 4 parallel |
| Sync 3 Lip-Sync | pro Sekunde Ausgabe (ein Teil ist typischerweise 3–6 s) | ~0,13 $ | ~70–150 s pro Teil, 6 parallel |
Häufige Fehler
| Fehler | Ergebnis | Lösung |
|---|---|---|
| Charaktere im Video-Prompt benennen | die falsche Person bewegt sich | durch Kleidung beschreiben |
| Omnis Audio behalten | erfundene Stimmen und Musik unter dem Dialog | stumm schalten (-an) |
| Einen Take ohne Blatt verwenden | doppelte Requisiten und neue Türen landen im Video | jeden Take mit Blatt prüfen |
| Den gesamten Clip für eine schlechte erste Sekunde neu generieren | neue Probleme an anderen Stellen | Versatz plus Verlangsamung |
| Eine ganze Zwei-Sprecher-Einstellung lippensynchronisieren | ein Mund bewegt sich für beide Zeilen | an Zeilengrenzen aufteilen, ein Sprecher pro Teil |
| Dem Modell die Sprecherwahl überlassen | das falsche Gesicht spricht | --face X,Y@FRAME |
| Den fal-Schlüssel kürzen | 401 | den Schlüssel genau so übergeben, wie fal ihn anzeigt |
| Teile zusammenfügen, ohne Frame-Anzahlen zu korrigieren | die Einstellung driftet aus dem Sync mit dem Schnitt | lipsync_parts.py join |
Verwandte Fähigkeiten
- image-generation: die Standbilder (Charakterkarten, Panels).
- elevenlabs-tts: der Dialog, zu dem die Münder synchronisiert werden.
- viewing-videos: Frame-Extraktion zur Überprüfung von Clips.
- producing-animated-episodes: der gesamte Episoden-Workflow, der diese Clips verwendet.